Merge "Update New Project Template Dependencies" am: f908080572 am: f4369128a0 am: 0562b43a09 am: 22b7ea6e04 am: fbab2ed4ed
am: b3d01579a0

* commit 'b3d01579a09d632ecbd090c533687256e520d74f':
diff --git a/.idea/dictionaries/adt.xml b/.idea/dictionaries/adt.xml
index 6a5aa48..ecf4830 100644
--- a/.idea/dictionaries/adt.xml
+++ b/.idea/dictionaries/adt.xml
@@ -4,6 +4,7 @@
       <w>&amp;amp</w>
       <w>aapt</w>
       <w>aarrggbb</w>
+      <w>abis</w>
       <w>accessors</w>
       <w>actionbar</w>
       <w>adb's</w>
@@ -288,6 +289,7 @@
       <w>versioncode</w>
       <w>vert</w>
       <w>vmsig</w>
+      <w>waitable</w>
       <w>wakelock</w>
       <w>wakelocks</w>
       <w>wakeup</w>
@@ -314,4 +316,4 @@
       <w>zipalign</w>
     </words>
   </dictionary>
-</component>
\ No newline at end of file
+</component>
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 0cf047b..51d9659 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <component name="InspectionProjectProfileManager">
   <profile version="1.0" is_locked="false">
     <option name="myName" value="Project Default" />
@@ -106,6 +107,7 @@
     <inspection_tool class="DollarSignInName" enabled="true" level="WARNING" enabled_by_default="true" />
     <inspection_tool class="EqualsHashCodeCalledOnUrl" enabled="true" level="WARNING" enabled_by_default="true" />
     <inspection_tool class="FileEqualsUsage" enabled="false" level="WARNING" enabled_by_default="false" />
+    <inspection_tool class="GroovyAssignabilityCheck" enabled="false" level="WARNING" enabled_by_default="false" />
     <inspection_tool class="GtkPreferredJComboBoxRenderer" enabled="false" level="WARNING" enabled_by_default="false" />
     <inspection_tool class="HtmlExtraClosingTag" enabled="false" level="ERROR" enabled_by_default="false" />
     <inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
diff --git a/.idea/libraries/JUnit4.xml b/.idea/libraries/JUnit4.xml
index 4a45849..463c02b 100644
--- a/.idea/libraries/JUnit4.xml
+++ b/.idea/libraries/JUnit4.xml
@@ -2,10 +2,12 @@
   <library name="JUnit4">
     <CLASSES>
       <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
       <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12-sources.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar!/" />
     </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/.idea/libraries/builder_model.xml b/.idea/libraries/builder_model.xml
index 10e5fa0..24e07b5 100644
--- a/.idea/libraries/builder_model.xml
+++ b/.idea/libraries/builder_model.xml
@@ -1,11 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <component name="libraryTable">
   <library name="builder-model">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/builder-model/builder-model-1.1.0-rc1.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/offline-m2/com/android/tools/build/builder-model/1.3.0/builder-model-1.3.0.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/builder-model/builder-model-1.1.0-rc1-sources.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/offline-m2/com/android/tools/build/builder-model/1.3.0/builder-model-1.3.0-sources.jar!/" />
     </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/.idea/libraries/ecj.xml b/.idea/libraries/ecj.xml
index 2cb447d..d92fa51 100644
--- a/.idea/libraries/ecj.xml
+++ b/.idea/libraries/ecj.xml
@@ -1,11 +1,11 @@
 <component name="libraryTable">
   <library name="ecj">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4-sources.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2-sources.jar!/" />
     </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/.idea/libraries/jack_api_0_1.xml b/.idea/libraries/jack_api_0_1.xml
deleted file mode 100644
index c8406b4..0000000
--- a/.idea/libraries/jack_api_0_1.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<component name="libraryTable">
-  <library name="jack-api-0.1">
-    <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/android/tools/jack/jack-api/0.1/jack-api-0.1.jar!/" />
-    </CLASSES>
-    <JAVADOC />
-    <SOURCES />
-  </library>
-</component>
\ No newline at end of file
diff --git a/.idea/libraries/jsr_305.xml b/.idea/libraries/jsr_305.xml
new file mode 100644
index 0000000..788b3ab
--- /dev/null
+++ b/.idea/libraries/jsr_305.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+  <library name="jsr-305">
+    <CLASSES>
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES>
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/truth.xml b/.idea/libraries/truth.xml
index a97570c..97137f6 100644
--- a/.idea/libraries/truth.xml
+++ b/.idea/libraries/truth.xml
@@ -1,11 +1,11 @@
 <component name="libraryTable">
   <library name="truth">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.24/truth-0.24.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.26/truth-0.26.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
-      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.24/truth-0.24-sources.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.26/truth-0.26-sources.jar!/" />
     </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3f3d65f..8ec0917 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -6,6 +6,19 @@
   </component>
   <component name="EntryPointsManager">
     <entry_points version="2.0" />
+    <list size="11">
+      <item index="0" class="java.lang.String" itemvalue="org.gradle.api.tasks.TaskAction" />
+      <item index="1" class="java.lang.String" itemvalue="org.gradle.model.Defaults" />
+      <item index="2" class="java.lang.String" itemvalue="org.gradle.model.Finalize" />
+      <item index="3" class="java.lang.String" itemvalue="org.gradle.model.Model" />
+      <item index="4" class="java.lang.String" itemvalue="org.gradle.model.Mutate" />
+      <item index="5" class="java.lang.String" itemvalue="org.gradle.model.Validate" />
+      <item index="6" class="java.lang.String" itemvalue="org.gradle.platform.base.BinaryTasks" />
+      <item index="7" class="java.lang.String" itemvalue="org.gradle.platform.base.BinaryType" />
+      <item index="8" class="java.lang.String" itemvalue="org.gradle.platform.base.ComponentBinaries" />
+      <item index="9" class="java.lang.String" itemvalue="org.gradle.platform.base.ComponentType" />
+      <item index="10" class="java.lang.String" itemvalue="org.gradle.platform.base.LanguageType" />
+    </list>
   </component>
   <component name="FrameworkDetectionExcludesConfiguration">
     <type id="android" />
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 6ffad99..5d2d335 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -15,10 +15,13 @@
       <module fileurl="file://$PROJECT_DIR$/draw9patch/draw9patch.iml" filepath="$PROJECT_DIR$/draw9patch/draw9patch.iml" />
       <module fileurl="file://$PROJECT_DIR$/device_validator/dvlib/dvlib.iml" filepath="$PROJECT_DIR$/device_validator/dvlib/dvlib.iml" />
       <module fileurl="file://$PROJECT_DIR$/sdk-common/generate-locale-data/generate-locale-data.iml" filepath="$PROJECT_DIR$/sdk-common/generate-locale-data/generate-locale-data.iml" />
+      <module fileurl="file://$PROJECT_DIR$/build-system/google-services/google-services.iml" filepath="$PROJECT_DIR$/build-system/google-services/google-services.iml" />
       <module fileurl="file://$PROJECT_DIR$/build-system/gradle/gradle.iml" filepath="$PROJECT_DIR$/build-system/gradle/gradle.iml" />
       <module fileurl="file://$PROJECT_DIR$/build-system/gradle-core/gradle-core.iml" filepath="$PROJECT_DIR$/build-system/gradle-core/gradle-core.iml" />
       <module fileurl="file://$PROJECT_DIR$/build-system/gradle-experimental/gradle-experimental.iml" filepath="$PROJECT_DIR$/build-system/gradle-experimental/gradle-experimental.iml" />
       <module fileurl="file://$PROJECT_DIR$/build-system/integration-test/integration-test.iml" filepath="$PROJECT_DIR$/build-system/integration-test/integration-test.iml" />
+      <module fileurl="file://$PROJECT_DIR$/jack/jack-api/jack-api.iml" filepath="$PROJECT_DIR$/jack/jack-api/jack-api.iml" />
+      <module fileurl="file://$PROJECT_DIR$/jack/jill-api/jill-api.iml" filepath="$PROJECT_DIR$/jack/jill-api/jill-api.iml" />
       <module fileurl="file://$PROJECT_DIR$/layoutlib-api/layoutlib-api-base.iml" filepath="$PROJECT_DIR$/layoutlib-api/layoutlib-api-base.iml" />
       <module fileurl="file://$PROJECT_DIR$/lint/libs/lint-api/lint-api-base.iml" filepath="$PROJECT_DIR$/lint/libs/lint-api/lint-api-base.iml" />
       <module fileurl="file://$PROJECT_DIR$/lint/libs/lint-checks/lint-checks-base.iml" filepath="$PROJECT_DIR$/lint/libs/lint-checks/lint-checks-base.iml" />
diff --git a/.idea/scopes/custom_scope.xml b/.idea/scopes/custom_scope.xml
new file mode 100644
index 0000000..1acfd41
--- /dev/null
+++ b/.idea/scopes/custom_scope.xml
@@ -0,0 +1,3 @@
+<component name="DependencyValidationManager">
+  <scope name="custom_scope" pattern="(src:*..*||test:*..*)&amp;&amp;!test[manifest-merger-base]:com.android.manifmerger.data..*&amp;&amp;!test[manifest-merger-base]:com.android.manifmerger.data2..*&amp;&amp;!test[lint-cli]:com.android.tools.lint.checks.data..*&amp;&amp;!test[sdk-common-base]:testData..*&amp;&amp;!test[builder]:testData..*&amp;&amp;!test[assetstudio-base]:com.android.assetstudiolib.testdata..*&amp;&amp;!src[assetstudio-base]:images..*" />
+</component>
\ No newline at end of file
diff --git a/annotations/build.gradle b/annotations/build.gradle
index 506a91f..dbfcea6 100644
--- a/annotations/build.gradle
+++ b/annotations/build.gradle
@@ -1,12 +1,3 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
 apply plugin: 'sdk-java-lib'
 
diff --git a/annotations/src/main/java/com/android/annotations/NonNull.java b/annotations/src/main/java/com/android/annotations/NonNull.java
index 973ebb6..e306a31 100644
--- a/annotations/src/main/java/com/android/annotations/NonNull.java
+++ b/annotations/src/main/java/com/android/annotations/NonNull.java
@@ -32,7 +32,7 @@
  * This is a marker annotation and it has no specific attributes.
  */
 @Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
 @Target({METHOD,PARAMETER,LOCAL_VARIABLE,FIELD})
 public @interface NonNull {
 }
diff --git a/annotations/src/main/java/com/android/annotations/NonNullByDefault.java b/annotations/src/main/java/com/android/annotations/NonNullByDefault.java
index 3db891c..23903d1 100644
--- a/annotations/src/main/java/com/android/annotations/NonNullByDefault.java
+++ b/annotations/src/main/java/com/android/annotations/NonNullByDefault.java
@@ -41,7 +41,7 @@
  * This is a marker annotation and it has no specific attributes.
  */
 @Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
 @Target({PACKAGE, TYPE})
 public @interface NonNullByDefault {
 }
diff --git a/annotations/src/main/java/com/android/annotations/Nullable.java b/annotations/src/main/java/com/android/annotations/Nullable.java
index d9c3861..376c1f6 100755
--- a/annotations/src/main/java/com/android/annotations/Nullable.java
+++ b/annotations/src/main/java/com/android/annotations/Nullable.java
@@ -43,7 +43,7 @@
  * This is a marker annotation and it has no specific attributes.
  */
 @Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
 @Target({METHOD, PARAMETER, LOCAL_VARIABLE, FIELD})
 public @interface Nullable {
 }
diff --git a/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java b/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java
index 9489bb1..ec606b7 100644
--- a/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java
+++ b/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java
@@ -27,7 +27,7 @@
  * with the specified lock being held.
  */
 @Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
 @Target({ElementType.METHOD, ElementType.FIELD})
 public @interface GuardedBy {
     String value();
diff --git a/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java b/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java
index d6c9a4a..a794d68 100644
--- a/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java
+++ b/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java
@@ -27,7 +27,7 @@
  * is immutable.
  */
 @Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
 @Target(ElementType.TYPE)
 public @interface Immutable {
 }
diff --git a/asset-studio/assetstudio-base.iml b/asset-studio/assetstudio-base.iml
index f25586b..9835f9f 100644
--- a/asset-studio/assetstudio-base.iml
+++ b/asset-studio/assetstudio-base.iml
@@ -13,6 +13,6 @@
     <orderEntry type="module" module-name="common" exported="" />
     <orderEntry type="module" module-name="layoutlib-api-base" exported="" />
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="module" module-name="sdk-common-base" />
   </component>
-</module>
-
+</module>
\ No newline at end of file
diff --git a/asset-studio/assetstudio.iml b/asset-studio/assetstudio.iml
index 2076a01..c11975b 100644
--- a/asset-studio/assetstudio.iml
+++ b/asset-studio/assetstudio.iml
@@ -13,6 +13,6 @@
     <orderEntry type="module" module-name="common" exported="" />
     <orderEntry type="module" module-name="layoutlib-api" exported="" />
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="module" module-name="sdk-common" />
   </component>
-</module>
-
+</module>
\ No newline at end of file
diff --git a/asset-studio/build.gradle b/asset-studio/build.gradle
index bb335ce..1e186ad 100644
--- a/asset-studio/build.gradle
+++ b/asset-studio/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools'
@@ -7,6 +8,7 @@
 
 dependencies {
     compile project(':base:layoutlib-api')
+    compile project(':base:sdk-common')
 
     testCompile 'junit:junit:4.12'
 }
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java
index 55f3409..8fa0a48 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java
@@ -15,8 +15,9 @@
  */
 package com.android.assetstudiolib;
 
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
 
 import java.awt.Color;
 import java.awt.Graphics2D;
@@ -40,29 +41,25 @@
                 ? new Rectangle(0, 0, 32, 32)
                 : new Rectangle(4, 4, 24, 24);
         final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
-        Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor);
-        Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor);
-        BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+        Rectangle imageRect = AssetUtil.scaleRectangle(iconSizeMdpi, scaleFactor);
+        Rectangle targetRect = AssetUtil.scaleRectangle(targetRectMdpi, scaleFactor);
+        BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g = (Graphics2D) outImage.getGraphics();
 
-        BufferedImage tempImage = Util.newArgbBufferedImage(
-                imageRect.width, imageRect.height);
-        Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
-        Util.drawCenterInside(g2, options.sourceImage, targetRect);
+        BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
+        Graphics2D g2 = (Graphics2D)tempImage.getGraphics();
+        AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
 
         if (actionBarOptions.theme == Theme.CUSTOM) {
-          Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-            new FillEffect(new Color(actionBarOptions.customThemeColor), 0.8),
-          });
+            AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{
+              new FillEffect(new Color(actionBarOptions.customThemeColor), 0.8),});
         } else if (actionBarOptions.theme == Theme.HOLO_LIGHT) {
-            Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                    new FillEffect(new Color(0x333333), 0.6),
-            });
+            AssetUtil.drawEffects(g, tempImage, 0, 0,
+                                  new Effect[]{new FillEffect(new Color(0x333333), 0.6),});
         } else {
             assert actionBarOptions.theme == Theme.HOLO_DARK;
-            Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                    new FillEffect(new Color(0xFFFFFF), 0.8)
-            });
+            AssetUtil.drawEffects(g, tempImage, 0, 0,
+                                  new Effect[]{new FillEffect(new Color(0xFFFFFF), 0.8)});
         }
 
         g.dispose();
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java
index 360408b..693619a 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java
@@ -137,7 +137,10 @@
      * example in unselected tabs we change foo.png to foo-unselected.png
      */
     protected String getIconName(Options options, String name) {
-        return name + ".png"; //$NON-NLS-1$
+        if (options.density == Density.ANYDPI) {
+            return name + SdkConstants.DOT_XML;
+        }
+        return name + SdkConstants.DOT_PNG; //$NON-NLS-1$
     }
 
     /**
@@ -146,6 +149,10 @@
      * notification icons we add in -v9 or -v11.
      */
     protected String getIconFolder(Options options) {
+        if (options.density == Density.ANYDPI) {
+            return SdkConstants.FD_RES + '/' +
+                   ResourceFolderType.DRAWABLE.getName();
+        }
         StringBuilder sb = new StringBuilder(50);
         sb.append(SdkConstants.FD_RES);
         sb.append('/');
@@ -176,6 +183,12 @@
      */
     public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap,
             GraphicGeneratorContext context, Options options, String name) {
+        // Vector image only need to generate one preview image, so we by pass all the
+        // other image densities.
+        if (options.density == Density.ANYDPI) {
+            generateImageAndUpdateMap(category, categoryMap, context, options, name);
+            return;
+        }
         Density[] densityValues = Density.values();
         // Sort density values into ascending order
         Arrays.sort(densityValues, new Comparator<Density>() {
@@ -184,7 +197,6 @@
                 return d1.getDpiValue() - d2.getDpiValue();
             }
         });
-
         for (Density density : densityValues) {
             if (!density.isValidValueForDevice()) {
                 continue;
@@ -195,19 +207,27 @@
                 continue;
             }
             options.density = density;
-            BufferedImage image = generate(context, options);
-            if (image != null) {
-                String mapCategory = category;
-                if (mapCategory == null) {
-                    mapCategory = options.density.getResourceValue();
-                }
-                Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
-                if (imageMap == null) {
-                    imageMap = new LinkedHashMap<String, BufferedImage>();
-                    categoryMap.put(mapCategory, imageMap);
-                }
-                imageMap.put(getIconPath(options, name), image);
+            generateImageAndUpdateMap(category, categoryMap, context, options, name);
+        }
+    }
+
+    private void generateImageAndUpdateMap(String category,
+                                           Map<String, Map<String, BufferedImage>> categoryMap,
+                                           GraphicGeneratorContext context,
+                                           Options options,
+                                           String name) {
+        BufferedImage image = generate(context, options);
+        if (image != null) {
+            String mapCategory = category;
+            if (mapCategory == null) {
+                mapCategory = options.density.getResourceValue();
             }
+            Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
+            if (imageMap == null) {
+                imageMap = new LinkedHashMap<String, BufferedImage>();
+                categoryMap.put(mapCategory, imageMap);
+            }
+            imageMap.put(getIconPath(options, name), image);
         }
     }
 
@@ -223,6 +243,9 @@
      * @return a factor to multiple mdpi distances with to compute the target density
      */
     public static float getMdpiScaleFactor(Density density) {
+        if (density == Density.ANYDPI) {
+            density = Density.XXXHIGH;
+        }
         return density.getDpiValue() / (float) Density.MEDIUM.getDpiValue();
     }
 
@@ -288,10 +311,9 @@
      *
      * @return an iterator for the available image names
      */
-    public static Iterator<String> getClipartNames() {
+    public static Iterator<String> getResourcesNames(String pathPrefix, String filenameExtension) {
         List<String> names = new ArrayList<String>(80);
         try {
-            String pathPrefix = "images/clipart/big/"; //$NON-NLS-1$
             ZipFile zipFile = null;
             ProtectionDomain protectionDomain = GraphicGenerator.class.getProtectionDomain();
             URL url = protectionDomain.getCodeSource().getLocation();
@@ -317,7 +339,7 @@
             while (enumeration.hasMoreElements()) {
                 ZipEntry zipEntry = enumeration.nextElement();
                 String name = zipEntry.getName();
-                if (!name.startsWith(pathPrefix) || !name.endsWith(".png")) { //$NON-NLS-1$
+                if (!name.startsWith(pathPrefix) || !name.endsWith(filenameExtension)) { //$NON-NLS-1$
                     continue;
                 }
 
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java
index bf69c85..040be4b 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java
@@ -17,6 +17,7 @@
 package com.android.assetstudiolib;
 
 import com.android.annotations.NonNull;
+import com.android.ide.common.util.AssetUtil;
 import com.android.resources.Density;
 import com.android.utils.Pair;
 
@@ -144,26 +145,26 @@
 
         Rectangle imageRect = IMAGE_SIZE_WEB;
         if (!launcherOptions.isWebGraphic) {
-            imageRect = Util.scaleRectangle(IMAGE_SIZE_MDPI,
-                    GraphicGenerator.getMdpiScaleFactor(launcherOptions.density));
+            imageRect = AssetUtil.scaleRectangle(IMAGE_SIZE_MDPI, GraphicGenerator
+              .getMdpiScaleFactor(launcherOptions.density));
         }
 
         Rectangle targetRect = TARGET_RECTS.get(
                 Pair.of(launcherOptions.shape, launcherOptions.density));
         if (targetRect == null) {
             // Scale up from MDPI if no density-specific target rectangle is defined.
-            targetRect = Util.scaleRectangle(
-                    TARGET_RECTS.get(Pair.of(launcherOptions.shape, Density.MEDIUM)),
-                    GraphicGenerator.getMdpiScaleFactor(launcherOptions.density));
+            targetRect = AssetUtil
+              .scaleRectangle(TARGET_RECTS.get(Pair.of(launcherOptions.shape, Density.MEDIUM)),
+                              GraphicGenerator.getMdpiScaleFactor(launcherOptions.density));
         }
 
-        BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+        BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g = (Graphics2D) outImage.getGraphics();
         if (backImage != null) {
             g.drawImage(backImage, 0, 0, null);
         }
 
-        BufferedImage tempImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+        BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
         if (maskImage != null) {
             g2.drawImage(maskImage, 0, 0, null);
@@ -173,9 +174,9 @@
         }
 
         if (launcherOptions.crop) {
-            Util.drawCenterCrop(g2, launcherOptions.sourceImage, targetRect);
+            AssetUtil.drawCenterCrop(g2, launcherOptions.sourceImage, targetRect);
         } else {
-            Util.drawCenterInside(g2, launcherOptions.sourceImage, targetRect);
+            AssetUtil.drawCenterInside(g2, launcherOptions.sourceImage, targetRect);
         }
 
         g.drawImage(tempImage, 0, 0, null);
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java
index 33b9c34..8b8eb7e 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java
@@ -16,9 +16,10 @@
 
 package com.android.assetstudiolib;
 
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
-import com.android.assetstudiolib.Util.ShadowEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
+import com.android.ide.common.util.AssetUtil.ShadowEffect;
 
 import java.awt.Color;
 import java.awt.GradientPaint;
@@ -39,46 +40,21 @@
         Rectangle imageSizeHdpi = new Rectangle(0, 0, 48, 48);
         Rectangle targetRectHdpi = new Rectangle(8, 8, 32, 32);
         float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
-        Rectangle imageRect = Util.scaleRectangle(imageSizeHdpi, scaleFactor);
-        Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor);
+        Rectangle imageRect = AssetUtil.scaleRectangle(imageSizeHdpi, scaleFactor);
+        Rectangle targetRect = AssetUtil.scaleRectangle(targetRectHdpi, scaleFactor);
 
-        BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+        BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g = (Graphics2D) outImage.getGraphics();
 
-        BufferedImage tempImage = Util.newArgbBufferedImage(
-                imageRect.width, imageRect.height);
+        BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
-        Util.drawCenterInside(g2, options.sourceImage, targetRect);
+        AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
 
-        Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                new FillEffect(
-                        new GradientPaint(
-                                0, 0,
-                                new Color(0xa3a3a3),
-                                0, imageRect.height,
-                                new Color(0x787878))),
-                new ShadowEffect(
-                        0,
-                        2 * scaleFactor,
-                        2 * scaleFactor,
-                        Color.BLACK,
-                        0.2,
-                        true),
-                new ShadowEffect(
-                        0,
-                        1,
-                        0,
-                        Color.BLACK,
-                        0.35,
-                        true),
-                new ShadowEffect(
-                        0,
-                        -1,
-                        0,
-                        Color.WHITE,
-                        0.35,
-                        true),
-        });
+        AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+          new GradientPaint(0, 0, new Color(0xa3a3a3), 0, imageRect.height, new Color(0x787878))),
+          new ShadowEffect(0, 2 * scaleFactor, 2 * scaleFactor, Color.BLACK, 0.2, true),
+          new ShadowEffect(0, 1, 0, Color.BLACK, 0.35, true),
+          new ShadowEffect(0, -1, 0, Color.WHITE, 0.35, true),});
 
         g.dispose();
         g2.dispose();
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java
index b84af1b..c145a39 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java
@@ -15,9 +15,10 @@
  */
 package com.android.assetstudiolib;
 
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
-import com.android.assetstudiolib.Util.ShadowEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
+import com.android.ide.common.util.AssetUtil.ShadowEffect;
 
 import java.awt.Color;
 import java.awt.GradientPaint;
@@ -52,14 +53,13 @@
         }
 
         final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
-        Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor);
-        Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor);
+        Rectangle imageRect = AssetUtil.scaleRectangle(iconSizeMdpi, scaleFactor);
+        Rectangle targetRect = AssetUtil.scaleRectangle(targetRectMdpi, scaleFactor);
 
-        BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+        BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g = (Graphics2D) outImage.getGraphics();
 
-        BufferedImage tempImage = Util.newArgbBufferedImage(
-                imageRect.width, imageRect.height);
+        BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
 
         if (notificationOptions.version == Version.OLDER) {
@@ -69,31 +69,18 @@
                             + ".png");
             g.drawImage(backImage, 0, 0, null);
             BufferedImage top = options.sourceImage;
-            BufferedImage filled = Util.filledImage(top, Color.WHITE);
-            Util.drawCenterInside(g, filled, targetRect);
+            BufferedImage filled = AssetUtil.filledImage(top, Color.WHITE);
+            AssetUtil.drawCenterInside(g, filled, targetRect);
         } else if (notificationOptions.version == Version.V11) {
-            Util.drawCenterInside(g2, options.sourceImage, targetRect);
-            Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                    new FillEffect(Color.WHITE),
-            });
+            AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
+            AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(Color.WHITE),});
         } else {
             assert notificationOptions.version == Version.V9;
-            Util.drawCenterInside(g2, options.sourceImage, targetRect);
-            Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                    new FillEffect(
-                            new GradientPaint(
-                                    0, 0,
-                                    new Color(0x919191),
-                                    0, imageRect.height,
-                                    new Color(0x828282))),
-                    new ShadowEffect(
-                            0,
-                            1,
-                            0,
-                            Color.WHITE,
-                            0.10,
-                            true),
-            });
+            AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
+            AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+              new GradientPaint(0, 0, new Color(0x919191), 0, imageRect.height,
+                                new Color(0x828282))),
+              new ShadowEffect(0, 1, 0, Color.WHITE, 0.10, true),});
         }
 
         g.dispose();
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java
index 3d2ac30..a540f5d 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java
@@ -15,9 +15,10 @@
  */
 package com.android.assetstudiolib;
 
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
-import com.android.assetstudiolib.Util.ShadowEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
+import com.android.ide.common.util.AssetUtil.ShadowEffect;
 
 import java.awt.Color;
 import java.awt.GradientPaint;
@@ -40,96 +41,40 @@
         Rectangle iconSizeMdpi = new Rectangle(0, 0, 32, 32);
         Rectangle targetRectMdpi = new Rectangle(2, 2, 28, 28);
         final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
-        Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor);
-        Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor);
-        BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+        Rectangle imageRect = AssetUtil.scaleRectangle(iconSizeMdpi, scaleFactor);
+        Rectangle targetRect = AssetUtil.scaleRectangle(targetRectMdpi, scaleFactor);
+        BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g = (Graphics2D) outImage.getGraphics();
 
-        BufferedImage tempImage = Util.newArgbBufferedImage(
-                imageRect.width, imageRect.height);
+        BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
         Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
-        Util.drawCenterInside(g2, options.sourceImage, targetRect);
+        AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
 
         TabOptions tabOptions = (TabOptions) options;
         if (tabOptions.selected) {
             if (tabOptions.oldStyle) {
-                Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                        new FillEffect(
-                                new GradientPaint(
-                                        0, 0,
-                                        new Color(0xa3a3a3),
-                                        0, imageRect.height,
-                                        new Color(0x787878))),
-                        new ShadowEffect(
-                                0,
-                                2 * scaleFactor,
-                                2 * scaleFactor,
-                                Color.BLACK,
-                                0.2,
-                                true),
-                        new ShadowEffect(
-                                0,
-                                1,
-                                0,
-                                Color.BLACK,
-                                0.35,
-                                true),
-                        new ShadowEffect(
-                                0,
-                                -1,
-                                0,
-                                Color.WHITE,
-                                0.35,
-                                true),
-                });
+                AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+                  new GradientPaint(0, 0, new Color(0xa3a3a3), 0, imageRect.height,
+                                    new Color(0x787878))),
+                  new ShadowEffect(0, 2 * scaleFactor, 2 * scaleFactor, Color.BLACK, 0.2, true),
+                  new ShadowEffect(0, 1, 0, Color.BLACK, 0.35, true),
+                  new ShadowEffect(0, -1, 0, Color.WHITE, 0.35, true),});
             } else {
-                Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                        new FillEffect(Color.WHITE),
-                        new ShadowEffect(
-                                0,
-                                0,
-                                3 * scaleFactor,
-                                Color.BLACK,
-                                0.25,
-                                false),
-                });
+                AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(Color.WHITE),
+                  new ShadowEffect(0, 0, 3 * scaleFactor, Color.BLACK, 0.25, false),});
             }
         } else {
             // Unselected
             if (tabOptions.oldStyle) {
-                Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                        new FillEffect(
-                                new GradientPaint(
-                                        0, 0.25f * imageRect.height,
-                                        new Color(0xf9f9f9),
-                                        0, imageRect.height,
-                                        new Color(0xdfdfdf))),
-                        new ShadowEffect(
-                                0,
-                                2 * scaleFactor,
-                                2 * scaleFactor,
-                                Color.BLACK,
-                                0.1,
-                                true),
-                        new ShadowEffect(
-                                0,
-                                1,
-                                0,
-                                Color.BLACK,
-                                0.35,
-                                true),
-                        new ShadowEffect(
-                                0,
-                                -1,
-                                0,
-                                Color.WHITE,
-                                0.35,
-                                true),
-                });
+                AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+                  new GradientPaint(0, 0.25f * imageRect.height, new Color(0xf9f9f9), 0,
+                                    imageRect.height, new Color(0xdfdfdf))),
+                  new ShadowEffect(0, 2 * scaleFactor, 2 * scaleFactor, Color.BLACK, 0.1, true),
+                  new ShadowEffect(0, 1, 0, Color.BLACK, 0.35, true),
+                  new ShadowEffect(0, -1, 0, Color.WHITE, 0.35, true),});
             } else {
-                Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
-                        new FillEffect(new Color(0x808080)),
-                });
+                AssetUtil.drawEffects(g, tempImage, 0, 0,
+                                      new Effect[]{new FillEffect(new Color(0x808080)),});
             }
         }
 
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java b/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java
index 3ec6a34..5bc1afc 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java
@@ -16,6 +16,8 @@
 
 package com.android.assetstudiolib;
 
+import com.android.ide.common.util.AssetUtil;
+
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Graphics2D;
@@ -47,7 +49,7 @@
             options = new Options();
         }
 
-        BufferedImage tempImage = Util.newArgbBufferedImage(1, 1);
+        BufferedImage tempImage = AssetUtil.newArgbBufferedImage(1, 1);
         if (text == null || text.isEmpty()) {
             return tempImage;
         }
@@ -75,8 +77,8 @@
                     bounds.getWidth() + 2 * delta, bounds.getHeight() + 2 * delta);
         }
 
-        BufferedImage image = Util.newArgbBufferedImage(
-                Math.max(1, (int) bounds.getWidth()), Math.max(1, (int) bounds.getHeight()));
+        BufferedImage image = AssetUtil.newArgbBufferedImage(Math.max(1, (int)bounds.getWidth()),
+                                                             Math.max(1, (int)bounds.getHeight()));
         Graphics2D g = (Graphics2D) image.getGraphics();
         g.setColor(new Color(options.foregroundColor, true));
         g.setFont(font);
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/Util.java b/asset-studio/src/main/java/com/android/assetstudiolib/Util.java
deleted file mode 100644
index da718b8..0000000
--- a/asset-studio/src/main/java/com/android/assetstudiolib/Util.java
+++ /dev/null
@@ -1,451 +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.assetstudiolib;
-
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.Paint;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ConvolveOp;
-import java.awt.image.Kernel;
-import java.awt.image.Raster;
-import java.awt.image.RescaleOp;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A set of utility classes for manipulating {@link BufferedImage} objects and drawing them to
- * {@link Graphics2D} canvases.
- */
-public class Util {
-    /**
-     * Scales the given rectangle by the given scale factor.
-     *
-     * @param rect        The rectangle to scale.
-     * @param scaleFactor The factor to scale by.
-     * @return The scaled rectangle.
-     */
-    public static Rectangle scaleRectangle(Rectangle rect, float scaleFactor) {
-        return new Rectangle(
-                Math.round(rect.x * scaleFactor),
-                Math.round(rect.y * scaleFactor),
-                Math.round(rect.width * scaleFactor),
-                Math.round(rect.height * scaleFactor));
-    }
-
-    /**
-     * Creates a new ARGB {@link BufferedImage} of the given width and height.
-     *
-     * @param width  The width of the new image.
-     * @param height The height of the new image.
-     * @return The newly created image.
-     */
-    public static BufferedImage newArgbBufferedImage(int width, int height) {
-        return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-    }
-
-    /**
-     * Smoothly scales the given {@link BufferedImage} to the given width and height using the
-     * {@link Image#SCALE_SMOOTH} algorithm (generally bicubic resampling or bilinear filtering).
-     *
-     * @param source The source image.
-     * @param width  The destination width to scale to.
-     * @param height The destination height to scale to.
-     * @return A new, scaled image.
-     */
-    public static BufferedImage scaledImage(BufferedImage source, int width, int height) {
-        Image scaledImage = source.getScaledInstance(width, height, Image.SCALE_SMOOTH);
-        BufferedImage scaledBufImage = new BufferedImage(width, height,
-                BufferedImage.TYPE_INT_ARGB);
-        Graphics g = scaledBufImage.createGraphics();
-        g.drawImage(scaledImage, 0, 0, null);
-        g.dispose();
-        return scaledBufImage;
-    }
-
-    /**
-     * Applies a gaussian blur of the given radius to the given {@link BufferedImage} using a kernel
-     * convolution.
-     *
-     * @param source The source image.
-     * @param radius The blur radius, in pixels.
-     * @return A new, blurred image, or the source image if no blur is performed.
-     */
-    public static BufferedImage blurredImage(BufferedImage source, double radius) {
-        if (radius == 0) {
-            return source;
-        }
-
-        final int r = (int) Math.ceil(radius);
-        final int rows = r * 2 + 1;
-        final float[] kernelData = new float[rows * rows];
-
-        final double sigma = radius / 3;
-        final double sigma22 = 2 * sigma * sigma;
-        final double sqrtPiSigma22 = Math.sqrt(Math.PI * sigma22);
-        final double radius2 = radius * radius;
-
-        double total = 0;
-        int index = 0;
-        double distance2;
-
-        int x, y;
-        for (y = -r; y <= r; y++) {
-            for (x = -r; x <= r; x++) {
-                distance2 = 1.0 * x * x + 1.0 * y * y;
-                if (distance2 > radius2) {
-                    kernelData[index] = 0;
-                } else {
-                    kernelData[index] = (float) (Math.exp(-distance2 / sigma22) / sqrtPiSigma22);
-                }
-                total += kernelData[index];
-                ++index;
-            }
-        }
-
-        for (index = 0; index < kernelData.length; index++) {
-            kernelData[index] /= total;
-        }
-
-        // We first pad the image so the kernel can operate at the edges.
-        BufferedImage paddedSource = paddedImage(source, r);
-        BufferedImage blurredPaddedImage = operatedImage(paddedSource, new ConvolveOp(
-                new Kernel(rows, rows, kernelData), ConvolveOp.EDGE_ZERO_FILL, null));
-        return blurredPaddedImage.getSubimage(r, r, source.getWidth(), source.getHeight());
-    }
-
-    /**
-     * Inverts the alpha channel of the given {@link BufferedImage}. RGB data for the inverted area
-     * are undefined, so it's generally best to fill the resulting image with a color.
-     *
-     * @param source The source image.
-     * @return A new image with an alpha channel inverted from the original.
-     */
-    public static BufferedImage invertedAlphaImage(BufferedImage source) {
-        final float[] scaleFactors = new float[]{1, 1, 1, -1};
-        final float[] offsets = new float[]{0, 0, 0, 255};
-
-        return operatedImage(source, new RescaleOp(scaleFactors, offsets, null));
-    }
-
-    /**
-     * Applies a {@link BufferedImageOp} on the given {@link BufferedImage}.
-     *
-     * @param source The source image.
-     * @param op     The operation to perform.
-     * @return A new image with the operation performed.
-     */
-    public static BufferedImage operatedImage(BufferedImage source, BufferedImageOp op) {
-        BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
-        Graphics2D g = (Graphics2D) newImage.getGraphics();
-        g.drawImage(source, op, 0, 0);
-        return newImage;
-    }
-
-    /**
-     * Fills the given {@link BufferedImage} with a {@link Paint}, preserving its alpha channel.
-     *
-     * @param source The source image.
-     * @param paint  The paint to fill with.
-     * @return A new, painted/filled image.
-     */
-    public static BufferedImage filledImage(BufferedImage source, Paint paint) {
-        BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
-        Graphics2D g = (Graphics2D) newImage.getGraphics();
-        g.drawImage(source, 0, 0, null);
-        g.setComposite(AlphaComposite.SrcAtop);
-        g.setPaint(paint);
-        g.fillRect(0, 0, source.getWidth(), source.getHeight());
-        return newImage;
-    }
-
-    /**
-     * Pads the given {@link BufferedImage} on all sides by the given padding amount.
-     *
-     * @param source  The source image.
-     * @param padding The amount to pad on all sides, in pixels.
-     * @return A new, padded image, or the source image if no padding is performed.
-     */
-    public static BufferedImage paddedImage(BufferedImage source, int padding) {
-        if (padding == 0) {
-            return source;
-        }
-
-        BufferedImage newImage = newArgbBufferedImage(
-                source.getWidth() + padding * 2, source.getHeight() + padding * 2);
-        Graphics2D g = (Graphics2D) newImage.getGraphics();
-        g.drawImage(source, padding, padding, null);
-        return newImage;
-    }
-
-    /**
-     * Trims the transparent pixels from the given {@link BufferedImage} (returns a sub-image).
-     *
-     * @param source The source image.
-     * @return A new, trimmed image, or the source image if no trim is performed.
-     */
-    public static BufferedImage trimmedImage(BufferedImage source) {
-        final int minAlpha = 1;
-        final int srcWidth = source.getWidth();
-        final int srcHeight = source.getHeight();
-        Raster raster = source.getRaster();
-        int l = srcWidth, t = srcHeight, r = 0, b = 0;
-
-        int alpha, x, y;
-        int[] pixel = new int[4];
-        for (y = 0; y < srcHeight; y++) {
-            for (x = 0; x < srcWidth; x++) {
-                raster.getPixel(x, y, pixel);
-                alpha = pixel[3];
-                if (alpha >= minAlpha) {
-                    l = Math.min(x, l);
-                    t = Math.min(y, t);
-                    r = Math.max(x, r);
-                    b = Math.max(y, b);
-                }
-            }
-        }
-
-        if (l > r || t > b) {
-            // No pixels, couldn't trim
-            return source;
-        }
-
-        return source.getSubimage(l, t, r - l + 1, b - t + 1);
-    }
-
-    /**
-     * Draws the given {@link BufferedImage} to the canvas, at the given coordinates, with the given
-     * {@link Effect}s applied. Note that drawn effects may be outside the bounds of the source
-     * image.
-     *
-     * @param g       The destination canvas.
-     * @param source  The source image.
-     * @param x       The x offset at which to draw the image.
-     * @param y       The y offset at which to draw the image.
-     * @param effects The list of effects to apply.
-     */
-    public static void drawEffects(Graphics2D g, BufferedImage source, int x, int y,
-            Effect[] effects) {
-        List<ShadowEffect> shadowEffects = new ArrayList<ShadowEffect>();
-        List<FillEffect> fillEffects = new ArrayList<FillEffect>();
-
-        for (Effect effect : effects) {
-            if (effect instanceof ShadowEffect) {
-                shadowEffects.add((ShadowEffect) effect);
-            } else if (effect instanceof FillEffect) {
-                fillEffects.add((FillEffect) effect);
-            }
-        }
-
-        Composite oldComposite = g.getComposite();
-        for (ShadowEffect effect : shadowEffects) {
-            if (effect.inner) {
-                continue;
-            }
-
-            // Outer shadow
-            g.setComposite(AlphaComposite.getInstance(
-                    AlphaComposite.SRC_OVER, (float) effect.opacity));
-            g.drawImage(
-                    filledImage(
-                            blurredImage(source, effect.radius),
-                            effect.color),
-                    (int) effect.xOffset, (int) effect.yOffset, null);
-        }
-        g.setComposite(oldComposite);
-
-        // Inner shadow & fill effects.
-        final Rectangle imageRect = new Rectangle(0, 0, source.getWidth(), source.getHeight());
-        BufferedImage out = newArgbBufferedImage(imageRect.width, imageRect.height);
-        Graphics2D g2 = (Graphics2D) out.getGraphics();
-        double fillOpacity = 1.0;
-
-        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
-        g2.drawImage(source, 0, 0, null);
-        g2.setComposite(AlphaComposite.SrcAtop);
-
-        // Gradient fill
-        for (FillEffect effect : fillEffects) {
-            g2.setPaint(effect.paint);
-            g2.fillRect(0, 0, imageRect.width, imageRect.height);
-            fillOpacity = Math.max(0, Math.min(1, effect.opacity));
-        }
-
-        // Inner shadows
-        for (ShadowEffect effect : shadowEffects) {
-            if (!effect.inner) {
-                continue;
-            }
-
-            BufferedImage innerShadowImage = newArgbBufferedImage(
-                    imageRect.width, imageRect.height);
-            Graphics2D g3 = (Graphics2D) innerShadowImage.getGraphics();
-            g3.drawImage(source, (int) effect.xOffset, (int) effect.yOffset, null);
-            g2.setComposite(AlphaComposite.getInstance(
-                    AlphaComposite.SRC_ATOP, (float) effect.opacity));
-            g2.drawImage(
-                    filledImage(
-                            blurredImage(invertedAlphaImage(innerShadowImage), effect.radius),
-                            effect.color),
-                    0, 0, null);
-        }
-
-        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) fillOpacity));
-        g.drawImage(out, x, y, null);
-        g.setComposite(oldComposite);
-    }
-
-    /**
-     * Draws the given {@link BufferedImage} to the canvas, centered, wholly contained within the
-     * bounds defined by the destination rectangle, and with preserved aspect ratio.
-     *
-     * @param g       The destination canvas.
-     * @param source  The source image.
-     * @param dstRect The destination rectangle in the destination canvas into which to draw the
-     *                image.
-     */
-    public static void drawCenterInside(Graphics2D g, BufferedImage source, Rectangle dstRect) {
-        final int srcWidth = source.getWidth();
-        final int srcHeight = source.getHeight();
-        if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
-            final int scaledWidth = Math.max(1, dstRect.width);
-            final int scaledHeight = Math.max(1, dstRect.width * srcHeight / srcWidth);
-            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
-            g.drawImage(scaledImage,
-                    dstRect.x,
-                    dstRect.y + (dstRect.height - scaledHeight) / 2,
-                    dstRect.x + dstRect.width,
-                    dstRect.y + (dstRect.height - scaledHeight) / 2 + scaledHeight,
-                    0,
-                    0,
-                    0 + scaledWidth,
-                    0 + scaledHeight,
-                    null);
-        } else {
-            final int scaledWidth = Math.max(1, dstRect.height * srcWidth / srcHeight);
-            final int scaledHeight = Math.max(1, dstRect.height);
-            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
-            g.drawImage(scaledImage,
-                    dstRect.x + (dstRect.width - scaledWidth) / 2,
-                    dstRect.y,
-                    dstRect.x + (dstRect.width - scaledWidth) / 2 + scaledWidth,
-                    dstRect.y + dstRect.height,
-                    0,
-                    0,
-                    0 + scaledWidth,
-                    0 + scaledHeight,
-                    null);
-        }
-    }
-
-    /**
-     * Draws the given {@link BufferedImage} to the canvas, centered and cropped to fill the
-     * bounds defined by the destination rectangle, and with preserved aspect ratio.
-     *
-     * @param g       The destination canvas.
-     * @param source  The source image.
-     * @param dstRect The destination rectangle in the destination canvas into which to draw the
-     *                image.
-     */
-    public static void drawCenterCrop(Graphics2D g, BufferedImage source, Rectangle dstRect) {
-        final int srcWidth = source.getWidth();
-        final int srcHeight = source.getHeight();
-        if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
-            final int scaledWidth = dstRect.height * srcWidth / srcHeight;
-            final int scaledHeight = dstRect.height;
-            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
-            g.drawImage(scaledImage,
-                    dstRect.x,
-                    dstRect.y,
-                    dstRect.x + dstRect.width,
-                    dstRect.y + dstRect.height,
-                    0 + (scaledWidth - dstRect.width) / 2,
-                    0,
-                    0 + (scaledWidth - dstRect.width) / 2 + dstRect.width,
-                    0 + dstRect.height,
-                    null);
-        } else {
-            final int scaledWidth = dstRect.width;
-            final int scaledHeight = dstRect.width * srcHeight / srcWidth;
-            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
-            g.drawImage(scaledImage,
-                    dstRect.x,
-                    dstRect.y,
-                    dstRect.x + dstRect.width,
-                    dstRect.y + dstRect.height,
-                    0,
-                    0 + (scaledHeight - dstRect.height) / 2,
-                    0 + dstRect.width,
-                    0 + (scaledHeight - dstRect.height) / 2 + dstRect.height,
-                    null);
-        }
-    }
-
-    /**
-     * An effect to apply in
-     * {@link Util#drawEffects(java.awt.Graphics2D, java.awt.image.BufferedImage, int, int, Util.Effect[])}
-     */
-    public abstract static class Effect {
-    }
-
-    /**
-     * An inner or outer shadow.
-     */
-    public static class ShadowEffect extends Effect {
-        public double xOffset;
-        public double yOffset;
-        public double radius;
-        public Color color;
-        public double opacity;
-        public boolean inner;
-
-        public ShadowEffect(double xOffset, double yOffset, double radius, Color color,
-                double opacity, boolean inner) {
-            this.xOffset = xOffset;
-            this.yOffset = yOffset;
-            this.radius = radius;
-            this.color = color;
-            this.opacity = opacity;
-            this.inner = inner;
-        }
-    }
-
-    /**
-     * A fill, defined by a paint.
-     */
-    public static class FillEffect extends Effect {
-        public Paint paint;
-        public double opacity;
-
-        public FillEffect(Paint paint, double opacity) {
-            this.paint = paint;
-            this.opacity = opacity;
-        }
-
-        public FillEffect(Paint paint) {
-            this.paint = paint;
-            this.opacity = 1.0;
-        }
-    }
-}
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/VectorIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/VectorIconGenerator.java
new file mode 100644
index 0000000..ecfd24c
--- /dev/null
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/VectorIconGenerator.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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.assetstudiolib;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Generate icons for the vector drawable.
+ * We need the image in original format and size, therefore, there is no extra
+ * operation on the sourceImage.
+ */
+public class VectorIconGenerator extends GraphicGenerator {
+    @Override
+    public BufferedImage generate(GraphicGeneratorContext context, Options options) {
+        return options.sourceImage;
+    }
+
+    public static class VectorIconOptions extends GraphicGenerator.Options {
+    }
+}
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_3d_rotation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_3d_rotation_24dp.xml
new file mode 100644
index 0000000..8a08cf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_3d_rotation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.52,21.48C4.25,19.94 1.91,16.76 1.55,13H0.05C0.56,19.16 5.71,24 12,24l0.66,-0.03 -3.81,-3.81 -1.33,1.32zm0.89,-6.52c-0.19,0 -0.37,-0.03 -0.52,-0.08 -0.16,-0.06 -0.29,-0.13 -0.4,-0.24 -0.11,-0.1 -0.2,-0.22 -0.26,-0.37 -0.06,-0.14 -0.09,-0.3 -0.09,-0.47h-1.3c0,0.36 0.07,0.68 0.21,0.95 0.14,0.27 0.33,0.5 0.56,0.69 0.24,0.18 0.51,0.32 0.82,0.41 0.3,0.1 0.62,0.15 0.96,0.15 0.37,0 0.72,-0.05 1.03,-0.15 0.32,-0.1 0.6,-0.25 0.83,-0.44s0.42,-0.43 0.55,-0.72c0.13,-0.29 0.2,-0.61 0.2,-0.97 0,-0.19 -0.02,-0.38 -0.07,-0.56 -0.05,-0.18 -0.12,-0.35 -0.23,-0.51 -0.1,-0.16 -0.24,-0.3 -0.4,-0.43 -0.17,-0.13 -0.37,-0.23 -0.61,-0.31 0.2,-0.09 0.37,-0.2 0.52,-0.33 0.15,-0.13 0.27,-0.27 0.37,-0.42 0.1,-0.15 0.17,-0.3 0.22,-0.46 0.05,-0.16 0.07,-0.32 0.07,-0.48 0,-0.36 -0.06,-0.68 -0.18,-0.96 -0.12,-0.28 -0.29,-0.51 -0.51,-0.69 -0.2,-0.19 -0.47,-0.33 -0.77,-0.43C9.1,8.05 8.76,8 8.39,8c-0.36,0 -0.69,0.05 -1,0.16 -0.3,0.11 -0.57,0.26 -0.79,0.45 -0.21,0.19 -0.38,0.41 -0.51,0.67 -0.12,0.26 -0.18,0.54 -0.18,0.85h1.3c0,-0.17 0.03,-0.32 0.09,-0.45s0.14,-0.25 0.25,-0.34c0.11,-0.09 0.23,-0.17 0.38,-0.22 0.15,-0.05 0.3,-0.08 0.48,-0.08 0.4,0 0.7,0.1 0.89,0.31 0.19,0.2 0.29,0.49 0.29,0.86 0,0.18 -0.03,0.34 -0.08,0.49 -0.05,0.15 -0.14,0.27 -0.25,0.37 -0.11,0.1 -0.25,0.18 -0.41,0.24 -0.16,0.06 -0.36,0.09 -0.58,0.09H7.5v1.03h0.77c0.22,0 0.42,0.02 0.6,0.07s0.33,0.13 0.45,0.23c0.12,0.11 0.22,0.24 0.29,0.4 0.07,0.16 0.1,0.35 0.1,0.57 0,0.41 -0.12,0.72 -0.35,0.93 -0.23,0.23 -0.55,0.33 -0.95,0.33zm8.55,-5.92c-0.32,-0.33 -0.7,-0.59 -1.14,-0.77 -0.43,-0.18 -0.92,-0.27 -1.46,-0.27H12v8h2.3c0.55,0 1.06,-0.09 1.51,-0.27 0.45,-0.18 0.84,-0.43 1.16,-0.76 0.32,-0.33 0.57,-0.73 0.74,-1.19 0.17,-0.47 0.26,-0.99 0.26,-1.57v-0.4c0,-0.58 -0.09,-1.1 -0.26,-1.57 -0.18,-0.47 -0.43,-0.87 -0.75,-1.2zm-0.39,3.16c0,0.42 -0.05,0.79 -0.14,1.13 -0.1,0.33 -0.24,0.62 -0.43,0.85 -0.19,0.23 -0.43,0.41 -0.71,0.53 -0.29,0.12 -0.62,0.18 -0.99,0.18h-0.91V9.12h0.97c0.72,0 1.27,0.23 1.64,0.69 0.38,0.46 0.57,1.12 0.57,1.99v0.4zM12,0l-0.66,0.03 3.81,3.81 1.33,-1.33c3.27,1.55 5.61,4.72 5.96,8.48h1.5C23.44,4.84 18.29,0 12,0z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_accessibility_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_accessibility_24dp.xml
new file mode 100644
index 0000000..075be55
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_accessibility_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zm9,7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_24dp.xml
new file mode 100644
index 0000000..c3a8755
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,10v7h3v-7H4zm6,0v7h3v-7h-3zM2,22h19v-3H2v3zm14,-12v7h3v-7h-3zm-4.5,-9L2,6v2h19V6l-9.5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_wallet_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_wallet_24dp.xml
new file mode 100644
index 0000000..6b570ff
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_wallet_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,18v1c0,1.1 -0.9,2 -2,2H5c-1.11,0 -2,-0.9 -2,-2V5c0,-1.1 0.89,-2 2,-2h14c1.1,0 2,0.9 2,2v1h-9c-1.11,0 -2,0.9 -2,2v8c0,1.1 0.89,2 2,2h9zm-9,-2h10V8H12v8zm4,-2.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_box_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_box_24dp.xml
new file mode 100644
index 0000000..13ed54c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_box_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2H5c-1.11,0 -2,0.9 -2,2zm12,4c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3zm-9,8c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1H6v-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_child_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_child_24dp.xml
new file mode 100644
index 0000000..c40ad49
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_child_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,13.49m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,2.5c1.24,0 2.25,1.01 2.25,2.25S13.24,9 12,9 9.75,7.99 9.75,6.75 10.76,4.5 12,4.5zm5,10.56v2.5c-0.45,0.41 -0.96,0.77 -1.5,1.05v-0.68c0,-0.34 -0.17,-0.65 -0.46,-0.92 -0.65,-0.62 -1.89,-1.02 -3.04,-1.02 -0.96,0 -1.96,0.28 -2.65,0.73l-0.17,0.12 -0.21,0.17c0.78,0.47 1.63,0.72 2.54,0.82l1.33,0.15c0.37,0.04 0.66,0.36 0.66,0.75 0,0.29 -0.16,0.53 -0.4,0.66 -0.28,0.15 -0.64,0.09 -0.95,0.09 -0.35,0 -0.69,-0.01 -1.03,-0.05 -0.5,-0.06 -0.99,-0.17 -1.46,-0.33 -0.49,-0.16 -0.97,-0.38 -1.42,-0.64 -0.22,-0.13 -0.44,-0.27 -0.65,-0.43l-0.31,-0.24c-0.04,-0.02 -0.28,-0.18 -0.28,-0.23v-4.28c0,-1.58 2.63,-2.78 5,-2.78s5,1.2 5,2.78v1.78z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_circle_24dp.xml
new file mode 100644
index 0000000..1f209f7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,3c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm0,14.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_add_shopping_cart_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_add_shopping_cart_24dp.xml
new file mode 100644
index 0000000..281dd33
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_add_shopping_cart_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4,9c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zm10,0c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-9.83,-3.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.86,-7.01L19.42,4h-0.01l-1.1,2 -2.76,5H8.53l-0.13,-0.27L6.16,6l-0.95,-2 -0.94,-2H1v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.13,0 -0.25,-0.11 -0.25,-0.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_24dp.xml
new file mode 100644
index 0000000..7918b03
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8H11v6l4.75,2.85 0.75,-1.23 -4,-2.37V8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_add_24dp.xml
new file mode 100644
index 0000000..5e0d986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm1,-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_off_24dp.xml
new file mode 100644
index 0000000..b041418
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,6c3.87,0 7,3.13 7,7 0,0.84 -0.16,1.65 -0.43,2.4l1.52,1.52c0.58,-1.19 0.91,-2.51 0.91,-3.92 0,-4.97 -4.03,-9 -9,-9 -1.41,0 -2.73,0.33 -3.92,0.91L9.6,6.43C10.35,6.16 11.16,6 12,6zm10,-0.28l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM2.92,2.29L1.65,3.57 2.98,4.9l-1.11,0.93 1.42,1.42 1.11,-0.94 0.8,0.8C3.83,8.69 3,10.75 3,13c0,4.97 4.02,9 9,9 2.25,0 4.31,-0.83 5.89,-2.2l2.2,2.2 1.27,-1.27L3.89,3.27l-0.97,-0.98zm13.55,16.1C15.26,19.39 13.7,20 12,20c-3.87,0 -7,-3.13 -7,-7 0,-1.7 0.61,-3.26 1.61,-4.47l9.86,9.86zM8.02,3.28L6.6,1.86l-0.86,0.71 1.42,1.42 0.86,-0.71z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_on_24dp.xml
new file mode 100644
index 0000000..c8e3f7a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm-1.46,-5.47L8.41,12.4l-1.06,1.06 3.18,3.18 6,-6 -1.06,-1.06 -4.93,4.95z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_android_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_android_24dp.xml
new file mode 100644
index 0000000..5aaace3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_android_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5V19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5V19h1c0.55,0 1,-0.45 1,-1V8H6v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zm17,0c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zm-4.97,-5.84l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5H9V4h1v1zm5,0h-1V4h1v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_announcement_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_announcement_24dp.xml
new file mode 100644
index 0000000..da2bb25
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_announcement_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,9h-2V5h2v6zm0,4h-2v-2h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_aspect_ratio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_aspect_ratio_24dp.xml
new file mode 100644
index 0000000..a6892ce
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_aspect_ratio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3V7H5v5h2V9zm14,-6H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assessment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assessment_24dp.xml
new file mode 100644
index 0000000..1d393b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assessment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2V7h2v10zm4,0h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_24dp.xml
new file mode 100644
index 0000000..0717c5f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm2,14H7v-2h7v2zm3,-4H7v-2h10v2zm0,-4H7V7h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_ind_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_ind_24dp.xml
new file mode 100644
index 0000000..0e0db8d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_ind_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm0,4c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H6v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1V19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_late_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_late_24dp.xml
new file mode 100644
index 0000000..cbf3474
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_late_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-6,15h-2v-2h2v2zm0,-4h-2V8h2v6zm-1,-9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_return_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_return_24dp.xml
new file mode 100644
index 0000000..f320ca4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_return_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm4,12h-4v3l-5,-5 5,-5v3h4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_returned_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_returned_24dp.xml
new file mode 100644
index 0000000..094cc9d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_returned_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm0,15l-5,-5h3V9h4v4h3l-5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_turned_in_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_turned_in_24dp.xml
new file mode 100644
index 0000000..818d8f1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_turned_in_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm-2,14l-4,-4 1.41,-1.41L10,14.17l6.59,-6.59L18,9l-8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_autorenew_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_autorenew_24dp.xml
new file mode 100644
index 0000000..25da1c4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_autorenew_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zm6.76,1.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_backup_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_backup_24dp.xml
new file mode 100644
index 0000000..9a79995
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_backup_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_book_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_book_24dp.xml
new file mode 100644
index 0000000..005d746
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_book_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_24dp.xml
new file mode 100644
index 0000000..61d6f32
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_outline_24dp.xml
new file mode 100644
index 0000000..9605ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2zm0,15l-5,-2.18L7,18V5h10v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_bug_report_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_bug_report_24dp.xml
new file mode 100644
index 0000000..70894fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_bug_report_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8H4v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3H20v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1H20V8zm-6,8h-4v-2h4v2zm0,-4h-4v-2h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_cached_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_cached_24dp.xml
new file mode 100644
index 0000000..a987172
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_cached_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,8l-4,4h3c0,3.31 -2.69,6 -6,6 -1.01,0 -1.97,-0.25 -2.8,-0.7l-1.46,1.46C8.97,19.54 10.43,20 12,20c4.42,0 8,-3.58 8,-8h3l-4,-4zM6,12c0,-3.31 2.69,-6 6,-6 1.01,0 1.97,0.25 2.8,0.7l1.46,-1.46C15.03,4.46 13.57,4 12,4c-4.42,0 -8,3.58 -8,8H1l4,4 4,-4H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_check_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_check_circle_24dp.xml
new file mode 100644
index 0000000..0ad8603
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_check_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-2,15l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_class_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_class_24dp.xml
new file mode 100644
index 0000000..005d746
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_class_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_credit_card_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_credit_card_24dp.xml
new file mode 100644
index 0000000..3f58e10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_credit_card_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6c0,-1.11 -0.89,-2 -2,-2zm0,14H4v-6h16v6zm0,-10H4V6h16v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_dashboard_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_dashboard_24dp.xml
new file mode 100644
index 0000000..c9c8fa6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_dashboard_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,13h8V3H3v10zm0,8h8v-6H3v6zm10,0h8V11h-8v10zm0,-18v6h8V3h-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_delete_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_delete_24dp.xml
new file mode 100644
index 0000000..fc0a9fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_delete_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_description_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_description_24dp.xml
new file mode 100644
index 0000000..9edc43e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_description_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zm2,16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5,9H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_dns_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_dns_24dp.xml
new file mode 100644
index 0000000..b72f863
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_dns_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,13H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM7,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM20,3H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1zM7,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_done_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_24dp.xml
new file mode 100644
index 0000000..02e8321
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_done_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_all_24dp.xml
new file mode 100644
index 0000000..d910df1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zm4.24,-1.41L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_event_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_event_24dp.xml
new file mode 100644
index 0000000..e0030fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_event_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,12h-5v5h5v-5zM16,1v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2h-1V1h-2zm3,18H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_exit_to_app_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_exit_to_app_24dp.xml
new file mode 100644
index 0000000..4aa81b0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_exit_to_app_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_explore_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_explore_24dp.xml
new file mode 100644
index 0000000..affc1d7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_explore_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm2.19,12.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_extension_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_extension_24dp.xml
new file mode 100644
index 0000000..57a615d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_extension_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_face_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_face_24dp.xml
new file mode 100644
index 0000000..70e4ff1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_face_24dp.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.69,17.1c-0.74,0.58 -1.7,0.9 -2.69,0.9s-1.95,-0.32 -2.69,-0.9c-0.22,-0.17 -0.53,-0.13 -0.7,0.09 -0.17,0.22 -0.13,0.53 0.09,0.7 0.91,0.72 2.09,1.11 3.3,1.11s2.39,-0.39 3.31,-1.1c0.22,-0.17 0.26,-0.48 0.09,-0.7 -0.17,-0.23 -0.49,-0.26 -0.71,-0.1z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8.5,12.5m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,0C5.37,0 0,5.37 0,12s5.37,12 12,12 12,-5.37 12,-12S18.63,0 12,0zm7.96,14.82c-1.09,3.74 -4.27,6.46 -8.04,6.46 -3.78,0 -6.96,-2.72 -8.04,-6.47 -1.19,-0.11 -2.13,-1.18 -2.13,-2.52 0,-1.27 0.85,-2.31 1.97,-2.5 2.09,-1.46 3.8,-3.49 4.09,-5.05v-0.01c1.35,2.63 6.3,5.19 11.83,5.06l0.3,-0.03c1.28,0 2.31,1.14 2.31,2.54 0,1.38 -1.02,2.51 -2.29,2.52z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.5,12.5m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_24dp.xml
new file mode 100644
index 0000000..1e38193
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_outline_24dp.xml
new file mode 100644
index 0000000..3a5916c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zm-4.4,15.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_find_in_page_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_in_page_24dp.xml
new file mode 100644
index 0000000..2a9c2fe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_in_page_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,19.59V8l-6,-6H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c0.45,0 0.85,-0.15 1.19,-0.4l-4.43,-4.43c-0.8,0.52 -1.74,0.83 -2.76,0.83 -2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5c0,1.02 -0.31,1.96 -0.83,2.75L20,19.59zM9,13c0,1.66 1.34,3 3,3s3,-1.34 3,-3 -1.34,-3 -3,-3 -3,1.34 -3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_find_replace_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_replace_24dp.xml
new file mode 100644
index 0000000..f11608e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_replace_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,6c1.38,0 2.63,0.56 3.54,1.46L12,10h6V4l-2.05,2.05C14.68,4.78 12.93,4 11,4c-3.53,0 -6.43,2.61 -6.92,6H6.1c0.46,-2.28 2.48,-4 4.9,-4zm5.64,9.14c0.66,-0.9 1.12,-1.97 1.28,-3.14H15.9c-0.46,2.28 -2.48,4 -4.9,4 -1.38,0 -2.63,-0.56 -3.54,-1.46L10,12H4v6l2.05,-2.05C7.32,17.22 9.07,18 11,18c1.55,0 2.98,-0.51 4.14,-1.36L20,21.49 21.49,20l-4.85,-4.86z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_back_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_back_24dp.xml
new file mode 100644
index 0000000..0fadaf5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_back_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,7H7v2h2V7zm0,4H7v2h2v-2zm0,-8c-1.11,0 -2,0.9 -2,2h2V3zm4,12h-2v2h2v-2zm6,-12v2h2c0,-1.1 -0.9,-2 -2,-2zm-6,0h-2v2h2V3zM9,17v-2H7c0,1.1 0.89,2 2,2zm10,-4h2v-2h-2v2zm0,-4h2V7h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2zM5,7H3v12c0,1.1 0.89,2 2,2h12v-2H5V7zm10,-2h2V3h-2v2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_front_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_front_24dp.xml
new file mode 100644
index 0000000..878e1ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_front_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,13h2v-2H3v2zm0,4h2v-2H3v2zm2,4v-2H3c0,1.1 0.89,2 2,2zM3,9h2V7H3v2zm12,12h2v-2h-2v2zm4,-18H9c-1.11,0 -2,0.9 -2,2v10c0,1.1 0.89,2 2,2h10c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,12H9V5h10v10zm-8,6h2v-2h-2v2zm-4,0h2v-2H7v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_get_app_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_get_app_24dp.xml
new file mode 100644
index 0000000..9c90028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_get_app_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_grade_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_grade_24dp.xml
new file mode 100644
index 0000000..e2c53b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_grade_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_group_work_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_group_work_24dp.xml
new file mode 100644
index 0000000..f7d2245
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_group_work_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM8,17.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5zM9.5,8c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5S9.5,9.38 9.5,8zm6.5,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_help_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_help_24dp.xml
new file mode 100644
index 0000000..5b524e3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_help_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,17h-2v-2h2v2zm2.07,-7.75l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2H8c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_highlight_remove_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_highlight_remove_24dp.xml
new file mode 100644
index 0000000..e1302a7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_highlight_remove_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_history_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_history_24dp.xml
new file mode 100644
index 0000000..a290938
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_history_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,3c-4.97,0 -9,4.03 -9,9H1l3.89,3.89 0.07,0.14L9,12H6c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm-1,5v5l4.28,2.54 0.72,-1.21 -3.5,-2.08V8H12z"
+        android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_home_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_home_24dp.xml
new file mode 100644
index 0000000..2a8e066
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_home_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_https_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_https_24dp.xml
new file mode 100644
index 0000000..9154ab9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_https_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zm3.1,-9H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_info_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_24dp.xml
new file mode 100644
index 0000000..cea2f8d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_info_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_outline_24dp.xml
new file mode 100644
index 0000000..f500013
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,17h2v-6h-2v6zm1,-15C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2V7h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_input_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_input_24dp.xml
new file mode 100644
index 0000000..6b10c4e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_input_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3.01H3c-1.1,0 -2,0.9 -2,2V9h2V4.99h18v14.03H3V15H1v4.01c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98v-14c0,-1.11 -0.9,-2 -2,-2zM11,16l4,-4 -4,-4v3H1v2h10v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_invert_colors_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_invert_colors_24dp.xml
new file mode 100644
index 0000000..8d98986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_invert_colors_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.66,7.93L12,2.27 6.34,7.93c-3.12,3.12 -3.12,8.19 0,11.31C7.9,20.8 9.95,21.58 12,21.58c2.05,0 4.1,-0.78 5.66,-2.34 3.12,-3.12 3.12,-8.19 0,-11.31zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_label_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_24dp.xml
new file mode 100644
index 0000000..7958502
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_label_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_outline_24dp.xml
new file mode 100644
index 0000000..06069cb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16zM16,17H5V7h11l3.55,5L16,17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_language_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_language_24dp.xml
new file mode 100644
index 0000000..6e17188
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_language_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zm6.93,6h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2H4.26zm0.82,2h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zm2.95,-8H5.08c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14H9.66c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zm0.25,5.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_launch_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_launch_24dp.xml
new file mode 100644
index 0000000..c8b33be
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_launch_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_list_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_list_24dp.xml
new file mode 100644
index 0000000..689c602
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_list_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,13h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2V7H3v2zm4,4h14v-2H7v2zm0,4h14v-2H7v2zM7,7v2h14V7H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_24dp.xml
new file mode 100644
index 0000000..9154ab9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zm3.1,-9H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_open_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_open_24dp.xml
new file mode 100644
index 0000000..bcd6b79
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_open_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm6,-9h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm0,12H6V10h12v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_outline_24dp.xml
new file mode 100644
index 0000000..10e2fcf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,-5.1c1.71,0 3.1,1.39 3.1,3.1v2H9V6h-0.1c0,-1.71 1.39,-3.1 3.1,-3.1zM18,20H6V10h12v10zm-6,-3c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_loyalty_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_loyalty_24dp.xml
new file mode 100644
index 0000000..9a92666
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_loyalty_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21.41,11.58l-9,-9C12.05,2.22 11.55,2 11,2H4c-1.1,0 -2,0.9 -2,2v7c0,0.55 0.22,1.05 0.59,1.42l9,9c0.36,0.36 0.86,0.58 1.41,0.58 0.55,0 1.05,-0.22 1.41,-0.59l7,-7c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-0.55 -0.23,-1.06 -0.59,-1.42zM5.5,7C4.67,7 4,6.33 4,5.5S4.67,4 5.5,4 7,4.67 7,5.5 6.33,7 5.5,7zm11.77,8.27L13,19.54l-4.27,-4.27C8.28,14.81 8,14.19 8,13.5c0,-1.38 1.12,-2.5 2.5,-2.5 0.69,0 1.32,0.28 1.77,0.74l0.73,0.72 0.73,-0.73c0.45,-0.45 1.08,-0.73 1.77,-0.73 1.38,0 2.5,1.12 2.5,2.5 0,0.69 -0.28,1.32 -0.73,1.77z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_markunread_mailbox_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_markunread_mailbox_24dp.xml
new file mode 100644
index 0000000..b947d8f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_markunread_mailbox_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6H10v6H8V4h6V0H6v6H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_note_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_note_add_24dp.xml
new file mode 100644
index 0000000..20a348e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_note_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zm2,14h-3v3h-2v-3H8v-2h3v-3h2v3h3v2zm-3,-7V3.5L18.5,9H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_browser_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_browser_24dp.xml
new file mode 100644
index 0000000..26ca0e3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_browser_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,4H5c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2H5V8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.89,-2 -2,-2zm-7,6l-4,4h3v6h2v-6h3l-4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_new_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_new_24dp.xml
new file mode 100644
index 0000000..c8b33be
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_new_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_open_with_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_with_24dp.xml
new file mode 100644
index 0000000..9b115d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_with_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,9h4V6h3l-5,-5 -5,5h3v3zm-1,1H6V7l-5,5 5,5v-3h3v-4zm14,2l-5,-5v3h-3v4h3v3l5,-5zm-9,3h-4v3H7l5,5 5,-5h-3v-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_pageview_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_pageview_24dp.xml
new file mode 100644
index 0000000..7eadee8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_pageview_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm8,-5H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-1.41,16l-3.83,-3.83c-0.8,0.52 -1.74,0.83 -2.76,0.83 -2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5c0,1.02 -0.31,1.96 -0.83,2.75L19,17.59 17.59,19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_payment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_payment_24dp.xml
new file mode 100644
index 0000000..3f58e10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_payment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6c0,-1.11 -0.89,-2 -2,-2zm0,14H4v-6h16v6zm0,-10H4V6h16v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_camera_mic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_camera_mic_24dp.xml
new file mode 100644
index 0000000..fc8a6cb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_camera_mic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,5h-3.17L15,3H9L7.17,5H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v-2.09c-2.83,-0.48 -5,-2.94 -5,-5.91h2c0,2.21 1.79,4 4,4s4,-1.79 4,-4h2c0,2.97 -2.17,5.43 -5,5.91V21h7c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm-6,8c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V9c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_contact_cal_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_contact_cal_24dp.xml
new file mode 100644
index 0000000..10dfe40
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_contact_cal_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,3c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H6v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_data_setting_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_data_setting_24dp.xml
new file mode 100644
index 0000000..f48b14b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_data_setting_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.99,11.5c0.34,0 0.67,0.03 1,0.07L20,0 0,20h11.56c-0.04,-0.33 -0.07,-0.66 -0.07,-1 0,-4.14 3.36,-7.5 7.5,-7.5zm3.71,7.99c0.02,-0.16 0.04,-0.32 0.04,-0.49 0,-0.17 -0.01,-0.33 -0.04,-0.49l1.06,-0.83c0.09,-0.08 0.12,-0.21 0.06,-0.32l-1,-1.73c-0.06,-0.11 -0.19,-0.15 -0.31,-0.11l-1.24,0.5c-0.26,-0.2 -0.54,-0.37 -0.85,-0.49l-0.19,-1.32c-0.01,-0.12 -0.12,-0.21 -0.24,-0.21h-2c-0.12,0 -0.23,0.09 -0.25,0.21l-0.19,1.32c-0.3,0.13 -0.59,0.29 -0.85,0.49l-1.24,-0.5c-0.11,-0.04 -0.24,0 -0.31,0.11l-1,1.73c-0.06,0.11 -0.04,0.24 0.06,0.32l1.06,0.83c-0.02,0.16 -0.03,0.32 -0.03,0.49 0,0.17 0.01,0.33 0.03,0.49l-1.06,0.83c-0.09,0.08 -0.12,0.21 -0.06,0.32l1,1.73c0.06,0.11 0.19,0.15 0.31,0.11l1.24,-0.5c0.26,0.2 0.54,0.37 0.85,0.49l0.19,1.32c0.02,0.12 0.12,0.21 0.25,0.21h2c0.12,0 0.23,-0.09 0.25,-0.21l0.19,-1.32c0.3,-0.13 0.59,-0.29 0.84,-0.49l1.25,0.5c0.11,0.04 0.24,0 0.31,-0.11l1,-1.73c0.06,-0.11 0.03,-0.24 -0.06,-0.32l-1.07,-0.83zm-3.71,1.01c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_device_info_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_device_info_24dp.xml
new file mode 100644
index 0000000..54536e4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_device_info_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,7h-2v2h2V7zm0,4h-2v6h2v-6zm4,-9.99L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_identity_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_identity_24dp.xml
new file mode 100644
index 0000000..a34bd73
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_identity_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1H5.9V17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,9c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_media_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_media_24dp.xml
new file mode 100644
index 0000000..e225808
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_media_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,6H0v5h0.01L0,20c0,1.1 0.9,2 2,2h18v-2H2V6zm20,-2h-8l-2,-2H6c-1.1,0 -1.99,0.9 -1.99,2L4,16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM7,15l4.5,-6 3.5,4.51 2.5,-3.01L21,15H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_phone_msg_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_phone_msg_24dp.xml
new file mode 100644
index 0000000..842b965
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_phone_msg_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.58l2.2,-2.21c0.28,-0.27 0.36,-0.66 0.25,-1.01C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM12,3v10l3,-3h6V3h-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_scan_wifi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_scan_wifi_24dp.xml
new file mode 100644
index 0000000..6a82875
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_scan_wifi_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,3C6.95,3 3.15,4.85 0,7.23L12,22 24,7.25C20.85,4.87 17.05,3 12,3zm1,13h-2v-6h2v6zm-2,-8V6h2v2h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_picture_in_picture_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_picture_in_picture_24dp.xml
new file mode 100644
index 0000000..9b5d81e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_picture_in_picture_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,7h-8v6h8V7zm2,-4H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.98h18v14.03z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_polymer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_polymer_24dp.xml
new file mode 100644
index 0000000..bee03f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_polymer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,4h-4L7.11,16.63 4.5,12 9,4H5L0.5,12 5,20h4l7.89,-12.63L19.5,12 15,20h4l4.5,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_print_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_print_24dp.xml
new file mode 100644
index 0000000..9bfb690
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_print_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,8H5c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zm-3,11H8v-5h8v5zm3,-7c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm-1,-9H6v4h12V3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_query_builder_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_query_builder_24dp.xml
new file mode 100644
index 0000000..cbbd47e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_query_builder_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_question_answer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_question_answer_24dp.xml
new file mode 100644
index 0000000..5e7d985
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_question_answer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,6h-2v9H6v2c0,0.55 0.45,1 1,1h11l4,4V7c0,-0.55 -0.45,-1 -1,-1zm-4,6V3c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_receipt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_receipt_24dp.xml
new file mode 100644
index 0000000..5a02c79
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_receipt_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,17H6v-2h12v2zm0,-4H6v-2h12v2zm0,-4H6V7h12v2zM3,22l1.5,-1.5L6,22l1.5,-1.5L9,22l1.5,-1.5L12,22l1.5,-1.5L15,22l1.5,-1.5L18,22l1.5,-1.5L21,22V2l-1.5,1.5L18,2l-1.5,1.5L15,2l-1.5,1.5L12,2l-1.5,1.5L9,2 7.5,3.5 6,2 4.5,3.5 3,2v20z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_redeem_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_redeem_24dp.xml
new file mode 100644
index 0000000..7f78847
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_redeem_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zm-5,-2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm11,15H4v-2h16v2zm0,-5H4V8h5.08L7,10.83 8.62,12 11,8.76l1,-1.36 1,1.36L15.38,12 17,10.83 14.92,8H20v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_reorder_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_reorder_24dp.xml
new file mode 100644
index 0000000..78be3e9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_reorder_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,15h18v-2H3v2zm0,4h18v-2H3v2zm0,-8h18V9H3v2zm0,-6v2h18V5H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_report_problem_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_report_problem_24dp.xml
new file mode 100644
index 0000000..70ad19a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_report_problem_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_restore_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_restore_24dp.xml
new file mode 100644
index 0000000..9963c56
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_restore_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,3c-4.97,0 -9,4.03 -9,9H1l3.89,3.89 0.07,0.14L9,12H6c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm-1,5v5l4.28,2.54 0.72,-1.21 -3.5,-2.08V8H12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_room_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_room_24dp.xml
new file mode 100644
index 0000000..04b8734
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_room_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_schedule_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_schedule_24dp.xml
new file mode 100644
index 0000000..7bf67c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_schedule_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"
+        android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_search_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_search_24dp.xml
new file mode 100644
index 0000000..ed83d8e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_search_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zm-6,0C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_24dp.xml
new file mode 100644
index 0000000..e92e5b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_applications_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_applications_24dp.xml
new file mode 100644
index 0000000..19e3442
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_applications_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm7,-7H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2V5c0,-1.1 -0.89,-2 -2,-2zm-1.75,9c0,0.23 -0.02,0.46 -0.05,0.68l1.48,1.16c0.13,0.11 0.17,0.3 0.08,0.45l-1.4,2.42c-0.09,0.15 -0.27,0.21 -0.43,0.15l-1.74,-0.7c-0.36,0.28 -0.76,0.51 -1.18,0.69l-0.26,1.85c-0.03,0.17 -0.18,0.3 -0.35,0.3h-2.8c-0.17,0 -0.32,-0.13 -0.35,-0.29l-0.26,-1.85c-0.43,-0.18 -0.82,-0.41 -1.18,-0.69l-1.74,0.7c-0.16,0.06 -0.34,0 -0.43,-0.15l-1.4,-2.42c-0.09,-0.15 -0.05,-0.34 0.08,-0.45l1.48,-1.16c-0.03,-0.23 -0.05,-0.46 -0.05,-0.69 0,-0.23 0.02,-0.46 0.05,-0.68l-1.48,-1.16c-0.13,-0.11 -0.17,-0.3 -0.08,-0.45l1.4,-2.42c0.09,-0.15 0.27,-0.21 0.43,-0.15l1.74,0.7c0.36,-0.28 0.76,-0.51 1.18,-0.69l0.26,-1.85c0.03,-0.17 0.18,-0.3 0.35,-0.3h2.8c0.17,0 0.32,0.13 0.35,0.29l0.26,1.85c0.43,0.18 0.82,0.41 1.18,0.69l1.74,-0.7c0.16,-0.06 0.34,0 0.43,0.15l1.4,2.42c0.09,0.15 0.05,0.34 -0.08,0.45l-1.48,1.16c0.03,0.23 0.05,0.46 0.05,0.69z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_backup_restore_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_backup_restore_24dp.xml
new file mode 100644
index 0000000..874ab3b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_backup_restore_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zm-2,-9c-4.97,0 -9,4.03 -9,9H0l4,4 4,-4H5c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_bluetooth_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_bluetooth_24dp.xml
new file mode 100644
index 0000000..23ad97e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_bluetooth_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,24h2v-2h-2v2zm-4,0h2v-2H7v2zm8,0h2v-2h-2v2zm2.71,-18.29L12,0h-1v7.59L6.41,3 5,4.41 10.59,10 5,15.59 6.41,17 11,12.41V20h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,3.83l1.88,1.88L13,7.59V3.83zm1.88,10.46L13,16.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_cell_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_cell_24dp.xml
new file mode 100644
index 0000000..89fdea7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_cell_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,24h2v-2H7v2zm4,0h2v-2h-2v2zm4,0h2v-2h-2v2zM16,0.01L8,0C6.9,0 6,0.9 6,2v16c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V2c0,-1.1 -0.9,-1.99 -2,-1.99zM16,16H8V4h8v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_display_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_display_24dp.xml
new file mode 100644
index 0000000..65bd27e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_display_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02zM8,16h2.5l1.5,1.5 1.5,-1.5H16v-2.5l1.5,-1.5 -1.5,-1.5V8h-2.5L12,6.5 10.5,8H8v2.5L6.5,12 8,13.5V16zm4,-7c1.66,0 3,1.34 3,3s-1.34,3 -3,3V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_ethernet_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_ethernet_24dp.xml
new file mode 100644
index 0000000..57be67f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_ethernet_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.77,6.76L6.23,5.48 0.82,12l5.41,6.52 1.54,-1.28L3.42,12l4.35,-5.24zM7,13h2v-2H7v2zm10,-2h-2v2h2v-2zm-6,2h2v-2h-2v2zm6.77,-7.52l-1.54,1.28L20.58,12l-4.35,5.24 1.54,1.28L23.18,12l-5.41,-6.52z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_antenna_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_antenna_24dp.xml
new file mode 100644
index 0000000..d73b70d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_antenna_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,5c-3.87,0 -7,3.13 -7,7h2c0,-2.76 2.24,-5 5,-5s5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm1,9.29c0.88,-0.39 1.5,-1.26 1.5,-2.29 0,-1.38 -1.12,-2.5 -2.5,-2.5S9.5,10.62 9.5,12c0,1.02 0.62,1.9 1.5,2.29v3.3L7.59,21 9,22.41l3,-3 3,3L16.41,21 13,17.59v-3.3zM12,1C5.93,1 1,5.93 1,12h2c0,-4.97 4.03,-9 9,-9s9,4.03 9,9h2c0,-6.07 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_component_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_component_24dp.xml
new file mode 100644
index 0000000..bf8dcc5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_component_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H1v6h6V6H5V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2H9v2zm-8,0c0,1.3 0.84,2.4 2,2.82V23h2v-4.18C6.16,18.4 7,17.3 7,16v-2H1v2zM21,6V2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4h-2v6h6V6h-2zm-8,-4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H9v6h6V6h-2V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2h-6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_composite_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_composite_24dp.xml
new file mode 100644
index 0000000..bf8dcc5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_composite_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H1v6h6V6H5V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2H9v2zm-8,0c0,1.3 0.84,2.4 2,2.82V23h2v-4.18C6.16,18.4 7,17.3 7,16v-2H1v2zM21,6V2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4h-2v6h6V6h-2zm-8,-4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H9v6h6V6h-2V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2h-6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_hdmi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_hdmi_24dp.xml
new file mode 100644
index 0000000..a6c8cfb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_hdmi_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,7V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v3H5v6l3,6v3h8v-3l3,-6V7h-1zM8,4h8v3h-2V5h-1v2h-2V5h-1v2H8V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_svideo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_svideo_24dp.xml
new file mode 100644
index 0000000..a062b66
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_svideo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,11.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S5,10.67 5,11.5 5.67,13 6.5,13 8,12.33 8,11.5zm7,-5c0,-0.83 -0.67,-1.5 -1.5,-1.5h-3C9.67,5 9,5.67 9,6.5S9.67,8 10.5,8h3c0.83,0 1.5,-0.67 1.5,-1.5zM8.5,15c-0.83,0 -1.5,0.67 -1.5,1.5S7.67,18 8.5,18s1.5,-0.67 1.5,-1.5S9.33,15 8.5,15zM12,1C5.93,1 1,5.93 1,12s4.93,11 11,11 11,-4.93 11,-11S18.07,1 12,1zm0,20c-4.96,0 -9,-4.04 -9,-9s4.04,-9 9,-9 9,4.04 9,9 -4.04,9 -9,9zm5.5,-11c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm-2,5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_overscan_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_overscan_24dp.xml
new file mode 100644
index 0000000..2e0b0dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_overscan_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,5.5L10,8h4l-1.99,-2.5zM18,10v4l2.5,-1.99L18,10zM6,10l-2.5,2.01L6,14v-4zm8,6h-4l2.01,2.5L14,16zm7,-13H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_phone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_phone_24dp.xml
new file mode 100644
index 0000000..eb7881b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_phone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,9h-2v2h2V9zm4,0h-2v2h2V9zm3,6.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.58l2.2,-2.21c0.28,-0.27 0.36,-0.66 0.25,-1.01C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,9v2h2V9h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_power_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_power_24dp.xml
new file mode 100644
index 0000000..8d64e7f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_power_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,24h2v-2H7v2zm4,0h2v-2h-2v2zm2,-22h-2v10h2V2zm3.56,2.44l-1.45,1.45C16.84,6.94 18,8.83 18,11c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6c0,-2.17 1.16,-4.06 2.88,-5.12L7.44,4.44C5.36,5.88 4,8.28 4,11c0,4.42 3.58,8 8,8s8,-3.58 8,-8c0,-2.72 -1.36,-5.12 -3.44,-6.56zM15,24h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_remote_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_remote_24dp.xml
new file mode 100644
index 0000000..ed5c7cc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_remote_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,9H9c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1V10c0,-0.55 -0.45,-1 -1,-1zm-3,6c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM7.05,6.05l1.41,1.41C9.37,6.56 10.62,6 12,6s2.63,0.56 3.54,1.46l1.41,-1.41C15.68,4.78 13.93,4 12,4s-3.68,0.78 -4.95,2.05zM12,0C8.96,0 6.21,1.23 4.22,3.22l1.41,1.41C7.26,3.01 9.51,2 12,2s4.74,1.01 6.36,2.64l1.41,-1.41C17.79,1.23 15.04,0 12,0z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_voice_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_voice_24dp.xml
new file mode 100644
index 0000000..a3857ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_voice_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,24h2v-2H7v2zm5,-11c1.66,0 2.99,-1.34 2.99,-3L15,4c0,-1.66 -1.34,-3 -3,-3S9,2.34 9,4v6c0,1.66 1.34,3 3,3zm-1,11h2v-2h-2v2zm4,0h2v-2h-2v2zm4,-14h-1.7c0,3 -2.54,5.1 -5.3,5.1S6.7,13 6.7,10H5c0,3.41 2.72,6.23 6,6.72V20h2v-3.28c3.28,-0.49 6,-3.31 6,-6.72z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_24dp.xml
new file mode 100644
index 0000000..97e4e62
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,6V4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H2v13c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6h-6zm-6,-2h4v2h-4V4zM9,18V9l7.5,4L9,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_two_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_two_24dp.xml
new file mode 100644
index 0000000..8634fb3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_two_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,9H1v11c0,1.11 0.89,2 2,2h14c1.11,0 2,-0.89 2,-2H3V9zm15,-4V3c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H5v11c0,1.11 0.89,2 2,2h14c1.11,0 2,-0.89 2,-2V5h-5zm-6,-2h4v2h-4V3zm0,12V8l5.5,3 -5.5,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_basket_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_basket_24dp.xml
new file mode 100644
index 0000000..2207e4f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_basket_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.21,9l-4.38,-6.56c-0.19,-0.28 -0.51,-0.42 -0.83,-0.42 -0.32,0 -0.64,0.14 -0.83,0.43L6.79,9H2c-0.55,0 -1,0.45 -1,1 0,0.09 0.01,0.18 0.04,0.27l2.54,9.27c0.23,0.84 1,1.46 1.92,1.46h13c0.92,0 1.69,-0.62 1.93,-1.46l2.54,-9.27L23,10c0,-0.55 -0.45,-1 -1,-1h-4.79zM9,9l3,-4.4L15,9H9zm3,8c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_cart_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_cart_24dp.xml
new file mode 100644
index 0000000..f28015e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_cart_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1H5.21l-0.94,-2H1zm16,16c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_speaker_notes_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_speaker_notes_24dp.xml
new file mode 100644
index 0000000..de7f2bc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_speaker_notes_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM8,14H6v-2h2v2zm0,-3H6V9h2v2zm0,-3H6V6h2v2zm7,6h-5v-2h5v2zm3,-3h-8V9h8v2zm0,-3h-8V6h8v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_spellcheck_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_spellcheck_24dp.xml
new file mode 100644
index 0000000..a61fef1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_spellcheck_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.45,16h2.09L9.43,3H7.57L2.46,16h2.09l1.12,-3h5.64l1.14,3zm-6.02,-5L8.5,5.48 10.57,11H6.43zm15.16,0.59l-8.09,8.09L9.83,16l-1.41,1.41 5.09,5.09L23,13l-1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_star_rate_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_star_rate_24dp.xml
new file mode 100644
index 0000000..44811e6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_star_rate_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,14.3l3.71,2.7 -1.42,-4.36L18,10h-4.55L12,5.5 10.55,10H6l3.71,2.64L8.29,17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_stars_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_stars_24dp.xml
new file mode 100644
index 0000000..94f4a56
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_stars_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zm4.24,16L12,15.45 7.77,18l1.12,-4.81 -3.73,-3.23 4.92,-0.42L12,5l1.92,4.53 4.92,0.42 -3.73,3.23L16.23,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_store_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_store_24dp.xml
new file mode 100644
index 0000000..cef78e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_store_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4v2h16V4zm1,10v-2l-1,-5H4l-1,5v2h1v6h10v-6h4v6h2v-6h1zm-9,4H6v-4h6v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_subject_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_subject_24dp.xml
new file mode 100644
index 0000000..0049c9c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_subject_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,17H4v2h10v-2zm6,-8H4v2h16V9zM4,15h16v-2H4v2zM4,5v2h16V5H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_supervisor_account_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_supervisor_account_24dp.xml
new file mode 100644
index 0000000..e775937
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_supervisor_account_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,12c1.38,0 2.49,-1.12 2.49,-2.5S17.88,7 16.5,7C15.12,7 14,8.12 14,9.5s1.12,2.5 2.5,2.5zM9,11c1.66,0 2.99,-1.34 2.99,-3S10.66,5 9,5C7.34,5 6,6.34 6,8s1.34,3 3,3zm7.5,3c-1.83,0 -5.5,0.92 -5.5,2.75V19h11v-2.25c0,-1.83 -3.67,-2.75 -5.5,-2.75zM9,13c-2.33,0 -7,1.17 -7,3.5V19h7v-2.25c0,-0.85 0.33,-2.34 2.37,-3.47C10.5,13.1 9.66,13 9,13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_horiz_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_horiz_24dp.xml
new file mode 100644
index 0000000..de4cb80
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_horiz_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_24dp.xml
new file mode 100644
index 0000000..d7374f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_circle_24dp.xml
new file mode 100644
index 0000000..89dca6e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM6.5,9L10,5.5 13.5,9H11v4H9V9H6.5zm11,6L14,18.5 10.5,15H13v-4h2v4h2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_system_update_tv_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_system_update_tv_24dp.xml
new file mode 100644
index 0000000..191c10a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_system_update_tv_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,16.5l4,-4h-3v-9h-2v9H8l4,4zm9,-13h-6v1.99h6v14.03H3V5.49h6V3.5H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2v-14c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_24dp.xml
new file mode 100644
index 0000000..77cbd77
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H3V5h10v4h8v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_unselected_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_unselected_24dp.xml
new file mode 100644
index 0000000..e7a1cc9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_unselected_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1,9h2V7H1v2zm0,4h2v-2H1v2zm0,-8h2V3c-1.1,0 -2,0.9 -2,2zm8,16h2v-2H9v2zm-8,-4h2v-2H1v2zm2,4v-2H1c0,1.1 0.9,2 2,2zM21,3h-8v6h10V5c0,-1.1 -0.9,-2 -2,-2zm0,14h2v-2h-2v2zM9,5h2V3H9v2zM5,21h2v-2H5v2zM5,5h2V3H5v2zm16,16c1.1,0 2,-0.9 2,-2h-2v2zm0,-8h2v-2h-2v2zm-8,8h2v-2h-2v2zm4,0h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_theaters_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_theaters_24dp.xml
new file mode 100644
index 0000000..7c662e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_theaters_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8,17H6v-2h2v2zm0,-4H6v-2h2v2zm0,-4H6V7h2v2zm10,8h-2v-2h2v2zm0,-4h-2v-2h2v2zm0,-4h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_down_24dp.xml
new file mode 100644
index 0000000..85a2639
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,3H6c-0.83,0 -1.54,0.5 -1.84,1.22l-3.02,7.05c-0.09,0.23 -0.14,0.47 -0.14,0.73v1.91l0.01,0.01L1,14c0,1.1 0.9,2 2,2h6.31l-0.95,4.57 -0.03,0.32c0,0.41 0.17,0.79 0.44,1.06L9.83,23l6.59,-6.59c0.36,-0.36 0.58,-0.86 0.58,-1.41V5c0,-1.1 -0.9,-2 -2,-2zm4,0v12h4V3h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_up_24dp.xml
new file mode 100644
index 0000000..05d5fd3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1,21h4V9H1v12zm22,-11c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-1.91l-0.01,-0.01L23,10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_thumbs_up_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumbs_up_down_24dp.xml
new file mode 100644
index 0000000..9b574e6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumbs_up_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,6c0,-0.55 -0.45,-1 -1,-1H5.82l0.66,-3.18 0.02,-0.23c0,-0.31 -0.13,-0.59 -0.33,-0.8L5.38,0 0.44,4.94C0.17,5.21 0,5.59 0,6v6.5c0,0.83 0.67,1.5 1.5,1.5h6.75c0.62,0 1.15,-0.38 1.38,-0.91l2.26,-5.29c0.07,-0.17 0.11,-0.36 0.11,-0.55V6zm10.5,4h-6.75c-0.62,0 -1.15,0.38 -1.38,0.91l-2.26,5.29c-0.07,0.17 -0.11,0.36 -0.11,0.55V18c0,0.55 0.45,1 1,1h5.18l-0.66,3.18 -0.02,0.24c0,0.31 0.13,0.59 0.33,0.8l0.79,0.78 4.94,-4.94c0.27,-0.27 0.44,-0.65 0.44,-1.06v-6.5c0,-0.83 -0.67,-1.5 -1.5,-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_toc_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_toc_24dp.xml
new file mode 100644
index 0000000..c9f8b1b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_toc_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,9h14V7H3v2zm0,4h14v-2H3v2zm0,4h14v-2H3v2zm16,0h2v-2h-2v2zm0,-10v2h2V7h-2zm0,6h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_today_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_today_24dp.xml
new file mode 100644
index 0000000..ddf162e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_today_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11zM7,10h5v5H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_track_changes_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_track_changes_24dp.xml
new file mode 100644
index 0000000..d402da3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_track_changes_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19.07,4.93l-1.41,1.41C19.1,7.79 20,9.79 20,12c0,4.42 -3.58,8 -8,8s-8,-3.58 -8,-8c0,-4.08 3.05,-7.44 7,-7.93v2.02C8.16,6.57 6,9.03 6,12c0,3.31 2.69,6 6,6s6,-2.69 6,-6c0,-1.66 -0.67,-3.16 -1.76,-4.24l-1.41,1.41C15.55,9.9 16,10.9 16,12c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4c0,-1.86 1.28,-3.41 3,-3.86v2.14c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72V2h-1C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10c0,-2.76 -1.12,-5.26 -2.93,-7.07z"
+        android:fillColor="#231F20"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_translate_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_translate_24dp.xml
new file mode 100644
index 0000000..592b094
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_translate_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zm-2.62,7l1.62,-4.33L19.12,17h-3.24z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_down_24dp.xml
new file mode 100644
index 0000000..3a6ba95
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,18l2.29,-2.29 -4.88,-4.88 -4,4L2,7.41 3.41,6l6,6 4,-4 6.3,6.29L22,12v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_neutral_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_neutral_24dp.xml
new file mode 100644
index 0000000..f5b62c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_neutral_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,12l-4,-4v3H3v2h15v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_up_24dp.xml
new file mode 100644
index 0000000..cd5e654
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,6l2.29,2.29 -4.88,4.88 -4,-4L2,16.59 3.41,18l6,-6 4,4 6.3,-6.29L22,12V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_24dp.xml
new file mode 100644
index 0000000..61d6f32
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_not_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_not_24dp.xml
new file mode 100644
index 0000000..9605ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_not_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2zm0,15l-5,-2.18L7,18V5h10v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_verified_user_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_verified_user_24dp.xml
new file mode 100644
index 0000000..0893b4d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_verified_user_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zm-2,16l-4,-4 1.41,-1.41L10,14.17l6.59,-6.59L18,9l-8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_agenda_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_agenda_24dp.xml
new file mode 100644
index 0000000..3f40bc6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_agenda_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,13H3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zm0,-10H3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_array_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_array_24dp.xml
new file mode 100644
index 0000000..c90c3f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_array_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,18h3V5H4v13zM18,5v13h3V5h-3zM8,18h9V5H8v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_carousel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_carousel_24dp.xml
new file mode 100644
index 0000000..4df06be
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_carousel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,19h10V4H7v15zm-5,-2h4V6H2v11zM18,6v11h4V6h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_column_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_column_24dp.xml
new file mode 100644
index 0000000..b9dc907
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_column_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,18h5V5h-5v13zm-6,0h5V5H4v13zM16,5v13h5V5h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_day_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_day_24dp.xml
new file mode 100644
index 0000000..0734594
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_day_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,21h19v-3H2v3zM20,8H3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1zM2,3v3h19V3H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_headline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_headline_24dp.xml
new file mode 100644
index 0000000..512155c9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_headline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,15h17v-2H4v2zm0,4h17v-2H4v2zm0,-8h17V9H4v2zm0,-6v2h17V5H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_list_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_list_24dp.xml
new file mode 100644
index 0000000..089b28e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_list_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,14h4v-4H4v4zm0,5h4v-4H4v4zM4,9h4V5H4v4zm5,5h12v-4H9v4zm0,5h12v-4H9v4zM9,5v4h12V5H9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_module_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_module_24dp.xml
new file mode 100644
index 0000000..701708c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_module_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,11h5V5H4v6zm0,7h5v-6H4v6zm6,0h5v-6h-5v6zm6,0h5v-6h-5v6zm-6,-7h5V5h-5v6zm6,-6v6h5V5h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_quilt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_quilt_24dp.xml
new file mode 100644
index 0000000..e2f1072
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_quilt_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,18h5v-6h-5v6zm-6,0h5V5H4v13zm12,0h5v-6h-5v6zM10,5v6h11V5H10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_stream_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_stream_24dp.xml
new file mode 100644
index 0000000..5b8c90f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_stream_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,18h17v-6H4v6zM4,5v6h17V5H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_week_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_week_24dp.xml
new file mode 100644
index 0000000..9c0d632
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_week_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,5H3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1zm14,0h-3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1zm-7,0h-3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_24dp.xml
new file mode 100644
index 0000000..46a571c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_off_24dp.xml
new file mode 100644
index 0000000..209bdec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zm4.31,-0.78l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_giftcard_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_giftcard_24dp.xml
new file mode 100644
index 0000000..7f78847
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_giftcard_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zm-5,-2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm11,15H4v-2h16v2zm0,-5H4V8h5.08L7,10.83 8.62,12 11,8.76l1,-1.36 1,1.36L15.38,12 17,10.83 14.92,8H20v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_membership_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_membership_24dp.xml
new file mode 100644
index 0000000..00af65d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_membership_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.11,0 -2,0.89 -2,2v11c0,1.11 0.89,2 2,2h4v5l4,-2 4,2v-5h4c1.11,0 2,-0.89 2,-2V4c0,-1.11 -0.89,-2 -2,-2zm0,13H4v-2h16v2zm0,-5H4V4h16v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_travel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_travel_24dp.xml
new file mode 100644
index 0000000..7ee959d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_travel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-3V4c0,-1.11 -0.89,-2 -2,-2H9c-1.11,0 -2,0.89 -2,2v2H4c-1.11,0 -2,0.89 -2,2v11c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zM9,4h6v2H9V4zm11,15H4v-2h16v2zm0,-5H4V8h3v2h2V8h6v2h2V8h3v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_work_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_work_24dp.xml
new file mode 100644
index 0000000..363b68b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_work_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zm-6,0h-4V4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/alert/ic_error_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/alert/ic_error_24dp.xml
new file mode 100644
index 0000000..ef1a8f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/alert/ic_error_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-2h2v2zm0,-4h-2V7h2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/alert/ic_warning_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/alert/ic_warning_24dp.xml
new file mode 100644
index 0000000..70ad19a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/alert/ic_warning_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_album_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_album_24dp.xml
new file mode 100644
index 0000000..8b3ecb9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_album_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,14.5c-2.49,0 -4.5,-2.01 -4.5,-4.5S9.51,7.5 12,7.5s4.5,2.01 4.5,4.5 -2.01,4.5 -4.5,4.5zm0,-5.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_av_timer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_av_timer_24dp.xml
new file mode 100644
index 0000000..f954282
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_av_timer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,17c0,0.55 0.45,1 1,1s1,-0.45 1,-1 -0.45,-1 -1,-1 -1,0.45 -1,1zm0,-14v4h2V5.08c3.39,0.49 6,3.39 6,6.92 0,3.87 -3.13,7 -7,7s-7,-3.13 -7,-7c0,-1.68 0.59,-3.22 1.58,-4.42L12,13l1.41,-1.41 -6.8,-6.8v0.02C4.42,6.45 3,9.05 3,12c0,4.97 4.02,9 9,9 4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9h-1zm7,9c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1 0.45,1 1,1 1,-0.45 1,-1zM6,12c0,0.55 0.45,1 1,1s1,-0.45 1,-1 -0.45,-1 -1,-1 -1,0.45 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_closed_caption_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_closed_caption_24dp.xml
new file mode 100644
index 0000000..1cc3ea4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_closed_caption_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,4H5c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,7H9.5v-0.5h-2v3h2V13H11v1c0,0.55 -0.45,1 -1,1H7c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v1zm7,0h-1.5v-0.5h-2v3h2V13H18v1c0,0.55 -0.45,1 -1,1h-3c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_equalizer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_equalizer_24dp.xml
new file mode 100644
index 0000000..d94c27d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_equalizer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,20h4V4h-4v16zm-6,0h4v-8H4v8zM16,9v11h4V9h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_explicit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_explicit_24dp.xml
new file mode 100644
index 0000000..7dcc5af
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_explicit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,6h-4v2h4v2h-4v2h4v2H9V7h6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_forward_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_forward_24dp.xml
new file mode 100644
index 0000000..248556a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_forward_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,18l8.5,-6L4,6v12zm9,-12v12l8.5,-6L13,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_rewind_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_rewind_24dp.xml
new file mode 100644
index 0000000..bd09f10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_rewind_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,18V6l-8.5,6 8.5,6zm0.5,-6l8.5,6V6l-8.5,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_games_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_games_24dp.xml
new file mode 100644
index 0000000..8891427
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_games_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,7.5V2H9v5.5l3,3 3,-3zM7.5,9H2v6h5.5l3,-3 -3,-3zM9,16.5V22h6v-5.5l-3,-3 -3,3zM16.5,9l-3,3 3,3H22V9h-5.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_hearing_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_hearing_24dp.xml
new file mode 100644
index 0000000..f696b00
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_hearing_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_high_quality_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_high_quality_24dp.xml
new file mode 100644
index 0000000..31c633d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_high_quality_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,4H5c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7,-1c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5V15H14c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zm-3.5,-0.5h2v-3h-2v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_loop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_loop_24dp.xml
new file mode 100644
index 0000000..5ad1561
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_loop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,4V1L8,5l4,4V6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zm0,14c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_24dp.xml
new file mode 100644
index 0000000..e2e95d5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zm5.3,-3c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_none_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_none_24dp.xml
new file mode 100644
index 0000000..ce4d384
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_none_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zm-1.2,-9.1c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2l-0.01,6.2c0,0.66 -0.53,1.2 -1.19,1.2 -0.66,0 -1.2,-0.54 -1.2,-1.2V4.9zm6.5,6.1c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_off_24dp.xml
new file mode 100644
index 0000000..73bf970
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,11h-1.7c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zm-4.02,0.17c0,-0.06 0.02,-0.11 0.02,-0.17V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v0.18l5.98,5.99zM4.27,3L3,4.27l6.01,6.01V11c0,1.66 1.33,3 2.99,3 0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52 -2.76,0 -5.3,-2.1 -5.3,-5.1H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9L19.73,21 21,19.73 4.27,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_movie_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_movie_24dp.xml
new file mode 100644
index 0000000..6d4c52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_movie_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_add_24dp.xml
new file mode 100644
index 0000000..78eaca1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_books_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_books_24dp.xml
new file mode 100644
index 0000000..aa446b2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_books_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9H9V9h10v2zm-4,4H9v-2h6v2zm4,-8H9V5h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_music_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_music_24dp.xml
new file mode 100644
index 0000000..0465e98
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_music_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-2,5h-3v5.5c0,1.38 -1.12,2.5 -2.5,2.5S10,13.88 10,12.5s1.12,-2.5 2.5,-2.5c0.57,0 1.08,0.19 1.5,0.51V5h4v2zM4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_new_releases_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_new_releases_24dp.xml
new file mode 100644
index 0000000..8fbe033
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_new_releases_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M23,12l-2.44,-2.78 0.34,-3.68 -3.61,-0.82 -1.89,-3.18L12,3 8.6,1.54 6.71,4.72l-3.61,0.81 0.34,3.68L1,12l2.44,2.78 -0.34,3.69 3.61,0.82 1.89,3.18L12,21l3.4,1.46 1.89,-3.18 3.61,-0.82 -0.34,-3.68L23,12zm-10,5h-2v-2h2v2zm0,-4h-2V7h2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_not_interested_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_not_interested_24dp.xml
new file mode 100644
index 0000000..90eae2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_not_interested_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_24dp.xml
new file mode 100644
index 0000000..c843731
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,19h4V5H6v14zm8,-14v14h4V5h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_fill_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_fill_24dp.xml
new file mode 100644
index 0000000..a91240e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_fill_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-1,14H9V8h2v8zm4,0h-2V8h2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_outline_24dp.xml
new file mode 100644
index 0000000..f9232e0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16h2V8H9v8zm3,-14C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zm1,-4h2V8h-2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_arrow_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_arrow_24dp.xml
new file mode 100644
index 0000000..f9f849e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_arrow_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_fill_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_fill_24dp.xml
new file mode 100644
index 0000000..9c6d722
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_fill_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-2,14.5v-9l6,4.5 -6,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_outline_24dp.xml
new file mode 100644
index 0000000..65f6993
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_shopping_bag_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_shopping_bag_24dp.xml
new file mode 100644
index 0000000..97e4e62
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_shopping_bag_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,6V4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H2v13c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6h-6zm-6,-2h4v2h-4V4zM9,18V9l7.5,4L9,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_playlist_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_playlist_add_24dp.xml
new file mode 100644
index 0000000..a2ddbbb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_playlist_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,10H2v2h12v-2zm0,-4H2v2h12V6zm4,8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2,16h8v-2H2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_24dp.xml
new file mode 100644
index 0000000..78eaca1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_music_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_music_24dp.xml
new file mode 100644
index 0000000..7169362
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_music_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,6H3v2h12V6zm0,4H3v2h12v-2zM3,16h8v-2H3v2zM17,6v8.18c-0.31,-0.11 -0.65,-0.18 -1,-0.18 -1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3V8h3V6h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_radio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_radio_24dp.xml
new file mode 100644
index 0000000..ac67acc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_radio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.24,6.15C2.51,6.43 2,7.17 2,8v12c0,1.1 0.89,2 2,2h16c1.11,0 2,-0.9 2,-2V8c0,-1.11 -0.89,-2 -2,-2H8.3l8.26,-3.34L15.88,1 3.24,6.15zM7,20c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zm13,-8h-2v-2h-2v2H4V8h16v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_recent_actors_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_recent_actors_24dp.xml
new file mode 100644
index 0000000..3d93f4c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_recent_actors_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,5v14h2V5h-2zm-4,14h2V5h-2v14zM14,5H2c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1zM8,7.75c1.24,0 2.25,1.01 2.25,2.25S9.24,12.25 8,12.25 5.75,11.24 5.75,10 6.76,7.75 8,7.75zM12.5,17h-9v-0.75c0,-1.5 3,-2.25 4.5,-2.25s4.5,0.75 4.5,2.25V17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_24dp.xml
new file mode 100644
index 0000000..0362e27
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,7h10v3l4,-4 -4,-4v3H5v6h2V7zm10,10H7v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_one_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_one_24dp.xml
new file mode 100644
index 0000000..bf42eaf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_one_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,7h10v3l4,-4 -4,-4v3H5v6h2V7zm10,10H7v-3l-4,4 4,4v-3h12v-6h-2v4zm-4,-2V9h-1l-2,1v1h1.5v4H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_replay_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_replay_24dp.xml
new file mode 100644
index 0000000..176e43a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_replay_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_shuffle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_shuffle_24dp.xml
new file mode 100644
index 0000000..9940b5b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_shuffle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5V4h-5.5zm0.33,9.41l-1.41,1.41 3.13,3.13L14.5,20H20v-5.5l-2.04,2.04 -3.13,-3.13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_next_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_next_24dp.xml
new file mode 100644
index 0000000..6ccd524
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_next_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_previous_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_previous_24dp.xml
new file mode 100644
index 0000000..9b3d6f8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_previous_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,6h2v12H6zm3.5,6l8.5,6V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_snooze_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_snooze_24dp.xml
new file mode 100644
index 0000000..09fa95b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_snooze_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm-3,-9h3.63L9,15.2V17h6v-2h-3.63L15,10.8V9H9v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_stop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_stop_24dp.xml
new file mode 100644
index 0000000..0bd2ca4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_stop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,6h12v12H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_subtitles_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_subtitles_24dp.xml
new file mode 100644
index 0000000..45cdb1e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_subtitles_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM4,12h4v2H4v-2zm10,6H4v-2h10v2zm6,0h-4v-2h4v2zm0,-4H10v-2h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_surround_sound_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_surround_sound_24dp.xml
new file mode 100644
index 0000000..bb19563
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_surround_sound_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM7.76,16.24l-1.41,1.41C4.78,16.1 4,14.05 4,12c0,-2.05 0.78,-4.1 2.34,-5.66l1.41,1.41C6.59,8.93 6,10.46 6,12s0.59,3.07 1.76,4.24zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4zm5.66,1.66l-1.41,-1.41C17.41,15.07 18,13.54 18,12s-0.59,-3.07 -1.76,-4.24l1.41,-1.41C19.22,7.9 20,9.95 20,12c0,2.05 -0.78,4.1 -2.34,5.66zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_video_collection_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_video_collection_24dp.xml
new file mode 100644
index 0000000..681053d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_video_collection_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-8,12.5v-9l6,4.5 -6,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_24dp.xml
new file mode 100644
index 0000000..a51b841
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_off_24dp.xml
new file mode 100644
index 0000000..b6dc40c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,6.5l-4,4V7c0,-0.55 -0.45,-1 -1,-1H9.82L21,17.18V6.5zM3.27,2L2,3.27 4.73,6H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.21,0 0.39,-0.08 0.54,-0.18L19.73,21 21,19.73 3.27,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_down_24dp.xml
new file mode 100644
index 0000000..847964d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM5,9v6h4l5,5V4L9,9H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_mute_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_mute_24dp.xml
new file mode 100644
index 0000000..6b12444
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_mute_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,9v6h4l5,5V4l-5,5H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_off_24dp.xml
new file mode 100644
index 0000000..634a7c7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zm2.5,0c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9H3v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_up_24dp.xml
new file mode 100644
index 0000000..c8c4271
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,9v6h4l5,5V4L7,9H3zm13.5,3c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_web_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_web_24dp.xml
new file mode 100644
index 0000000..621df45
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_web_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-5,14H4v-4h11v4zm0,-5H4V9h11v4zm5,5h-4V9h4v9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_business_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_business_24dp.xml
new file mode 100644
index 0000000..62bc39f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_business_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,7V3H2v18h20V7H12zM6,19H4v-2h2v2zm0,-4H4v-2h2v2zm0,-4H4V9h2v2zm0,-4H4V5h2v2zm4,12H8v-2h2v2zm0,-4H8v-2h2v2zm0,-4H8V9h2v2zm0,-4H8V5h2v2zm10,12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2,-8h-2v2h2v-2zm0,4h-2v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_24dp.xml
new file mode 100644
index 0000000..3a3ba47
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_end_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_end_24dp.xml
new file mode 100644
index 0000000..0f3a0ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_end_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_made_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_made_24dp.xml
new file mode 100644
index 0000000..1048f49
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_made_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,5v2h6.59L4,18.59 5.41,20 17,8.41V15h2V5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_merge_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_merge_24dp.xml
new file mode 100644
index 0000000..4eee325
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_merge_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_missed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_missed_24dp.xml
new file mode 100644
index 0000000..a462e9f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_missed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.59,7L12,14.59 6.41,9H11V7H3v8h2v-4.59l7,7 9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_received_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_received_24dp.xml
new file mode 100644
index 0000000..ac65fd2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_received_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,5.41L18.59,4 7,15.59V9H5v10h10v-2H8.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_split_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_split_24dp.xml
new file mode 100644
index 0000000..c34b117
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_split_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,4l2.29,2.29 -2.88,2.88 1.42,1.42 2.88,-2.88L20,10V4zm-4,0H4v6l2.29,-2.29 4.71,4.7V20h2v-8.41l-5.29,-5.3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_chat_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_chat_24dp.xml
new file mode 100644
index 0000000..c28e3cf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_chat_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2H6V9zm8,5H6v-2h8v2zm4,-6H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_clear_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_clear_all_24dp.xml
new file mode 100644
index 0000000..c0829b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_clear_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,13h14v-2H5v2zm-2,4h14v-2H3v2zM7,7v2h14V7H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_comment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_comment_24dp.xml
new file mode 100644
index 0000000..e73d2c8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_comment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18zM18,14H6v-2h12v2zm0,-3H6V9h12v2zm0,-3H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_contacts_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_contacts_24dp.xml
new file mode 100644
index 0000000..8a2b3e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_contacts_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,0H4v2h16V0zM4,24h16v-2H4v2zM20,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,2.75c1.24,0 2.25,1.01 2.25,2.25s-1.01,2.25 -2.25,2.25S9.75,10.24 9.75,9 10.76,6.75 12,6.75zM17,17H7v-1.5c0,-1.67 3.33,-2.5 5,-2.5s5,0.83 5,2.5V17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialer_sip_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialer_sip_24dp.xml
new file mode 100644
index 0000000..787f93a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialer_sip_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3h-1v5h1V3zm-2,2h-2V4h2V3h-3v3h2v1h-2v1h3V5zm3,-2v5h1V6h2V3h-3zm2,2h-1V4h1v1zm0,10.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.01,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.27,-0.26 0.35,-0.65 0.24,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialpad_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialpad_24dp.xml
new file mode 100644
index 0000000..faa4d8b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialpad_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,19c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,1c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,-8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm-6,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,-6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,-6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_dnd_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dnd_on_24dp.xml
new file mode 100644
index 0000000..90eae2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dnd_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_email_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_email_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_email_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_forum_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_forum_24dp.xml
new file mode 100644
index 0000000..5e7d985
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_forum_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,6h-2v9H6v2c0,0.55 0.45,1 1,1h11l4,4V7c0,-0.55 -0.45,-1 -1,-1zm-4,6V3c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_import_export_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_import_export_24dp.xml
new file mode 100644
index 0000000..e0004d3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_import_export_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,3L5,6.99h3V14h2V6.99h3L9,3zm7,14.01V10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_off_24dp.xml
new file mode 100644
index 0000000..0052ad8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.65,20.87l-2.35,-2.35 -6.3,-6.29 -3.56,-3.57 -1.42,-1.41L4.27,4.5 3,5.77l2.78,2.78c-2.55,3.14 -2.36,7.76 0.56,10.69C7.9,20.8 9.95,21.58 12,21.58c1.79,0 3.57,-0.59 5.03,-1.78l2.7,2.7L21,21.23l-0.35,-0.36zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59c0,-1.32 0.43,-2.57 1.21,-3.6L12,14.77v4.82zM12,5.1v4.58l7.25,7.26c1.37,-2.96 0.84,-6.57 -1.6,-9.01L12,2.27l-3.7,3.7 1.41,1.41L12,5.1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_on_24dp.xml
new file mode 100644
index 0000000..8d98986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.66,7.93L12,2.27 6.34,7.93c-3.12,3.12 -3.12,8.19 0,11.31C7.9,20.8 9.95,21.58 12,21.58c2.05,0 4.1,-0.78 5.66,-2.34 3.12,-3.12 3.12,-8.19 0,-11.31zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_live_help_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_live_help_24dp.xml
new file mode 100644
index 0000000..de9b40a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_live_help_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,2H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,16h-2v-2h2v2zm2.07,-7.75l-0.9,0.92C13.45,11.9 13,12.5 13,14h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2H8c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_off_24dp.xml
new file mode 100644
index 0000000..09a700a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,6.5c1.38,0 2.5,1.12 2.5,2.5 0,0.74 -0.33,1.39 -0.83,1.85l3.63,3.63c0.98,-1.86 1.7,-3.8 1.7,-5.48 0,-3.87 -3.13,-7 -7,-7 -1.98,0 -3.76,0.83 -5.04,2.15l3.19,3.19c0.46,-0.52 1.11,-0.84 1.85,-0.84zm4.37,9.6l-4.63,-4.63 -0.11,-0.11L3.27,3 2,4.27l3.18,3.18C5.07,7.95 5,8.47 5,9c0,5.25 7,13 7,13s1.67,-1.85 3.38,-4.35L18.73,21 20,19.73l-3.63,-3.63z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_on_24dp.xml
new file mode 100644
index 0000000..04b8734
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_message_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_message_24dp.xml
new file mode 100644
index 0000000..afbdf64
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_message_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-2,12H6v-2h12v2zm0,-3H6V9h12v2zm0,-3H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_messenger_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_messenger_24dp.xml
new file mode 100644
index 0000000..5134282
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_messenger_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v18l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_no_sim_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_no_sim_24dp.xml
new file mode 100644
index 0000000..b36e505
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_no_sim_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.99,5c0,-1.1 -0.89,-2 -1.99,-2h-7L7.66,5.34 19,16.68 18.99,5zM3.65,3.88L2.38,5.15 5,7.77V19c0,1.1 0.9,2 2,2h10.01c0.35,0 0.67,-0.1 0.96,-0.26l1.88,1.88 1.27,-1.27L3.65,3.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_phone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_phone_24dp.xml
new file mode 100644
index 0000000..3a3ba47
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_phone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_portable_wifi_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_portable_wifi_off_24dp.xml
new file mode 100644
index 0000000..6bb84dd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_portable_wifi_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.56,14.24c0.28,-0.69 0.44,-1.45 0.44,-2.24 0,-3.31 -2.69,-6 -6,-6 -0.79,0 -1.55,0.16 -2.24,0.44l1.62,1.62c0.2,-0.03 0.41,-0.06 0.62,-0.06 2.21,0 4,1.79 4,4 0,0.21 -0.02,0.42 -0.05,0.63l1.61,1.61zM12,4c4.42,0 8,3.58 8,8 0,1.35 -0.35,2.62 -0.95,3.74l1.47,1.47C21.46,15.69 22,13.91 22,12c0,-5.52 -4.48,-10 -10,-10 -1.91,0 -3.69,0.55 -5.21,1.47l1.46,1.46C9.37,4.34 10.65,4 12,4zM3.27,2.5L2,3.77l2.1,2.1C2.79,7.57 2,9.69 2,12c0,3.7 2.01,6.92 4.99,8.65l1,-1.73C5.61,17.53 4,14.96 4,12c0,-1.76 0.57,-3.38 1.53,-4.69l1.43,1.44C6.36,9.68 6,10.8 6,12c0,2.22 1.21,4.15 3,5.19l1,-1.74c-1.19,-0.7 -2,-1.97 -2,-3.45 0,-0.65 0.17,-1.25 0.44,-1.79l1.58,1.58L10,12c0,1.1 0.9,2 2,2l0.21,-0.02 0.01,0.01 7.51,7.51L21,20.23 4.27,3.5l-1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_dialer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_dialer_24dp.xml
new file mode 100644
index 0000000..43baea0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_dialer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,3H2C0.9,3 0,3.9 0,5v14c0,1.1 0.9,2 2,2h20c1.1,0 1.99,-0.9 1.99,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM8,6c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H2v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1zm3.85,-4h1.64L21,16l-1.99,1.99c-1.31,-0.98 -2.28,-2.38 -2.73,-3.99 -0.18,-0.64 -0.28,-1.31 -0.28,-2s0.1,-1.36 0.28,-2c0.45,-1.62 1.42,-3.01 2.73,-3.99L21,8l-1.51,2h-1.64c-0.22,0.63 -0.35,1.3 -0.35,2s0.13,1.37 0.35,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_mail_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_mail_24dp.xml
new file mode 100644
index 0000000..fea2a54
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_mail_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,8V7l-3,2 -3,-2v1l3,2 3,-2zm1,-5H2C0.9,3 0,3.9 0,5v14c0,1.1 0.9,2 2,2h20c1.1,0 1.99,-0.9 1.99,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM8,6c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H2v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1zm8,-6h-8V6h8v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_ring_volume_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_ring_volume_24dp.xml
new file mode 100644
index 0000000..c76a7c2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_ring_volume_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M23.71,16.67C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71 0,0.28 0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73s3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.66,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71 0,-0.27 -0.11,-0.52 -0.29,-0.7zM21.16,6.26l-1.41,-1.41 -3.56,3.55 1.41,1.41s3.45,-3.52 3.56,-3.55zM13,2h-2v5h2V2zM6.4,9.81L7.81,8.4 4.26,4.84 2.84,6.26c0.11,0.03 3.56,3.55 3.56,3.55z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_landscape_24dp.xml
new file mode 100644
index 0000000..e765296
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1.01,7L1,17c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -1.99,0.9 -1.99,2zM19,7v10H5V7h14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_portrait_24dp.xml
new file mode 100644
index 0000000..5b22214
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_landscape_24dp.xml
new file mode 100644
index 0000000..e765296
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1.01,7L1,17c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -1.99,0.9 -1.99,2zM19,7v10H5V7h14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_portrait_24dp.xml
new file mode 100644
index 0000000..5b22214
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_swap_calls_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_swap_calls_24dp.xml
new file mode 100644
index 0000000..723a66d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_swap_calls_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,4l-4,4h3v7c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8v7H2l4,4 4,-4H7V8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v7c0,2.21 1.79,4 4,4s4,-1.79 4,-4V8h3l-4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_textsms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_textsms_24dp.xml
new file mode 100644
index 0000000..796a028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_textsms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM9,11H7V9h2v2zm4,0h-2V9h2v2zm4,0h-2V9h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_voicemail_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_voicemail_24dp.xml
new file mode 100644
index 0000000..92a8765
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_voicemail_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.5,6C15.46,6 13,8.46 13,11.5c0,1.33 0.47,2.55 1.26,3.5H9.74c0.79,-0.95 1.26,-2.17 1.26,-3.5C11,8.46 8.54,6 5.5,6S0,8.46 0,11.5 2.46,17 5.5,17h13c3.04,0 5.5,-2.46 5.5,-5.5S21.54,6 18.5,6zm-13,9C3.57,15 2,13.43 2,11.5S3.57,8 5.5,8 9,9.57 9,11.5 7.43,15 5.5,15zm13,0c-1.93,0 -3.5,-1.57 -3.5,-3.5S16.57,8 18.5,8 22,9.57 22,11.5 20.43,15 18.5,15z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_vpn_key_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_vpn_key_24dp.xml
new file mode 100644
index 0000000..fe9c93f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_vpn_key_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_24dp.xml
new file mode 100644
index 0000000..059be44
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_box_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_box_24dp.xml
new file mode 100644
index 0000000..df3fa3a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_box_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-2,10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_24dp.xml
new file mode 100644
index 0000000..f4d87c1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm5,11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_outline_24dp.xml
new file mode 100644
index 0000000..3eaea09
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1,-5C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_archive_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_archive_24dp.xml
new file mode 100644
index 0000000..5aa321c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_archive_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_backspace_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_backspace_24dp.xml
new file mode 100644
index 0000000..7bc3f93
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_backspace_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,3H7c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-3,12.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_block_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_block_24dp.xml
new file mode 100644
index 0000000..02bb4aa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_block_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zm8,8c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_clear_24dp.xml
new file mode 100644
index 0000000..88f04ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_content_copy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_copy_24dp.xml
new file mode 100644
index 0000000..baa0796
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_copy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,1H4c-1.1,0 -2,0.9 -2,2v14h2V3h12V1zm3,4H8c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm0,16H8V7h11v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_content_cut_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_cut_24dp.xml
new file mode 100644
index 0000000..82c28fa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_cut_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.64,7.64c0.23,-0.5 0.36,-1.05 0.36,-1.64 0,-2.21 -1.79,-4 -4,-4S2,3.79 2,6s1.79,4 4,4c0.59,0 1.14,-0.13 1.64,-0.36L10,12l-2.36,2.36C7.14,14.13 6.59,14 6,14c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4c0,-0.59 -0.13,-1.14 -0.36,-1.64L12,14l7,7h3v-1L9.64,7.64zM6,8c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zm0,12c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zm6,-7.5c-0.28,0 -0.5,-0.22 -0.5,-0.5s0.22,-0.5 0.5,-0.5 0.5,0.22 0.5,0.5 -0.22,0.5 -0.5,0.5zM19,3l-6,6 2,2 7,-7V3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_content_paste_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_paste_24dp.xml
new file mode 100644
index 0000000..cc248b5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_paste_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm7,18H5V4h2v3h10V4h2v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_create_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_create_24dp.xml
new file mode 100644
index 0000000..0ff336a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_create_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_drafts_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_drafts_24dp.xml
new file mode 100644
index 0000000..679321c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_drafts_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21.99,8c0,-0.72 -0.37,-1.35 -0.94,-1.7L12,1 2.95,6.3C2.38,6.65 2,7.28 2,8v10c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2l-0.01,-10zM12,13L3.74,7.84 12,3l8.26,4.84L12,13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_filter_list_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_filter_list_24dp.xml
new file mode 100644
index 0000000..16ca2a1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_filter_list_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,18h4v-2h-4v2zM3,6v2h18V6H3zm3,7h12v-2H6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_flag_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_flag_24dp.xml
new file mode 100644
index 0000000..581ba88
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_flag_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_forward_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_forward_24dp.xml
new file mode 100644
index 0000000..76d901e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_forward_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8V4l8,8 -8,8v-4H4V8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_gesture_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_gesture_24dp.xml
new file mode 100644
index 0000000..a8e4c17
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_gesture_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4.59,6.89c0.7,-0.71 1.4,-1.35 1.71,-1.22 0.5,0.2 0,1.03 -0.3,1.52 -0.25,0.42 -2.86,3.89 -2.86,6.31 0,1.28 0.48,2.34 1.34,2.98 0.75,0.56 1.74,0.73 2.64,0.46 1.07,-0.31 1.95,-1.4 3.06,-2.77 1.21,-1.49 2.83,-3.44 4.08,-3.44 1.63,0 1.65,1.01 1.76,1.79 -3.78,0.64 -5.38,3.67 -5.38,5.37 0,1.7 1.44,3.09 3.21,3.09 1.63,0 4.29,-1.33 4.69,-6.1H21v-2.5h-2.47c-0.15,-1.65 -1.09,-4.2 -4.03,-4.2 -2.25,0 -4.18,1.91 -4.94,2.84 -0.58,0.73 -2.06,2.48 -2.29,2.72 -0.25,0.3 -0.68,0.84 -1.11,0.84 -0.45,0 -0.72,-0.83 -0.36,-1.92 0.35,-1.09 1.4,-2.86 1.85,-3.52 0.78,-1.14 1.3,-1.92 1.3,-3.28C8.95,3.69 7.31,3 6.44,3 5.12,3 3.97,4 3.72,4.25c-0.36,0.36 -0.66,0.66 -0.88,0.93l1.75,1.71zm9.29,11.66c-0.31,0 -0.74,-0.26 -0.74,-0.72 0,-0.6 0.73,-2.2 2.87,-2.76 -0.3,2.69 -1.43,3.48 -2.13,3.48z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_inbox_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_inbox_24dp.xml
new file mode 100644
index 0000000..e2b49cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_inbox_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H4.99c-1.1,0 -1.98,0.9 -1.98,2L3,19c0,1.1 0.89,2 1.99,2H19c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,12h-4c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3H4.99V5H19v10zm-3,-5h-2V7h-4v3H8l4,4 4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_link_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_link_24dp.xml
new file mode 100644
index 0000000..1b8f436
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_link_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4V7H7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9H7c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2H8v2zm9,-6h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4V17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_mail_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_mail_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_mail_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_markunread_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_markunread_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_markunread_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_redo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_redo_24dp.xml
new file mode 100644
index 0000000..1beacbb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_redo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_24dp.xml
new file mode 100644
index 0000000..8757791
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,13H5v-2h14v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_24dp.xml
new file mode 100644
index 0000000..98ef63c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm5,11H7v-2h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_outline_24dp.xml
new file mode 100644
index 0000000..42d36f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,11v2h10v-2H7zm5,-9C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_24dp.xml
new file mode 100644
index 0000000..92ec9e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_all_24dp.xml
new file mode 100644
index 0000000..e351767
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,8V5l-7,7 7,7v-3l-4,-4 4,-4zm6,1V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_report_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_report_24dp.xml
new file mode 100644
index 0000000..09d5660
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_report_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.73,3H8.27L3,8.27v7.46L8.27,21h7.46L21,15.73V8.27L15.73,3zM12,17.3c-0.72,0 -1.3,-0.58 -1.3,-1.3 0,-0.72 0.58,-1.3 1.3,-1.3 0.72,0 1.3,0.58 1.3,1.3 0,0.72 -0.58,1.3 -1.3,1.3zm1,-4.3h-2V7h2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_save_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_save_24dp.xml
new file mode 100644
index 0000000..d36b668
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_save_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V7l-4,-4zm-5,16c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zm3,-10H5V5h10v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_select_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_select_all_24dp.xml
new file mode 100644
index 0000000..2dde2d4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_select_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5h2V3c-1.1,0 -2,0.9 -2,2zm0,8h2v-2H3v2zm4,8h2v-2H7v2zM3,9h2V7H3v2zm10,-6h-2v2h2V3zm6,0v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2H3c0,1.1 0.9,2 2,2zm-2,-4h2v-2H3v2zM9,3H7v2h2V3zm2,18h2v-2h-2v2zm8,-8h2v-2h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2zm0,-12h2V7h-2v2zm0,8h2v-2h-2v2zm-4,4h2v-2h-2v2zm0,-16h2V3h-2v2zM7,17h10V7H7v10zm2,-8h6v6H9V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_send_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_send_24dp.xml
new file mode 100644
index 0000000..f9dc3a1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_send_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_sort_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_sort_24dp.xml
new file mode 100644
index 0000000..1605498
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_sort_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,18h6v-2H3v2zM3,6v2h18V6H3zm0,7h12v-2H3v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_text_format_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_text_format_24dp.xml
new file mode 100644
index 0000000..09b3888
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_text_format_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,17v2h14v-2H5zm4.5,-4.2h5l0.9,2.2h2.1L12.75,4h-1.5L6.5,15h2.1l0.9,-2.2zM12,5.98L13.87,11h-3.74L12,5.98z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_undo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_undo_24dp.xml
new file mode 100644
index 0000000..36bbbcf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_undo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarm_24dp.xml
new file mode 100644
index 0000000..7918b03
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8H11v6l4.75,2.85 0.75,-1.23 -4,-2.37V8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarms_24dp.xml
new file mode 100644
index 0000000..479b5c1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,5.7l-4.6,-3.9 -1.3,1.5 4.6,3.9L22,5.7zM7.9,3.4L6.6,1.9 2,5.7l1.3,1.5 4.6,-3.8zM12.5,8H11v6l4.7,2.9 0.8,-1.2 -4,-2.4V8zM12,4c-5,0 -9,4 -9,9s4,9 9,9 9,-4 9,-9 -4,-9 -9,-9zm0,16c-3.9,0 -7,-3.1 -7,-7s3.1,-7 7,-7 7,3.1 7,7 -3.1,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_access_time_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_time_24dp.xml
new file mode 100644
index 0000000..7bf67c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_time_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"
+        android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_add_alarm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_add_alarm_24dp.xml
new file mode 100644
index 0000000..5e0d986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_add_alarm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm1,-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_off_24dp.xml
new file mode 100644
index 0000000..0dfb747
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,9V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v3.68l7.83,7.83L21,16v-2l-8,-5zM3,5.27l4.99,4.99L2,14v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-3.73L18.73,21 20,19.73 4.27,4 3,5.27z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_on_24dp.xml
new file mode 100644
index 0000000..3aa5af7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_on_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.18,9"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_20_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_20_24dp.xml
new file mode 100644
index 0000000..81ebec3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_20_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,17v3.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V17H7z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V17h10V5.33z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_30_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_30_24dp.xml
new file mode 100644
index 0000000..46f602b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_30_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V15h10V5.33z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,15v5.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V15H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_50_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_50_24dp.xml
new file mode 100644
index 0000000..b4390e3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_50_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V13h10V5.33z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,13v7.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V13H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_60_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_60_24dp.xml
new file mode 100644
index 0000000..e23a724
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_60_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V11h10V5.33z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,11v9.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V11H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_80_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_80_24dp.xml
new file mode 100644
index 0000000..246e394
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_80_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h10V5.33z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,9v11.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V9H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_90_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_90_24dp.xml
new file mode 100644
index 0000000..05efad9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_90_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V8h10V5.33z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,8v12.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V8H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_alert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_alert_24dp.xml
new file mode 100644
index 0000000..aeca8fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_alert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM13,18h-2v-2h2v2zm0,-4h-2V9h2v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_20_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_20_24dp.xml
new file mode 100644
index 0000000..9018f25
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_20_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,20v-3H7v3.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V17h-4.4L11,20z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V17h4v-2.5H9L13,7v5.5h2L12.6,17H17V5.33C17,4.6 16.4,4 15.67,4z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_30_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_30_24dp.xml
new file mode 100644
index 0000000..40753fc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_30_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v9.17h2L13,7v5.5h2l-1.07,2H17V5.33C17,4.6 16.4,4 15.67,4z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,20v-5.5H7v6.17C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V14.5h-3.07L11,20z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_50_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_50_24dp.xml
new file mode 100644
index 0000000..ecd86f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_50_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.47,13.5L11,20v-5.5H9l0.53,-1H7v7.17C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V13.5h-2.53z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v8.17h2.53L13,7v5.5h2l-0.53,1H17V5.33C17,4.6 16.4,4 15.67,4z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_60_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_60_24dp.xml
new file mode 100644
index 0000000..1893c50
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_60_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V11h3.87L13,7v4h4V5.33C17,4.6 16.4,4 15.67,4z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,12.5h2L11,20v-5.5H9l1.87,-3.5H7v9.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V11h-4v1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_80_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_80_24dp.xml
new file mode 100644
index 0000000..8e1e8b5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_80_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h4.93L13,7v2h4V5.33C17,4.6 16.4,4 15.67,4z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,12.5h2L11,20v-5.5H9L11.93,9H7v11.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V9h-4v3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_90_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_90_24dp.xml
new file mode 100644
index 0000000..105f0ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_90_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V8h5.47L13,7v1h4V5.33C17,4.6 16.4,4 15.67,4z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,12.5h2L11,20v-5.5H9L12.47,8H7v12.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V8h-4v4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_full_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_full_24dp.xml
new file mode 100644
index 0000000..e2ca7a6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_full_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM11,20v-5.5H9L13,7v5.5h2L11,20z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_full_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_full_24dp.xml
new file mode 100644
index 0000000..92f9db6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_full_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_std_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_std_24dp.xml
new file mode 100644
index 0000000..92f9db6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_std_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_unknown_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_unknown_24dp.xml
new file mode 100644
index 0000000..4b48da8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_unknown_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zm-2.72,13.95h-1.9v-1.9h1.9v1.9zm1.35,-5.26s-0.38,0.42 -0.67,0.71c-0.48,0.48 -0.83,1.15 -0.83,1.6h-1.6c0,-0.83 0.46,-1.52 0.93,-2l0.93,-0.94c0.27,-0.27 0.44,-0.65 0.44,-1.06 0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5H9c0,-1.66 1.34,-3 3,-3s3,1.34 3,3c0,0.66 -0.27,1.26 -0.7,1.69z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_24dp.xml
new file mode 100644
index 0000000..6ce13d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59V5.83zm1.88,10.46L13,18.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_connected_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_connected_24dp.xml
new file mode 100644
index 0000000..27a3001
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_connected_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,12l-2,-2 -2,2 2,2 2,-2zm10.71,-4.29L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59V5.83zm1.88,10.46L13,18.17v-3.76l1.88,1.88zM19,10l-2,2 2,2 2,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_disabled_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_disabled_24dp.xml
new file mode 100644
index 0000000..02d7bc8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_disabled_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,5.83l1.88,1.88 -1.6,1.6 1.41,1.41 3.02,-3.02L12,2h-1v5.03l2,2v-3.2zM5.41,4L4,5.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l4.29,-4.29 2.3,2.29L20,18.59 5.41,4zM13,18.17v-3.76l1.88,1.88L13,18.17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_searching_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_searching_24dp.xml
new file mode 100644
index 0000000..d09fb6f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_searching_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zm5.29,-5.3l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zm-3.82,1L10,2H9v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59V5.83zm1.88,10.46L11,18.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_auto_24dp.xml
new file mode 100644
index 0000000..a7495b9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.85,12.65h2.3L12,9l-1.15,3.65zM20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM14.3,16l-0.7,-2h-3.2l-0.7,2H7.8L11,7h2l3.2,9h-1.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_high_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_high_24dp.xml
new file mode 100644
index 0000000..c254041
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_high_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zm0,-10c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_low_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_low_24dp.xml
new file mode 100644
index 0000000..406a52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_low_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_medium_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_medium_24dp.xml
new file mode 100644
index 0000000..cc63bac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_medium_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_data_usage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_data_usage_24dp.xml
new file mode 100644
index 0000000..69bc3ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_data_usage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92V2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_developer_mode_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_developer_mode_24dp.xml
new file mode 100644
index 0000000..93288cb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_developer_mode_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,5h10v2h2V3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v4h2V5zm8.41,11.59L20,12l-4.59,-4.59L14,8.83 17.17,12 14,15.17l1.41,1.42zM10,15.17L6.83,12 10,8.83 8.59,7.41 4,12l4.59,4.59L10,15.17zM17,19H7v-2H5v4c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_devices_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_devices_24dp.xml
new file mode 100644
index 0000000..e54c722
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_devices_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6h18V4H4c-1.1,0 -2,0.9 -2,2v11H0v3h14v-3H4V6zm19,2h-6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1zm-1,9h-4v-7h4v7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_dvr_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_dvr_24dp.xml
new file mode 100644
index 0000000..61a2bc6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_dvr_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zm0,14H3V5h18v12zm-2,-9H8v2h11V8zm0,4H8v2h11v-2zM7,8H5v2h2V8zm0,4H5v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_fixed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_fixed_24dp.xml
new file mode 100644
index 0000000..2a7817e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_fixed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm8.94,3c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_not_fixed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_not_fixed_24dp.xml
new file mode 100644
index 0000000..d8fb3b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_not_fixed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_off_24dp.xml
new file mode 100644
index 0000000..68db2a9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15H23v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zm13.27,13.27C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_location_disabled_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_disabled_24dp.xml
new file mode 100644
index 0000000..68db2a9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_disabled_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15H23v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zm13.27,13.27C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_location_searching_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_searching_24dp.xml
new file mode 100644
index 0000000..d8fb3b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_searching_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_multitrack_audio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_multitrack_audio_24dp.xml
new file mode 100644
index 0000000..252e560
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_multitrack_audio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,18h2V6H7v12zm4,4h2V2h-2v20zm-8,-8h2v-4H3v4zm12,4h2V6h-2v12zm4,-8v4h2v-4h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_network_cell_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_cell_24dp.xml
new file mode 100644
index 0000000..bc6f3ca
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_cell_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22h20V2z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,7L2,22h15z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_network_wifi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_wifi_24dp.xml
new file mode 100644
index 0000000..3a1e2e5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_wifi_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_nfc_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_nfc_24dp.xml
new file mode 100644
index 0000000..83c3ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_nfc_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm0,18H4V4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72V8h3v8H8V8h2V6H6v12h12V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_now_wallpaper_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_wallpaper_24dp.xml
new file mode 100644
index 0000000..6e1973e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_wallpaper_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,4h7V2H4c-1.1,0 -2,0.9 -2,2v7h2V4zm6,9l-4,5h12l-3,-4 -2.03,2.71L10,13zm7,-4.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S14,7.67 14,8.5s0.67,1.5 1.5,1.5S17,9.33 17,8.5zM20,2h-7v2h7v7h2V4c0,-1.1 -0.9,-2 -2,-2zm0,18h-7v2h7c1.1,0 2,-0.9 2,-2v-7h-2v7zM4,13H2v7c0,1.1 0.9,2 2,2h7v-2H4v-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_now_widgets_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_widgets_24dp.xml
new file mode 100644
index 0000000..02996e0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_widgets_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,13v8h8v-8h-8zM3,21h8v-8H3v8zM3,3v8h8V3H3zm13.66,-1.31L11,7.34 16.66,13l5.66,-5.66 -5.66,-5.65z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_landscape_24dp.xml
new file mode 100644
index 0000000..1e0f44a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,5H3c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm-2,12H5V7h14v10zm-9,-1h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zm0.8,-6c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_portrait_24dp.xml
new file mode 100644
index 0000000..d35bc48
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zm0.8,-6c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1H7c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,18H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_rotation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_rotation_24dp.xml
new file mode 100644
index 0000000..2e364cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_rotation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M23.25,12.77l-2.57,-2.57 -1.41,1.41 2.22,2.22 -5.66,5.66L4.51,8.17l5.66,-5.66 2.1,2.1 1.41,-1.41L11.23,0.75c-0.59,-0.59 -1.54,-0.59 -2.12,0L2.75,7.11c-0.59,0.59 -0.59,1.54 0,2.12l12.02,12.02c0.59,0.59 1.54,0.59 2.12,0l6.36,-6.36c0.59,-0.59 0.59,-1.54 0,-2.12zM8.47,20.48C5.2,18.94 2.86,15.76 2.5,12H1c0.51,6.16 5.66,11 11.95,11l0.66,-0.03 -3.81,-3.82 -1.33,1.33zM16,9h5c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1v-0.5C21,1.12 19.88,0 18.5,0S16,1.12 16,2.5V3c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1zm0.8,-6.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7V3h-3.4v-0.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_rotation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_rotation_24dp.xml
new file mode 100644
index 0000000..ab4d9ce
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_rotation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.48,2.52c3.27,1.55 5.61,4.72 5.97,8.48h1.5C23.44,4.84 18.29,0 12,0l-0.66,0.03 3.81,3.81 1.33,-1.32zm-6.25,-0.77c-0.59,-0.59 -1.54,-0.59 -2.12,0L1.75,8.11c-0.59,0.59 -0.59,1.54 0,2.12l12.02,12.02c0.59,0.59 1.54,0.59 2.12,0l6.36,-6.36c0.59,-0.59 0.59,-1.54 0,-2.12L10.23,1.75zm4.6,19.44L2.81,9.17l6.36,-6.36 12.02,12.02 -6.36,6.36zm-7.31,0.29C4.25,19.94 1.91,16.76 1.55,13H0.05C0.56,19.16 5.71,24 12,24l0.66,-0.03 -3.81,-3.81 -1.33,1.32z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_sd_storage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_sd_storage_24dp.xml
new file mode 100644
index 0000000..74e0e41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_sd_storage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,6h-2V4h2v4zm3,0h-2V4h2v4zm3,0h-2V4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_settings_system_daydream_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_settings_system_daydream_24dp.xml
new file mode 100644
index 0000000..0882c81
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_settings_system_daydream_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16h6.5c1.38,0 2.5,-1.12 2.5,-2.5S16.88,11 15.5,11h-0.05c-0.24,-1.69 -1.69,-3 -3.45,-3 -1.4,0 -2.6,0.83 -3.16,2.02h-0.16C7.17,10.18 6,11.45 6,13c0,1.66 1.34,3 3,3zM21,3H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_0_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_0_bar_24dp.xml
new file mode 100644
index 0000000..b385c9b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_0_bar_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22h20V2z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_1_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_1_bar_24dp.xml
new file mode 100644
index 0000000..4fe8b5a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_1_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22h20V2z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12L2,22h10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_2_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_2_bar_24dp.xml
new file mode 100644
index 0000000..6fb8da3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_2_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22h20V2z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,10L2,22h12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_3_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_3_bar_24dp.xml
new file mode 100644
index 0000000..bc6f3ca
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_3_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22h20V2z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,7L2,22h15z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_4_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_4_bar_24dp.xml
new file mode 100644
index 0000000..13e38ee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_4_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22h20V2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_0_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_0_bar_24dp.xml
new file mode 100644
index 0000000..9f74cf5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_0_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,8V2L2,22h16V8z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_1_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_1_bar_24dp.xml
new file mode 100644
index 0000000..877cad6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_1_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,8V2L2,22h16V8z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,10v8h2v-8h-2zm-8,12V12L2,22h10zm8,0h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_2_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_2_bar_24dp.xml
new file mode 100644
index 0000000..3d15914
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_2_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,8V2L2,22h16V8z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,22V10L2,22h12zm6,-12v8h2v-8h-2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_3_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_3_bar_24dp.xml
new file mode 100644
index 0000000..1fc9c2b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_3_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,8V2L2,22h16V8z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,22V7L2,22h15zm3,-12v8h2v-8h-2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_4_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_4_bar_24dp.xml
new file mode 100644
index 0000000..e836247
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_4_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,18h2v-8h-2v8zm0,4h2v-2h-2v2zM2,22h16V8h4V2L2,22z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_no_sim_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_no_sim_24dp.xml
new file mode 100644
index 0000000..b36e505
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_no_sim_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.99,5c0,-1.1 -0.89,-2 -1.99,-2h-7L7.66,5.34 19,16.68 18.99,5zM3.65,3.88L2.38,5.15 5,7.77V19c0,1.1 0.9,2 2,2h10.01c0.35,0 0.67,-0.1 0.96,-0.26l1.88,1.88 1.27,-1.27L3.65,3.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_null_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_null_24dp.xml
new file mode 100644
index 0000000..f152649
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_null_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6.83V20H6.83L20,6.83M22,2L2,22h20V2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_off_24dp.xml
new file mode 100644
index 0000000..343e9b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,1l-8.59,8.59L21,18.18V1zM4.77,4.5L3.5,5.77l6.36,6.36L1,21h17.73l2,2L22,21.73 4.77,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_0_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_0_bar_24dp.xml
new file mode 100644
index 0000000..5e45e6d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_0_bar_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_1_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_1_bar_24dp.xml
new file mode 100644
index 0000000..2fcb74a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_1_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.67,14.86L12,21.49v0.01l0.01,-0.01 5.33,-6.63C17.06,14.65 15.03,13 12,13s-5.06,1.65 -5.33,1.86z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_2_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_2_bar_24dp.xml
new file mode 100644
index 0000000..ac24574
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_2_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4.79,12.52l7.2,8.98H12l0.01,-0.01 7.2,-8.98C18.85,12.24 16.1,10 12,10s-6.85,2.24 -7.21,2.52z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_3_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_3_bar_24dp.xml
new file mode 100644
index 0000000..3a1e2e5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_3_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_4_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_4_bar_24dp.xml
new file mode 100644
index 0000000..f47a229
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_4_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_off_24dp.xml
new file mode 100644
index 0000000..d9eaa7a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4 -1.5,0 -2.89,0.19 -4.15,0.48L18.18,13.8 23.64,7zm-6.6,8.22L3.27,1.44 2,2.72l2.05,2.06C1.91,5.76 0.59,6.82 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01 3.9,-4.86 3.32,3.32 1.27,-1.27 -3.46,-3.46z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_1_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_1_bar_26x24px.xml
new file mode 100644
index 0000000..830fb67
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_1_bar_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,21.99l5.66,-7.05C18.44,14.78 16.27,13 13,13s-5.44,1.78 -5.66,1.95L13,21.99z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_2_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_2_bar_26x24px.xml
new file mode 100644
index 0000000..57aab89
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_2_bar_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.01,21.99l7.54,-9.4C20.26,12.38 17.36,10 13,10c-4.36,0 -7.26,2.38 -7.55,2.59l7.54,9.4h0.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_3_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_3_bar_26x24px.xml
new file mode 100644
index 0000000..c0b7b4a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_3_bar_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.01,21.99l9.43,-11.75C22.07,9.97 18.44,7 13,7c-5.44,0 -9.07,2.97 -9.44,3.24l9.43,11.75h0.02z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_4_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_4_bar_26x24px.xml
new file mode 100644
index 0000000..6cc9ebe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_4_bar_26x24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_1_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_1_26x24px.xml
new file mode 100644
index 0000000..70bd006
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_1_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.34,14.95L13,21.99V22v-0.01l5.66,-7.05C18.44,14.78 16.27,13 13,13s-5.44,1.78 -5.66,1.95zM22,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_26x24px.xml
new file mode 100644
index 0000000..910bf9d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_2_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_2_26x24px.xml
new file mode 100644
index 0000000..e158ddf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_2_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5.45,12.59l7.54,9.4 0.01,0.01 0.01,-0.01L20,13.28v-1.09c-1.07,-0.73 -3.59,-2.19 -7,-2.19 -4.36,0 -7.26,2.38 -7.55,2.59zM22,10v8h2v-8h-2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_3_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_3_26x24px.xml
new file mode 100644
index 0000000..c242b36
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_3_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,13.28V8.71C18.35,7.87 15.94,7 13,7c-5.44,0 -9.07,2.97 -9.44,3.24l9.43,11.75 0.01,0.01 0.01,-0.01L20,13.28zM22,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_4_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_4_26x24px.xml
new file mode 100644
index 0000000..d5f15af
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_4_26x24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,22h2v-2h-2v2zM13,2C5.74,2 0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24l1.35,-1.68C25.1,5.96 20.26,2 13,2zm9,16h2v-8h-2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_not_connected_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_not_connected_26x24px.xml
new file mode 100644
index 0000000..a654e58
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_not_connected_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,8.5c0.85,0 1.64,0.23 2.34,0.62l2.24,-2.79C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01 4.21,-5.24c-0.76,-0.87 -1.22,-2 -1.22,-3.25 0,-2.76 2.24,-5 5,-5z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,10c-1.93,0 -3.5,1.57 -3.5,3.5h1.75c0,-0.97 0.78,-1.75 1.75,-1.75s1.75,0.78 1.75,1.75c0,0.48 -0.2,0.92 -0.51,1.24l-1.09,1.1c-0.63,0.63 -1.02,1.51 -1.02,2.47v0.44h1.75c0,-1.31 0.39,-1.84 1.03,-2.47l0.78,-0.8c0.5,-0.5 0.82,-1.2 0.82,-1.97C24.5,11.57 22.93,10 21,10zm-0.95,11.95h1.9v-1.9h-1.9v1.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_null_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_null_26x24px.xml
new file mode 100644
index 0000000..9951921
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_null_26x24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="26dp"
+        android:height="24dp"
+        android:viewportWidth="26.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,4c4.25,0 7.62,1.51 9.68,2.75L13,18.8 3.33,6.75C5.38,5.51 8.75,4 13,4m0,-2C5.74,2 0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L25.58,6.32C25.1,5.96 20.26,2 13,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_storage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_storage_24dp.xml
new file mode 100644
index 0000000..8d18d84
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_storage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,20h20v-4H2v4zm2,-3h2v2H4v-2zM2,4v4h20V4H2zm4,3H4V5h2v2zm-4,7h20v-4H2v4zm2,-3h2v2H4v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_usb_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_usb_24dp.xml
new file mode 100644
index 0000000..ae726d8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_usb_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,7v4h1v2h-3V5h2l-3,-4 -3,4h2v8H8v-2.07c0.7,-0.37 1.2,-1.08 1.2,-1.93 0,-1.21 -0.99,-2.2 -2.2,-2.2 -1.21,0 -2.2,0.99 -2.2,2.2 0,0.85 0.5,1.56 1.2,1.93V13c0,1.11 0.89,2 2,2h3v3.05c-0.71,0.37 -1.2,1.1 -1.2,1.95 0,1.22 0.99,2.2 2.2,2.2 1.21,0 2.2,-0.98 2.2,-2.2 0,-0.85 -0.49,-1.58 -1.2,-1.95V15h3c1.11,0 2,-0.89 2,-2v-2h1V7h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_lock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_lock_24dp.xml
new file mode 100644
index 0000000..ca9ce19
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_lock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.5,9.5c0.28,0 0.55,0.04 0.81,0.08L24,6c-3.34,-2.51 -7.5,-4 -12,-4S3.34,3.49 0,6l12,16 3.5,-4.67V14.5c0,-2.76 2.24,-5 5,-5zM23,16v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S18,13.12 18,14.5V16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zm-1,0h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5V16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_tethering_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_tethering_24dp.xml
new file mode 100644
index 0000000..7fc86a5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_tethering_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,11c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm6,2c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6c0,2.22 1.21,4.15 3,5.19l1,-1.74c-1.19,-0.7 -2,-1.97 -2,-3.45 0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,1.48 -0.81,2.75 -2,3.45l1,1.74c1.79,-1.04 3,-2.97 3,-5.19zM12,3C6.48,3 2,7.48 2,13c0,3.7 2.01,6.92 4.99,8.65l1,-1.73C5.61,18.53 4,15.96 4,13c0,-4.42 3.58,-8 8,-8s8,3.58 8,8c0,2.96 -1.61,5.53 -4,6.92l1,1.73c2.99,-1.73 5,-4.95 5,-8.65 0,-5.52 -4.48,-10 -10,-10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_file_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_file_24dp.xml
new file mode 100644
index 0000000..ed1aad3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_file_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_money_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_money_24dp.xml
new file mode 100644
index 0000000..4a0cd0e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_money_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_all_24dp.xml
new file mode 100644
index 0000000..7a5ee14
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,3v18h18V3H3zm8,16H5v-6h6v6zm0,-8H5V5h6v6zm8,8h-6v-6h6v6zm0,-8h-6V5h6v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_bottom_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_bottom_24dp.xml
new file mode 100644
index 0000000..4538725
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_bottom_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,11H7v2h2v-2zm4,4h-2v2h2v-2zM9,3H7v2h2V3zm4,8h-2v2h2v-2zM5,3H3v2h2V3zm8,4h-2v2h2V7zm4,4h-2v2h2v-2zm-4,-8h-2v2h2V3zm4,0h-2v2h2V3zm2,10h2v-2h-2v2zm0,4h2v-2h-2v2zM5,7H3v2h2V7zm14,-4v2h2V3h-2zm0,6h2V7h-2v2zM5,11H3v2h2v-2zM3,21h18v-2H3v2zm2,-6H3v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_clear_24dp.xml
new file mode 100644
index 0000000..0d9c07f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,5h2V3H7v2zm0,8h2v-2H7v2zm0,8h2v-2H7v2zm4,-4h2v-2h-2v2zm0,4h2v-2h-2v2zm-8,0h2v-2H3v2zm0,-4h2v-2H3v2zm0,-4h2v-2H3v2zm0,-4h2V7H3v2zm0,-4h2V3H3v2zm8,8h2v-2h-2v2zm8,4h2v-2h-2v2zm0,-4h2v-2h-2v2zm0,8h2v-2h-2v2zm0,-12h2V7h-2v2zm-8,0h2V7h-2v2zm8,-6v2h2V3h-2zm-8,2h2V3h-2v2zm4,16h2v-2h-2v2zm0,-8h2v-2h-2v2zm0,-8h2V3h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_color_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_color_24dp.xml
new file mode 100644
index 0000000..c164764
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_color_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.75,7L14,3.25l-10,10V17h3.75l10,-10zm2.96,-2.96c0.39,-0.39 0.39,-1.02 0,-1.41L18.37,0.29c-0.39,-0.39 -1.02,-0.39 -1.41,0L15,2.25 18.75,6l1.96,-1.96z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0,20h24v4H0z"
+        android:fillAlpha=".36"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_horizontal_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_horizontal_24dp.xml
new file mode 100644
index 0000000..caedc37
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_horizontal_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,21h2v-2H3v2zM5,7H3v2h2V7zM3,17h2v-2H3v2zm4,4h2v-2H7v2zM5,3H3v2h2V3zm4,0H7v2h2V3zm8,0h-2v2h2V3zm-4,4h-2v2h2V7zm0,-4h-2v2h2V3zm6,14h2v-2h-2v2zm-8,4h2v-2h-2v2zm-8,-8h18v-2H3v2zM19,3v2h2V3h-2zm0,6h2V7h-2v2zm-8,8h2v-2h-2v2zm4,4h2v-2h-2v2zm4,0h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_inner_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_inner_24dp.xml
new file mode 100644
index 0000000..da4372a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_inner_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,21h2v-2H3v2zm4,0h2v-2H7v2zM5,7H3v2h2V7zM3,17h2v-2H3v2zM9,3H7v2h2V3zM5,3H3v2h2V3zm12,0h-2v2h2V3zm2,6h2V7h-2v2zm0,-6v2h2V3h-2zm-4,18h2v-2h-2v2zM13,3h-2v8H3v2h8v8h2v-8h8v-2h-8V3zm6,18h2v-2h-2v2zm0,-4h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_left_24dp.xml
new file mode 100644
index 0000000..6d2e070
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,21h2v-2h-2v2zm0,-4h2v-2h-2v2zm0,-12h2V3h-2v2zm0,4h2V7h-2v2zm0,4h2v-2h-2v2zm-4,8h2v-2H7v2zM7,5h2V3H7v2zm0,8h2v-2H7v2zm-4,8h2V3H3v18zM19,9h2V7h-2v2zm-4,12h2v-2h-2v2zm4,-4h2v-2h-2v2zm0,-14v2h2V3h-2zm0,10h2v-2h-2v2zm0,8h2v-2h-2v2zm-4,-8h2v-2h-2v2zm0,-8h2V3h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_outer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_outer_24dp.xml
new file mode 100644
index 0000000..5addeec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_outer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,7h-2v2h2V7zm0,4h-2v2h2v-2zm4,0h-2v2h2v-2zM3,3v18h18V3H3zm16,16H5V5h14v14zm-6,-4h-2v2h2v-2zm-4,-4H7v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_right_24dp.xml
new file mode 100644
index 0000000..4a9b068
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,21h2v-2H7v2zM3,5h2V3H3v2zm4,0h2V3H7v2zm0,8h2v-2H7v2zm-4,8h2v-2H3v2zm8,0h2v-2h-2v2zm-8,-8h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2V7H3v2zm8,8h2v-2h-2v2zm4,-4h2v-2h-2v2zm4,-10v18h2V3h-2zm-4,18h2v-2h-2v2zm0,-16h2V3h-2v2zm-4,8h2v-2h-2v2zm0,-8h2V3h-2v2zm0,4h2V7h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_style_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_style_24dp.xml
new file mode 100644
index 0000000..5079af0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_style_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,21h2v-2h-2v2zm4,0h2v-2h-2v2zM7,21h2v-2H7v2zm4,0h2v-2h-2v2zm8,-4h2v-2h-2v2zm0,-4h2v-2h-2v2zM3,3v18h2V5h16V3H3zm16,6h2V7h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_top_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_top_24dp.xml
new file mode 100644
index 0000000..f38ac53
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_top_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,21h2v-2H7v2zm0,-8h2v-2H7v2zm4,0h2v-2h-2v2zm0,8h2v-2h-2v2zm-8,-4h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2v-2H3v2zm0,-4h2V7H3v2zm8,8h2v-2h-2v2zm8,-8h2V7h-2v2zm0,4h2v-2h-2v2zM3,3v2h18V3H3zm16,14h2v-2h-2v2zm-4,4h2v-2h-2v2zM11,9h2V7h-2v2zm8,12h2v-2h-2v2zm-4,-8h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_vertical_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_vertical_24dp.xml
new file mode 100644
index 0000000..45f9f94
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_vertical_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,9h2V7H3v2zm0,-4h2V3H3v2zm4,16h2v-2H7v2zm0,-8h2v-2H7v2zm-4,0h2v-2H3v2zm0,8h2v-2H3v2zm0,-4h2v-2H3v2zM7,5h2V3H7v2zm12,12h2v-2h-2v2zm-8,4h2V3h-2v18zm8,0h2v-2h-2v2zm0,-8h2v-2h-2v2zm0,-10v2h2V3h-2zm0,6h2V7h-2v2zm-4,-4h2V3h-2v2zm0,16h2v-2h-2v2zm0,-8h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_center_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_center_24dp.xml
new file mode 100644
index 0000000..10649b2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_center_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,15v2h10v-2H7zm-4,6h18v-2H3v2zm0,-8h18v-2H3v2zm4,-6v2h10V7H7zM3,3v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_justify_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_justify_24dp.xml
new file mode 100644
index 0000000..bddb3af
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_justify_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,21h18v-2H3v2zm0,-4h18v-2H3v2zm0,-4h18v-2H3v2zm0,-4h18V7H3v2zm0,-6v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_left_24dp.xml
new file mode 100644
index 0000000..39cdf41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,15H3v2h12v-2zm0,-8H3v2h12V7zM3,13h18v-2H3v2zm0,8h18v-2H3v2zM3,3v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_right_24dp.xml
new file mode 100644
index 0000000..59473f3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,21h18v-2H3v2zm6,-4h12v-2H9v2zm-6,-4h18v-2H3v2zm6,-4h12V7H9v2zM3,3v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_bold_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_bold_24dp.xml
new file mode 100644
index 0000000..8b6f679
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_bold_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.6,10.79c0.97,-0.67 1.65,-1.77 1.65,-2.79 0,-2.26 -1.75,-4 -4,-4H7v14h7.04c2.09,0 3.71,-1.7 3.71,-3.79 0,-1.52 -0.86,-2.82 -2.15,-3.42zM10,6.5h3c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5h-3v-3zm3.5,9H10v-3h3.5c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_clear_24dp.xml
new file mode 100644
index 0000000..54e3092
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.27,5L2,6.27l6.97,6.97L6.5,19h3l1.57,-3.66L16.73,21 18,19.73 3.55,5.27 3.27,5zM6,5v0.18L8.82,8h2.4l-0.72,1.68 2.1,2.1L14.21,8H20V5H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_fill_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_fill_24dp.xml
new file mode 100644
index 0000000..99e6f6a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_fill_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.56,8.94L7.62,0 6.21,1.41l2.38,2.38 -5.15,5.15c-0.59,0.59 -0.59,1.54 0,2.12l5.5,5.5c0.29,0.29 0.68,0.44 1.06,0.44s0.77,-0.15 1.06,-0.44l5.5,-5.5c0.59,-0.58 0.59,-1.53 0,-2.12zM5.21,10L10,5.21 14.79,10H5.21zM19,11.5s-2,2.17 -2,3.5c0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-1.33 -2,-3.5 -2,-3.5z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0,20h24v4H0z"
+        android:fillAlpha=".36"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_reset_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_reset_24dp.xml
new file mode 100644
index 0000000..f1153fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_reset_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,14c0,-4 -6,-10.8 -6,-10.8s-1.33,1.51 -2.73,3.52l8.59,8.59c0.09,-0.42 0.14,-0.86 0.14,-1.31zm-0.88,3.12L12.5,12.5 5.27,5.27 4,6.55l3.32,3.32C6.55,11.32 6,12.79 6,14c0,3.31 2.69,6 6,6 1.52,0 2.9,-0.57 3.96,-1.5l2.63,2.63 1.27,-1.27 -2.74,-2.74z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_text_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_text_24dp.xml
new file mode 100644
index 0000000..abc0c96
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_text_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0,20h24v4H0z"
+        android:fillAlpha=".36"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,3L5.5,17h2.25l1.12,-3h6.25l1.12,3h2.25L13,3h-2zm-1.38,9L12,5.67 14.38,12H9.62z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_decrease_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_decrease_24dp.xml
new file mode 100644
index 0000000..3cdf4fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_decrease_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,17h10v-2H11v2zm-8,-5l4,4V8l-4,4zm0,9h18v-2H3v2zM3,3v2h18V3H3zm8,6h10V7H11v2zm0,4h10v-2H11v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_increase_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_increase_24dp.xml
new file mode 100644
index 0000000..d4545b8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_increase_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,21h18v-2H3v2zM3,8v8l4,-4 -4,-4zm8,9h10v-2H11v2zM3,3v2h18V3H3zm8,6h10V7H11v2zm0,4h10v-2H11v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_italic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_italic_24dp.xml
new file mode 100644
index 0000000..95cdea6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_italic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,4v3h2.21l-3.42,8H6v3h8v-3h-2.21l3.42,-8H18V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_line_spacing_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_line_spacing_24dp.xml
new file mode 100644
index 0000000..4bf031a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_line_spacing_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,7h2.5L5,3.5 1.5,7H4v10H1.5L5,20.5 8.5,17H6V7zm4,-2v2h12V5H10zm0,14h12v-2H10v2zm0,-6h12v-2H10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_bulleted_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_bulleted_24dp.xml
new file mode 100644
index 0000000..6386300
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_bulleted_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,-6c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zm0,12.17c-0.74,0 -1.33,0.6 -1.33,1.33s0.6,1.33 1.33,1.33 1.33,-0.6 1.33,-1.33 -0.59,-1.33 -1.33,-1.33zM7,19h14v-2H7v2zm0,-6h14v-2H7v2zm0,-8v2h14V5H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_numbered_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_numbered_24dp.xml
new file mode 100644
index 0000000..1452f02
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_numbered_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,17h2v0.5H3v1h1v0.5H2v1h3v-4H2v1zm1,-9h1V4H2v1h1v3zm-1,3h1.8L2,13.1v0.9h3v-1H3.2L5,10.9V10H2v1zm5,-6v2h14V5H7zm0,14h14v-2H7v2zm0,-6h14v-2H7v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_paint_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_paint_24dp.xml
new file mode 100644
index 0000000..c906069
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_paint_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,4V3c0,-0.55 -0.45,-1 -1,-1H5c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1V6h1v4H9v11c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-9h8V4h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_quote_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_quote_24dp.xml
new file mode 100644
index 0000000..7e5e327
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_quote_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,17h3l2,-4V7H5v6h3zm8,0h3l2,-4V7h-6v6h3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_size_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_size_24dp.xml
new file mode 100644
index 0000000..d8d8f35
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_size_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,4v3h5v12h3V7h5V4H9zm-6,8h3v7h3v-7h3V9H3v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_strikethrough_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_strikethrough_24dp.xml
new file mode 100644
index 0000000..a31a531
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_strikethrough_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,19h4v-3h-4v3zM5,4v3h5v3h4V7h5V4H5zM3,14h18v-2H3v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_l_to_r_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_l_to_r_24dp.xml
new file mode 100644
index 0000000..67b9e4f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_l_to_r_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,10v5h2V4h2v11h2V4h2V2H9C6.79,2 5,3.79 5,6s1.79,4 4,4zm12,8l-4,-4v3H5v2h12v3l4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_r_to_l_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_r_to_l_24dp.xml
new file mode 100644
index 0000000..7fa296a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_r_to_l_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,10v5h2V4h2v11h2V4h2V2h-8C7.79,2 6,3.79 6,6s1.79,4 4,4zm-2,7v-3l-4,4 4,4v-3h12v-2H8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_underline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_underline_24dp.xml
new file mode 100644
index 0000000..0509b3e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_underline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,17c3.31,0 6,-2.69 6,-6V3h-2.5v8c0,1.93 -1.57,3.5 -3.5,3.5S8.5,12.93 8.5,11V3H6v8c0,3.31 2.69,6 6,6zm-7,2v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_functions_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_functions_24dp.xml
new file mode 100644
index 0000000..0351ae8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_functions_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,4H6v2l6.5,6L6,18v2h12v-3h-7l5,-5 -5,-5h7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_chart_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_chart_24dp.xml
new file mode 100644
index 0000000..1d393b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_chart_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2V7h2v10zm4,0h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_comment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_comment_24dp.xml
new file mode 100644
index 0000000..44c6f6d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_comment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4V4c0,-1.1 -0.9,-2 -2,-2zm-2,12H6v-2h12v2zm0,-3H6V9h12v2zm0,-3H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_drive_file_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_drive_file_24dp.xml
new file mode 100644
index 0000000..08e825b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_drive_file_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6H6zm7,7V3.5L18.5,9H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_emoticon_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_emoticon_24dp.xml
new file mode 100644
index 0000000..1ef868a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_emoticon_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_invitation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_invitation_24dp.xml
new file mode 100644
index 0000000..e0030fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_invitation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,12h-5v5h5v-5zM16,1v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2h-1V1h-2zm3,18H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_link_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_link_24dp.xml
new file mode 100644
index 0000000..1b8f436
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_link_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4V7H7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9H7c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2H8v2zm9,-6h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4V17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_photo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_photo_24dp.xml
new file mode 100644
index 0000000..61dcb63
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_photo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_merge_type_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_merge_type_24dp.xml
new file mode 100644
index 0000000..4eee325
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_merge_type_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_comment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_comment_24dp.xml
new file mode 100644
index 0000000..b275ad9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_comment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_edit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_edit_24dp.xml
new file mode 100644
index 0000000..0ff336a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_edit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_publish_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_publish_24dp.xml
new file mode 100644
index 0000000..b0651fc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_publish_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,4v2h14V4H5zm0,10h4v6h6v-6h4l-7,-7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_bottom_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_bottom_24dp.xml
new file mode 100644
index 0000000..877284e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_bottom_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,13h-3V3h-2v10H8l4,4 4,-4zM4,19v2h16v-2H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_center_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_center_24dp.xml
new file mode 100644
index 0000000..65df54e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_center_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,19h3v4h2v-4h3l-4,-4 -4,4zm8,-14h-3V1h-2v4H8l4,4 4,-4zM4,11v2h16v-2H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_top_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_top_24dp.xml
new file mode 100644
index 0000000..518a9f2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_top_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,11h3v10h2V11h3l-4,-4 -4,4zM4,3v2h16V3H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_wrap_text_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_wrap_text_24dp.xml
new file mode 100644
index 0000000..ec233dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_wrap_text_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,19h6v-2H4v2zM20,5H4v2h16V5zm-3,6H4v2h13.25c1.1,0 2,0.9 2,2s-0.9,2 -2,2H15v-2l-3,3 3,3v-2h2c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_attachment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_attachment_24dp.xml
new file mode 100644
index 0000000..be5b538
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_attachment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.5,18C4.46,18 2,15.54 2,12.5S4.46,7 7.5,7H18c2.21,0 4,1.79 4,4s-1.79,4 -4,4H9.5C8.12,15 7,13.88 7,12.5S8.12,10 9.5,10H17v1.5H9.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1H18c1.38,0 2.5,-1.12 2.5,-2.5S19.38,8.5 18,8.5H7.5c-2.21,0 -4,1.79 -4,4s1.79,4 4,4H17V18H7.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_24dp.xml
new file mode 100644
index 0000000..41b533d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_circle_24dp.xml
new file mode 100644
index 0000000..0d6e8c3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm4.5,14H8c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3l0.14,0.01C8.58,8.28 10.13,7 12,7c2.21,0 4,1.79 4,4h0.5c1.38,0 2.5,1.12 2.5,2.5S17.88,16 16.5,16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_done_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_done_24dp.xml
new file mode 100644
index 0000000..ad6129a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_done_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM10,17l-3.5,-3.5 1.41,-1.41L10,14.17 15.18,9l1.41,1.41L10,17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_download_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_download_24dp.xml
new file mode 100644
index 0000000..d76c1f6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_download_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_off_24dp.xml
new file mode 100644
index 0000000..cff2984
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_queue_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_queue_24dp.xml
new file mode 100644
index 0000000..104ec97
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_queue_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h0.71C7.37,7.69 9.48,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3s-1.34,3 -3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_upload_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_upload_24dp.xml
new file mode 100644
index 0000000..9a79995
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_upload_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_file_download_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_download_24dp.xml
new file mode 100644
index 0000000..9c90028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_download_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_file_upload_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_upload_24dp.xml
new file mode 100644
index 0000000..e362b93
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_upload_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zm-4,2h14v2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_24dp.xml
new file mode 100644
index 0000000..520679b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_open_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_open_24dp.xml
new file mode 100644
index 0000000..851069c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_open_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-8l-2,-2H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm0,12H4V8h16v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_shared_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_shared_24dp.xml
new file mode 100644
index 0000000..c4d91c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_shared_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-8l-2,-2H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm-5,3c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zm4,8h-8v-1c0,-1.33 2.67,-2 4,-2s4,0.67 4,2v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_24dp.xml
new file mode 100644
index 0000000..2082de1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v3h2V5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zm0,-4v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm0,-4v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_connected_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_connected_24dp.xml
new file mode 100644
index 0000000..af4a654
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_connected_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M1,18v3h3c0,-1.66 -1.34,-3 -3,-3zm0,-4v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm18,-7H5v1.63c3.96,1.28 7.09,4.41 8.37,8.37H19V7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11zm20,-7H3c-1.1,0 -2,0.9 -2,2v3h2V5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_computer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_computer_24dp.xml
new file mode 100644
index 0000000..60406f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_computer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,18c1.1,0 1.99,-0.9 1.99,-2L22,6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_mac_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_mac_24dp.xml
new file mode 100644
index 0000000..0d51046
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_mac_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,2H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7l-2,3v1h8v-1l-2,-3h7c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm0,12H3V4h18v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_windows_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_windows_24dp.xml
new file mode 100644
index 0000000..f4f1f3b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_windows_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,2H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v2H8v2h8v-2h-2v-2h7c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm0,14H3V4h18v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_dock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_dock_24dp.xml
new file mode 100644
index 0000000..e923ece
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_dock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,23h8v-2H8v2zm8,-21.99L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM16,15H8V5h8v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_gamepad_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_gamepad_24dp.xml
new file mode 100644
index 0000000..8891427
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_gamepad_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,7.5V2H9v5.5l3,3 3,-3zM7.5,9H2v6h5.5l3,-3 -3,-3zM9,16.5V22h6v-5.5l-3,-3 -3,3zM16.5,9l-3,3 3,3H22V9h-5.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_24dp.xml
new file mode 100644
index 0000000..320d262
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_mic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_mic_24dp.xml
new file mode 100644
index 0000000..a8e775a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_mic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_24dp.xml
new file mode 100644
index 0000000..63c10e5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,5H4c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm-9,3h2v2h-2V8zm0,3h2v2h-2v-2zM8,8h2v2H8V8zm0,3h2v2H8v-2zm-1,2H5v-2h2v2zm0,-3H5V8h2v2zm9,7H8v-2h8v2zm0,-4h-2v-2h2v2zm0,-3h-2V8h2v2zm3,3h-2v-2h2v2zm0,-3h-2V8h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_alt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_alt_24dp.xml
new file mode 100644
index 0000000..98a8966
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_alt_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.5,10c0.83,0 1.5,-0.67 1.5,-1.5S16.33,7 15.5,7 14,7.67 14,8.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,7 8.5,7 7,7.67 7,8.5 7.67,10 8.5,10zm3.5,7c2.61,0 4.83,-1.67 5.65,-4H6.35c0.82,2.33 3.04,4 5.65,4zm-0.01,-16C6.47,1 2,5.48 2,11s4.47,10 9.99,10C17.52,21 22,16.52 22,11S17.52,1 11.99,1zM12,19c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_down_24dp.xml
new file mode 100644
index 0000000..d5c447b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_left_24dp.xml
new file mode 100644
index 0000000..7f27205
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.41,16.09l-4.58,-4.59 4.58,-4.59L14,5.5l-6,6 6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_right_24dp.xml
new file mode 100644
index 0000000..4bae6bb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_up_24dp.xml
new file mode 100644
index 0000000..ebf42c3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_backspace_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_backspace_24dp.xml
new file mode 100644
index 0000000..8ada65f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_backspace_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,11H6.83l3.58,-3.59L9,6l-6,6 6,6 1.41,-1.41L6.83,13H21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_capslock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_capslock_24dp.xml
new file mode 100644
index 0000000..1c26f8a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_capslock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8.41L16.59,13 18,11.59l-6,-6 -6,6L7.41,13 12,8.41zM6,18h12v-2H6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_control_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_control_24dp.xml
new file mode 100644
index 0000000..74c9367
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_control_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_hide_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_hide_24dp.xml
new file mode 100644
index 0000000..6ae3110
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_hide_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,3H4c-1.1,0 -1.99,0.9 -1.99,2L2,15c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-9,3h2v2h-2V6zm0,3h2v2h-2V9zM8,6h2v2H8V6zm0,3h2v2H8V9zm-1,2H5V9h2v2zm0,-3H5V6h2v2zm9,7H8v-2h8v2zm0,-4h-2V9h2v2zm0,-3h-2V6h2v2zm3,3h-2V9h2v2zm0,-3h-2V6h2v2zm-7,15l4,-4H8l4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_return_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_return_24dp.xml
new file mode 100644
index 0000000..f74dfc7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_return_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,7v4H5.83l3.58,-3.59L8,6l-6,6 6,6 1.41,-1.41L5.83,13H21V7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_tab_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_tab_24dp.xml
new file mode 100644
index 0000000..37c5692
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_tab_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.59,7.41L15.17,11H1v2h14.17l-3.59,3.59L13,18l6,-6 -6,-6 -1.41,1.41zM20,6v12h2V6h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_voice_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_voice_24dp.xml
new file mode 100644
index 0000000..a41eedd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_voice_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zm5.3,-3c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12H5c0,3.42 2.72,6.23 6,6.72V22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_24dp.xml
new file mode 100644
index 0000000..87ebf54
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,18c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_chromebook_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_chromebook_24dp.xml
new file mode 100644
index 0000000..78101d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_chromebook_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,18V3H2v15H0v2h24v-2h-2zm-8,0h-4v-1h4v1zm6,-3H4V5h16v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_mac_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_mac_24dp.xml
new file mode 100644
index 0000000..471cf10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_mac_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,18c1.1,0 1.99,-0.9 1.99,-2L22,5c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v11c0,1.1 0.9,2 2,2H0c0,1.1 0.9,2 2,2h20c1.1,0 2,-0.9 2,-2h-4zM4,5h16v11H4V5zm8,14c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_windows_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_windows_24dp.xml
new file mode 100644
index 0000000..e4beebb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_windows_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,18v-1c1.1,0 1.99,-0.9 1.99,-2L22,5c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2v1H0v2h24v-2h-4zM4,5h16v10H4V5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_memory_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_memory_24dp.xml
new file mode 100644
index 0000000..53a7f92
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_memory_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,9H9v6h6V9zm-2,4h-2v-2h2v2zm8,-2V9h-2V7c0,-1.1 -0.9,-2 -2,-2h-2V3h-2v2h-2V3H9v2H7c-1.1,0 -2,0.9 -2,2v2H3v2h2v2H3v2h2v2c0,1.1 0.9,2 2,2h2v2h2v-2h2v2h2v-2h2c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2zm-4,6H7V7h10v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_mouse_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_mouse_24dp.xml
new file mode 100644
index 0000000..83281a8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_mouse_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,1.07V9h7c0,-4.08 -3.05,-7.44 -7,-7.93zM4,15c0,4.42 3.58,8 8,8s8,-3.58 8,-8v-4H4v4zm7,-13.93C7.05,1.56 4,4.92 4,9h7V1.07z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_android_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_android_24dp.xml
new file mode 100644
index 0000000..94b46f0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_android_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,1H8C6.34,1 5,2.34 5,4v16c0,1.66 1.34,3 3,3h8c1.66,0 3,-1.34 3,-3V4c0,-1.66 -1.34,-3 -3,-3zm-2,20h-4v-1h4v1zm3.25,-3H6.75V4h10.5v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_iphone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_iphone_24dp.xml
new file mode 100644
index 0000000..d234325
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_iphone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.5,1h-8C6.12,1 5,2.12 5,3.5v17C5,21.88 6.12,23 7.5,23h8c1.38,0 2.5,-1.12 2.5,-2.5v-17C18,2.12 16.88,1 15.5,1zm-4,21c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm4.5,-4H7V4h9v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_24dp.xml
new file mode 100644
index 0000000..e54c722
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6h18V4H4c-1.1,0 -2,0.9 -2,2v11H0v3h14v-3H4V6zm19,2h-6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1zm-1,9h-4v-7h4v7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_off_24dp.xml
new file mode 100644
index 0000000..91b57e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,6V4H6.82l2,2H22zM1.92,1.65L0.65,2.92l1.82,1.82C2.18,5.08 2,5.52 2,6v11H0v3h17.73l2.35,2.35 1.27,-1.27L3.89,3.62 1.92,1.65zM4,6.27L14.73,17H4V6.27zM23,8h-6c-0.55,0 -1,0.45 -1,1v4.18l2,2V10h4v7h-2.18l3,3H23c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_security_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_security_24dp.xml
new file mode 100644
index 0000000..5110f1e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_security_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zm0,10.99h7c-0.53,4.12 -3.28,7.79 -7,8.94V12H5V6.3l7,-3.11v8.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_sim_card_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_sim_card_24dp.xml
new file mode 100644
index 0000000..de928fc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_sim_card_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.99,4c0,-1.1 -0.89,-2 -1.99,-2h-8L4,8v12c0,1.1 0.9,2 2,2h12.01c1.1,0 1.99,-0.9 1.99,-2l-0.01,-16zM9,19H7v-2h2v2zm8,0h-2v-2h2v2zm-8,-4H7v-4h2v4zm4,4h-2v-4h2v4zm0,-6h-2v-2h2v2zm4,2h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_smartphone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_smartphone_24dp.xml
new file mode 100644
index 0000000..8b9235f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_smartphone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_speaker_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_speaker_24dp.xml
new file mode 100644
index 0000000..f0c2944
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_speaker_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,2H7c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,1.99 2,1.99L17,22c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-5,2c1.1,0 2,0.9 2,2s-0.9,2 -2,2c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2zm0,16c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_24dp.xml
new file mode 100644
index 0000000..2f97c67
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,4H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 1.99,-0.9 1.99,-2L23,6c0,-1.1 -0.9,-2 -2,-2zm-2,14H5V6h14v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_android_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_android_24dp.xml
new file mode 100644
index 0000000..7a63df9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_android_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,0H6C4.34,0 3,1.34 3,3v18c0,1.66 1.34,3 3,3h12c1.66,0 3,-1.34 3,-3V3c0,-1.66 -1.34,-3 -3,-3zm-4,22h-4v-1h4v1zm5.25,-3H4.75V3h14.5v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_mac_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_mac_24dp.xml
new file mode 100644
index 0000000..3318071
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_mac_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.5,0h-14C3.12,0 2,1.12 2,2.5v19C2,22.88 3.12,24 4.5,24h14c1.38,0 2.5,-1.12 2.5,-2.5v-19C21,1.12 19.88,0 18.5,0zm-7,23c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm7.5,-4H4V3h15v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tv_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tv_24dp.xml
new file mode 100644
index 0000000..245642a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tv_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zm0,14H3V5h18v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_watch_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_watch_24dp.xml
new file mode 100644
index 0000000..e90cf2d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_watch_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,12c0,-2.54 -1.19,-4.81 -3.04,-6.27L16,0H8l-0.95,5.73C5.19,7.19 4,9.45 4,12s1.19,4.81 3.05,6.27L8,24h8l0.96,-5.73C18.81,16.81 20,14.54 20,12zM6,12c0,-3.31 2.69,-6 6,-6s6,2.69 6,6 -2.69,6 -6,6 -6,-2.69 -6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_delivery_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_delivery_36px.xml
new file mode 100644
index 0000000..244971a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_delivery_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2.5,16.875L4.375,15H6.25v3H2.5V16.875z M7,26.5c0,1.932,1.556,3.5,3.491,3.5                         C12.432,30,14,28.432,14,26.5c0,-1.933,-1.568,-3.5,-3.509,-3.5C8.556,23,7,24.567,7,26.5z M14,23l2,2h9l2,-2H14z M27,26.5                         c0,1.932,1.566,3.5,3.5,3.5c1.933,0,3.5,-1.568,3.5,-3.5c0,-1.933,-1.567,-3.5,-3.5,-3.5C28.566,23,27,24.567,27,26.5z M9,13v9                         c-0.488,0,-3,0,-3,2l-2.833,0.083L1,23l0.01,-6.5L4,13H9z M10,21V10c0,-2.128,2.03,-4,4,-4h16.423C32.392,6,34,7.739,34,9.867V21H10z                         "/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_flight_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_flight_36px.xml
new file mode 100644
index 0000000..33b3aac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_flight_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.964,33l-3.234,-6.728L3,23.035l1.421,-1.422c0.173,-0.168,0.407,-0.233,0.627,-0.188l5.707,0.828l5.769,-6.188                 L4.421,10.226l1.942,-1.939c0.176,-0.176,0.465,-0.28,0.688,-0.23l13.032,3.593l5.87,-5.871c1.6,-1.601,3.732,-1.604,4.805,-0.535                 c1.066,1.071,1.066,3.205,-0.537,4.805l-5.873,5.87l3.592,13.034c0.055,0.223,-0.051,0.508,-0.227,0.684l-1.939,1.941l-5.836,-12.1                 l-6.191,5.766l0.826,5.71c0.049,0.217,-0.016,0.451,-0.184,0.624L12.964,33z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_hangout_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_hangout_36px.xml
new file mode 100644
index 0000000..1483f09
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_hangout_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.25,3C10.209,3,4.5,8.709,4.5,15.75S10.209,28.5,17.25,28.5H18v5.25c7.289,-3.516,12,-11.25,12,-18             C30,8.709,24.291,3,17.25,3z M16.5,16.5l-1.5,3h-2.25l1.5,-3H12V12h4.5V16.5z M22.5,16.5l-1.5,3h-2.25l1.5,-3H18V12h4.5V16.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_location_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_location_36px.xml
new file mode 100644
index 0000000..95a390c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_location_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,3C12.202,3,7.5,7.701,7.5,13.5C7.5,21.375,18,33,18,33s10.5,-11.625,10.5,-19.5C28.5,7.701,23.798,3,18,3z M18,17.25                     c-2.071,0,-3.75,-1.679,-3.75,-3.75S15.929,9.75,18,9.75s3.75,1.679,3.75,3.75S20.071,17.25,18,17.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_purchase_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_purchase_36px.xml
new file mode 100644
index 0000000..c8cea24
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_purchase_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M32.736,13h-7.056l-6.454,-9.345c-0.452,-0.677,-1.367,-0.859,-2.044,-0.407C17.016,3.36,16.881,3.5,16.776,3.656l-0.003,-0.001             L16.77,3.663l-0.002,0.001L10.319,13H3.264c-0.814,0,-1.475,0.976,-1.475,1.79c0,0.137,0.019,0.269,0.054,0.395L5.58,28.843             C5.926,30.087,7.066,31,8.421,31h19.157c1.355,0,2.496,-0.913,2.842,-2.157l3.737,-13.658c0.034,-0.126,0.053,-0.258,0.053,-0.395             C34.21,13.976,33.551,13,32.736,13z M18,25c-1.657,0,-3,-1.343,-3,-3s1.343,-3,3,-3c1.656,0,3,1.343,3,3S19.656,25,18,25z M13.579,13             L18,6.832L22.421,13H13.579z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_restaurant_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_restaurant_36px.xml
new file mode 100644
index 0000000..7e041e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_restaurant_36px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,0.0 h36.0 v36.0 h-36.0z"
+        android:fillAlpha="0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,10c0,0.55,-0.45,1,-1,1l0,0c-0.55,0,-1,-0.45,-1,-1V3c0,-0.55,-0.45,-1,-1,-1l0,0c-0.55,0,-1,0.45,-1,1v7c0,0.55,-0.45,1,-1,1l0,0             c-0.55,0,-1,-0.45,-1,-1V3c0,-0.55,-0.45,-1,-1,-1l0,0C7.45,2,7,2.45,7,3v9c0,0.55,0.232,1.386,0.514,1.857l1.971,3.285             c0.283,0.472,0.486,1.307,0.452,1.856L9.062,33.002C9.028,33.551,9.45,34,10,34h4c0.55,0,0.972,-0.449,0.938,-0.998l-0.875,-14.004             c-0.034,-0.549,0.169,-1.384,0.452,-1.856l1.971,-3.285C16.768,13.386,17,12.55,17,12V3c0,-0.55,-0.45,-1,-1,-1l0,0c-0.55,0,-1,0.45,-1,1V10z              M22.707,5.293C22.318,5.682,22,6.45,22,7v9c0,0.55,0.25,1.374,0.555,1.832l0.891,1.336C23.75,19.626,24,20.45,24,21v12             c0,0.55,0.45,1,1,1h3c0.55,0,1,-0.45,1,-1V3c0,-0.55,-0.45,-1,-1,-1h-1c-0.55,0,-1.318,0.318,-1.707,0.707L22.707,5.293z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_ticket_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_ticket_36px.xml
new file mode 100644
index 0000000..150e96d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_ticket_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M32,15v-5c0,-1.656,-1.344,-3,-3,-3H7c-1.656,0,-3,1.344,-3,3v5c1.656,0,3,1.344,3,3s-1.344,3,-3,3v5c0,1.656,1.344,3,3,3h22             c1.656,0,3,-1.344,3,-3v-5c-1.656,0,-3,-1.344,-3,-3S30.344,15,32,15z M22.695,24.75L18,21.917l-4.695,2.833l1.242,-5.341L10.5,15.75             l5.363,-0.404L18,10.5l2.137,4.846L25.5,15.75l-4.047,3.659L22.695,24.75z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_wallet_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_wallet_36px.xml
new file mode 100644
index 0000000..352a520
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_wallet_36px.xml
@@ -0,0 +1,35 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.797,5.986c-0.082,-0.053,-0.154,-0.115,-0.238,-0.168c-0.525,-0.327,-1.203,-0.421,-1.881,-0.451l-0.156,0.062                 c-0.104,0,-0.129,0,-0.152,0c-0.559,0,-1.119,0.058,-1.637,0.342c-1.576,0.868,-2.115,2.757,-1.207,4.263                 c1.26,2.076,1.979,4.347,2.178,6.667c-0.025,-0.033,-0.057,-0.072,-0.08,-0.107c0.496,4.996,-1.418,8.639,-2.539,11.144                 c0.004,0.035,0.014,0.068,0.016,0.104c0.094,0.988,0.666,1.92,1.637,2.456c0.518,0.285,1.086,0.42,1.645,0.42                 c1.139,0,2.246,-0.565,2.857,-1.575c1.875,-3.103,2.896,-6.546,3.08,-10.022c0.008,0.017,0.018,0.033,0.027,0.051                 C21.758,14.308,20.945,9.223,17.797,5.986z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.623,16.585c-1.924,-2.673,-4.453,-4.963,-7.469,-6.643C6.633,9.651,6.061,9.514,5.498,9.514                 c-1.137,0,-2.238,0.56,-2.85,1.562c-0.918,1.499,-0.387,3.427,1.186,4.3c4.678,2.607,7.75,7.22,8.25,12.356                 C13.205,25.228,15.119,21.581,14.623,16.585z"
+        android:fillAlpha="0.5"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M30.631,3.937C30.051,2.718,28.799,2,27.488,2c-0.477,0,-0.957,0.093,-1.416,0.292c-1.736,0.746,-2.508,2.694,-1.725,4.348                 c1.248,2.64,2.033,5.42,2.369,8.238c-0.025,-0.043,-0.062,-0.081,-0.09,-0.124c0.754,7.063,-1.365,12.161,-2.576,15.625                 c-0.002,0.054,0,0.105,-0.002,0.159c-0.072,1.313,0.689,2.596,2.023,3.168C26.531,33.907,27.012,34,27.484,34                 c1.314,0,2.566,-0.721,3.146,-1.938C34.857,23.125,34.857,12.873,30.631,3.937z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M26.627,14.754c-2.207,-3.453,-5.199,-6.465,-8.83,-8.768c3.148,3.236,3.961,8.321,3.549,13.183                 c1.916,3.388,2.893,7.253,2.705,11.21C25.262,26.915,27.381,21.817,26.627,14.754z"
+        android:fillAlpha="0.5"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_weather_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_weather_36px.xml
new file mode 100644
index 0000000..7d347c9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_weather_36px.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.067,15.237c0.064,0,0.129,0.012,0.194,0.012c0.706,-2.286,2.23,-4.253,4.303,-5.558                     c-1.011,-1.79,-2.902,-3.017,-5.104,-3.017c-3.257,0,-5.896,2.64,-5.896,5.896c0,1.783,0.814,3.357,2.066,4.437                     c0.011,-0.011,0.021,-0.022,0.032,-0.033C5.915,15.841,7.477,15.237,9.067,15.237z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M29.026,18.433c-0.454,0,-0.891,0.068,-1.306,0.186c-0.336,-4.412,-3.927,-7.886,-8.313,-7.886c-4.487,0,-8.145,3.634,-8.335,8.191             c-0.636,-0.312,-1.342,-0.492,-2.095,-0.492c-2.693,0,-4.878,2.02,-4.878,4.784S6.284,28,8.978,28c2.041,0,18.21,0,20.049,0             c2.691,0,4.874,-2.02,4.874,-4.783S31.718,18.433,29.026,18.433z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_youtube_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_youtube_36px.xml
new file mode 100644
index 0000000..394ad76
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_youtube_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M30.003,6.601C29.101,6.305,23.552,6,18,6C12.451,6,6.902,6.281,6,6.576C3.653,7.351,3,12.606,3,18                 s0.653,10.649,3,11.424C6.902,29.72,12.451,30,18,30c5.552,0,11.101,-0.28,12.003,-0.576c2.344,-0.775,2.982,-6.031,2.982,-11.424                 S32.347,7.374,30.003,6.601z M15,24V12l8.25,6L15,24z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_add_cluster_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_add_cluster_36px.xml
new file mode 100644
index 0000000..4a70cf1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_add_cluster_36px.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="35dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="35.998">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.0,8.0 h3.0 v21.0 h-3.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.0,17.0 h21.0 v3.0 h-21.0z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_check_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/ic_check_24dp.xml
new file mode 100644
index 0000000..065f56e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_check_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.17l-4.17,-4.17 -1.42,1.41 5.59,5.59 12,-12 -1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_chevron_down_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_chevron_down_36px.xml
new file mode 100644
index 0000000..af6708a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_chevron_down_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.879,12.879l-6.879000,6.879000 -6.879000,-6.879000 -2.121000,2.121000 9.000000,9.000000 9.000000,-9.000000z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_compose_popout_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_compose_popout_36px.xml
new file mode 100644
index 0000000..6019979
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_compose_popout_36px.xml
@@ -0,0 +1,31 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,0.0 h36.0 v36.0 h-36.0z"
+        android:fillAlpha="0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.41,24.832l1.094000,1.171999 6.496000,-6.004000 -6.496000,-6.000000 -1.094000,1.168000 3.086000,2.832000 -17.496000,0.000000 0.000000,4.000000 17.500000,0.000000z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,3v11h3v-4h19v20H11v-4H8v7h25V3H8z M30,8H11V6h19V8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_custom_cluster_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_custom_cluster_36px_clr.xml
new file mode 100644
index 0000000..50a2c2b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_custom_cluster_36px_clr.xml
@@ -0,0 +1,29 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M31.954,8.458L13.313,6.02c-1.288,-0.168,-2.467,0.74,-2.637,2.029l-0.527,4.041l12.641,-1.374                         c1.959,-0.212,3.708,1.218,3.917,3.157l1.157,10.673l1.961,0.257c1.287,0.169,2.465,-0.741,2.632,-2.029l1.523,-11.676                         C34.148,9.809,33.242,8.627,31.954,8.458z"
+        android:fillAlpha="0.6"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22.382,12.2L4.057,14.163c-1.265,0.136,-2.181,1.256,-2.042,2.507l1.242,11.301c0.137,1.248,1.274,2.151,2.54,2.016                 l18.325,-1.963c1.266,-0.137,2.18,-1.259,2.042,-2.505l-1.243,-11.303C24.784,12.967,23.647,12.064,22.382,12.2 M22.425,16.516                 l-7.41,6.525v-0.002c-0.18,0.171,-0.412,0.286,-0.678,0.315c-0.263,0.027,-0.518,-0.036,-0.726,-0.166l-0.001,0.004l-8.657,-4.805                 c-0.282,-0.18,-0.484,-0.48,-0.525,-0.835c-0.068,-0.626,0.389,-1.184,1.021,-1.252c0.252,-0.026,0.49,0.027,0.692,0.142l7.917,4.37                 l6.782,-5.944c0.173,-0.153,0.396,-0.259,0.646,-0.286c0.632,-0.067,1.2,0.383,1.27,1.008C22.793,15.944,22.663,16.281,22.425,16.516"
+        android:fillAlpha="0.6"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_finance_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_finance_36px_clr.xml
new file mode 100644
index 0000000..2b736e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_finance_36px_clr.xml
@@ -0,0 +1,40 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M0.0,0.0 h36.0 v36.0 h-36.0z"
+        android:fillAlpha="0"
+        android:fillColor="#689F38"/>
+    <path
+        android:pathData="M6.0,20.0 h6.0 v13.0 h-6.0z"
+        android:fillColor="#689F38"/>
+    <path
+        android:pathData="M15.0,21.0 h6.0 v12.0 h-6.0z"
+        android:fillColor="#689F38"/>
+    <path
+        android:pathData="M24.0,14.0 h6.0 v19.0 h-6.0z"
+        android:fillColor="#689F38"/>
+    <path
+        android:pathData="M3.707,18.707l-1.414000,-1.414001 8.207000,-8.206999 6.000000,6.000000 11.500000,-11.500000 1.414000,1.414000 -12.914000,12.914000 -6.000000,-6.000000z"
+        android:fillColor="#689F38"/>
+    <path
+        android:pathData="M24.535,5.0l3.465000,0.000000 0.000000,3.464000 2.000000,-1.628000 0.000000,-3.836000 -3.836000,0.000000z"
+        android:fillColor="#689F38"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_forums_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_forums_36px_clr.xml
new file mode 100644
index 0000000..b742c9b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_forums_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M24,7.111C24,5.393,22.605,4,20.889,4H7.111C5.395,4,4.016,5.393,4.016,7.111L4,25l6.223,-5H20                     c2.375,0,4,-2,4,-4V7.111z M12,22v1.889C12,25.607,13.395,27,15.111,27h10.666L32,33V14.111C32,12.393,30.605,11,28.889,11H26                     l0.01,5c0,4,-1.979,6,-5.01,6H12z"
+        android:fillColor="#3F51B5"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_history_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/ic_history_24dp.xml
new file mode 100644
index 0000000..4174bf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_history_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,3c-4.97,0 -9,4.03 -9,9h-3l3.89,3.89 0.07,0.14 4.04,-4.03h-3c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42c1.63,1.63 3.87,2.64 6.36,2.64 4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm-1,5v5l4.28,2.54 0.72,-1.21 -3.5,-2.08v-4.25h-1.5z"
+        android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_inbox_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_inbox_36px_clr.xml
new file mode 100644
index 0000000..5f77a5c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_inbox_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M30,6H6C4.344,6,3.015,7.343,3.015,9L3,27c0,1.657,1.344,3,3,3h24c1.656,0,3,-1.343,3,-3V9             C33,7.343,31.656,6,30,6z M29.44,13.168L18.92,20.685v-0.002C18.665,20.881,18.346,21,18,21s-0.665,-0.119,-0.917,-0.317             l-0.003,0.002L6.56,13.168C6.22,12.892,6,12.471,6,12c0,-0.83,0.671,-1.5,1.5,-1.5c0.328,0,0.63,0.104,0.876,0.283L18,17.628             l9.624,-6.845C27.87,10.604,28.172,10.5,28.5,10.5c0.829,0,1.5,0.67,1.5,1.5C30,12.471,29.783,12.892,29.44,13.168"
+        android:fillColor="#4285F4"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_low_priority_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_low_priority_36px.xml
new file mode 100644
index 0000000..ad29907
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_low_priority_36px.xml
@@ -0,0 +1,35 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4.457,25.029L0,29h14V15l-4.113,4.572C5.264,14.97,3.648,8.091,4.556,2.556C0.88,8.1,-1.096,17.663,4.457,25.029z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M35,10c0,0.55,-0.45,1,-1,1H18c-0.55,0,-1,-0.45,-1,-1V7c0,-0.55,0.45,-1,1,-1h16c0.55,0,1,0.45,1,1V10z"
+        android:fillAlpha="0.6"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M35,19c0,0.55,-0.45,1,-1,1H18c-0.55,0,-1,-0.45,-1,-1v-3c0,-0.55,0.45,-1,1,-1h16c0.55,0,1,0.45,1,1V19z"
+        android:fillAlpha="0.6"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M35,28c0,0.55,-0.45,1,-1,1H18c-0.55,0,-1,-0.45,-1,-1v-3c0,-0.55,0.45,-1,1,-1h16c0.55,0,1,0.45,1,1V28z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_multiselect_check_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_multiselect_check_36px.xml
new file mode 100644
index 0000000..0e03c3c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_multiselect_check_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M30.0,9.0l-2.551001,-1.726000 -12.448999,16.726000 -7.131000,-5.250000 -1.869000,2.540001 9.750000,7.209999z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_offers_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_offers_36px_clr.xml
new file mode 100644
index 0000000..02fe5e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_offers_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M4,28.889C4,30.605,5.395,32,7.111,32h7.777L4,21.111V28.889z M31.089,17.362L18.645,4.912                 c-0.565,-0.562,-1.344,-0.91,-2.2,-0.912H7.889C5.741,4,4,5.741,4,7.889v8.556c0,0.86,0.35,1.636,0.911,2.2v0.001l12.451,12.443                 C17.924,31.65,18.702,32,19.562,32c0.86,0,1.638,-0.348,2.2,-0.91l9.324,-9.326C31.65,21.202,32,20.421,32,19.562                 S31.65,17.924,31.089,17.362 M9.444,12.167c-1.507,0,-2.722,-1.221,-2.722,-2.723s1.215,-2.723,2.722,-2.723                 c1.504,0,2.723,1.221,2.723,2.723S10.948,12.167,9.444,12.167"
+        android:fillColor="#00BCD4"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_pin_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_pin_36px.xml
new file mode 100644
index 0000000..6fcad41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_pin_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24,7.091c0,0,3,-1.092,3,-3.091V2H10v2c0,1.999,3,3.091,3,3.091V18c-2.463,0,-4,1.46,-4,4h8l0.075,10.545                     C17.114,33.179,17.787,34,18.5,34s1.384,-0.821,1.425,-1.455L20,22h8c0,-2.54,-1.546,-4,-4,-4V7.091z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_purchase_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_purchase_36px_clr.xml
new file mode 100644
index 0000000..f014a95
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_purchase_36px_clr.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M31.799,11H10.939c-0.029,0,-0.05,0.015,-0.078,0.016L9.068,5H3.49C2.666,5,2,5.671,2,6.5                     S2.666,8,3.49,8h3.36l4.151,13.931C11.195,22.581,11.787,23,12.43,23H29.15c0.817,0,1.636,-0.909,1.813,-1.715l2.002,-8.945                     C33.143,11.534,32.617,11,31.799,11z"
+        android:fillColor="#795548"/>
+    <path
+        android:pathData="M27.5,29.0 m-3.0, 0 a 3.0,3.0 0 1,1 6.0,0 a3.0,3.0 0 1,1 -6.0,0"
+        android:fillColor="#795548"/>
+    <path
+        android:pathData="M14.0,29.0 m-3.0, 0 a 3.0,3.0 0 1,1 6.0,0 a3.0,3.0 0 1,1 -6.0,0"
+        android:fillColor="#795548"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_reminder_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_reminder_36px_clr.xml
new file mode 100644
index 0000000..ecfe620
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_reminder_36px_clr.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M22,8v0.668C22.548,8.839,23.496,9,24.524,9c2.225,0,4.809,-0.756,4.44,-3.802                 C28.771,3.583,27.944,3,26.896,3C25.291,3,23.168,4.365,22,5.49V4c0,-1.104,-0.896,-2,-2,-2s-2,0.896,-2,2v1.49                 C16.832,4.365,14.709,3,13.104,3c-1.048,0,-1.876,0.583,-2.069,2.198C10.667,8.244,13.251,9,15.476,9                 C16.503,9,17.452,8.839,18,8.668V8H22z M22.017,6.859C22.306,6.197,25.045,3.9,26.896,3.9c0.353,0,1.007,0,1.176,1.406                 c0.097,0.795,-0.044,1.394,-0.43,1.828C26.897,7.975,25.369,8.1,24.524,8.1c-1.245,0,-2.256,-0.259,-2.427,-0.348                 C22.058,7.724,22.028,7.687,22,7.65V6.906C22.007,6.891,22.01,6.876,22.017,6.859z M17.924,7.738                 C17.731,7.841,16.721,8.1,15.476,8.1c-0.845,0,-2.373,-0.125,-3.117,-0.965c-0.386,-0.435,-0.526,-1.033,-0.43,-1.829                 C12.098,3.9,12.753,3.9,13.105,3.9c1.85,0,4.589,2.297,4.879,2.962c0.007,0.016,0.01,0.029,0.016,0.044v0.746                 C17.977,7.684,17.953,7.717,17.924,7.738z"
+        android:fillColor="#3C80F5"/>
+    <path
+        android:pathData="M29,17c-2.277,1.139,-3.736,2.739,-4.469,3.875c-0.906,1.406,-1.25,0.828,-1.25,0.828                 s-0.344,-0.391,-0.719,-1.328S22,17,22,17V8.609h-4v4.641c0,-1.1,-0.9,-1.25,-2,-1.25s-2,0.9,-2,2v0.25c0,-1.1,-0.9,-1.25,-2,-1.25s-2,0.9,-2,2                 v1.25c0,-1.1,-1.969,-1.562,-2.844,-0.688c-0.748,0.748,-1.031,1.744,-1.031,2.844L6,22c0,4.375,2.875,11,6.369,11                 c1.631,0,5.818,0,5.818,0c3.438,0,5.23,-3,5.23,-3l2.676,-3.594c0.562,-0.547,0.634,-0.859,0.701,-1.023C28.025,22.398,32,18,32,18                 S33,15,29,17z"
+        android:fillColor="#3C80F5"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_reply_all_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_reply_all_36px.xml
new file mode 100644
index 0000000..c2075dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_reply_all_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.828,11.672v-4.5L0,18l10.859,10.859v-4.5L4.5,18L10.828,11.672z M20,13.5v-6L9,18l11,10.5v-6.149         c7.5,0,12.25,2.399,16,7.649C34.5,22.5,30.5,15,20,13.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_rotate_24_01.xml b/asset-studio/src/main/java/images/material_design_icons/ic_rotate_24_01.xml
new file mode 100644
index 0000000..fbbd84d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_rotate_24_01.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.25,1.75c-0.6,-0.6,-1.5,-0.6,-2.1,0l-6.4,6.4c-0.6,0.6,-0.6,1.5,0,2.1l12,12c0.6,0.6,1.5,0.6,2.1,0l6.4,-6.4     c0.6,-0.6,0.6,-1.5,0,-2.1L10.25,1.75z M14.85,21.25l-12,-12l6.4,-6.4l12,12L14.85,21.25z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.55,2.5c3.3,1.5,5.6,4.7,6,8.5h1.5c-0.6,-6.2,-5.7,-11,-12,-11c-0.2,0,-0.4,0,-0.7,0l3.8,3.8L16.55,2.5z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.55,21.5c-3.3,-1.5,-5.6,-4.7,-6,-8.5h-1.4c0.5,6.2,5.6,11,11.9,11c0.2,0,0.4,0,0.7,0l-3.8,-3.8L7.55,21.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_search_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/ic_search_24dp.xml
new file mode 100644
index 0000000..6cc717d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_search_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.5,14h-0.79l-0.28,-0.27c0.98,-1.14 1.57,-2.62 1.57,-4.23 0,-3.59 -2.91,-6.5 -6.5,-6.5s-6.5,2.91 -6.5,6.5 2.91,6.5 6.5,6.5c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99 1.49,-1.49 -4.99,-5zm-6,0c-2.49,0 -4.5,-2.01 -4.5,-4.5s2.01,-4.5 4.5,-4.5 4.5,2.01 4.5,4.5 -2.01,4.5 -4.5,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_social_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_social_36px_clr.xml
new file mode 100644
index 0000000..5c0b9f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_social_36px_clr.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M12.314,23.667c-0.92,-0.513,-1.747,-0.437,-2.871,-0.437C5.879,23.23,3.015,27,3.015,29h8.142                 C11.189,27,11.459,25.13,12.314,23.667z"
+        android:fillColor="#DB4437"/>
+    <path
+        android:pathData="M23.25,20c-4.549,0,-9.75,3,-9.75,9H33C33,23,27.803,20,23.25,20z"
+        android:fillColor="#DB4437"/>
+    <path
+        android:pathData="M23.0,12.0 m-5.0, 0 a 5.0,5.0 0 1,1 10.0,0 a5.0,5.0 0 1,1 -10.0,0"
+        android:fillColor="#DB4437"/>
+    <path
+        android:pathData="M10.0,16.0 m-4.0, 0 a 4.0,4.0 0 1,1 8.0,0 a4.0,4.0 0 1,1 -8.0,0"
+        android:fillColor="#DB4437"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_speed_dial_48px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_speed_dial_48px_clr.xml
new file mode 100644
index 0000000..7968925
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_speed_dial_48px_clr.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M38,22H26V10c0,-1.1,-0.898,-2,-2,-2l0,0c-1.098,0,-2,0.9,-2,2v12H10c-1.098,0,-2,0.9,-2,2l0,0      c0,1.1,0.902,2,2,2h12v12c0,1.1,0.902,2,2,2l0,0c1.102,0,2,-0.9,2,-2V26h12c1.102,0,2,-0.9,2,-2l0,0C40,22.9,39.102,22,38,22z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M38,21H26V9c0,-1.1,-0.898,-2,-2,-2l0,0c-1.098,0,-2,0.9,-2,2v12H10c-1.098,0,-2,0.9,-2,2l0,0      c0,1.1,0.902,2,2,2h12v12c0,1.1,0.902,2,2,2l0,0c1.102,0,2,-0.9,2,-2V25h12c1.102,0,2,-0.9,2,-2l0,0C40,21.9,39.102,21,38,21z"
+        android:fillAlpha="0.15"
+        android:fillColor="#070707"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_sys_tty_24px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_sys_tty_24px.xml
new file mode 100644
index 0000000..53b32cf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_sys_tty_24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22.1,4.2H1.9C1,4.2,0.2,5,0.2,5.9v12.1c0,1,0.8,1.8,1.8,1.8h20.1c1,0,1.8,-0.8,1.8,-1.8V5.9C23.8,5,23,4.2,22.1,4.2z       M8,9.3H5.5V16H3.9V9.3H1.5V8.1H8V9.3z M15.3,9.3h-2.5v6.6h-1.6V9.3H8.7V8.1h6.5V9.3z M19.7,13.2V16h-1.6v-2.8l-2.7,-5h1.7      l1.7,3.7l1.7,-3.7h1.7L19.7,13.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_travel_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_travel_36px_clr.xml
new file mode 100644
index 0000000..54d2477
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_travel_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M12.964,33l-3.234,-6.728L3,23.035l1.421,-1.422c0.173,-0.168,0.407,-0.233,0.627,-0.188l5.707,0.828                 l5.769,-6.188L4.421,10.226l1.942,-1.939c0.176,-0.176,0.465,-0.28,0.688,-0.23l13.032,3.593l5.87,-5.871                 c1.6,-1.601,3.732,-1.604,4.805,-0.535c1.066,1.071,1.066,3.205,-0.537,4.805l-5.873,5.87l3.592,13.034                 c0.055,0.223,-0.051,0.508,-0.227,0.684l-1.939,1.941l-5.836,-12.1l-6.191,5.766l0.826,5.71c0.049,0.217,-0.016,0.451,-0.184,0.624                 L12.964,33z"
+        android:fillColor="#9C27B0"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_upcoming_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_upcoming_36px_clr.xml
new file mode 100644
index 0000000..33f29ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_upcoming_36px_clr.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M18,5.5c6.893,0,12.5,5.607,12.5,12.5S24.893,30.5,18,30.5S5.5,24.892,5.5,18S11.107,5.5,18,5.5              M18,3C9.715,3,3,9.714,3,18c0,8.285,6.715,15,15,15s15,-6.715,15,-15C33,9.714,26.285,3,18,3L18,3z"
+        android:fillColor="#ECA403"/>
+    <path
+        android:pathData="M24.4,24.248L16,19.2V9h1.699l1.242,9l6.249,5.25L24.4,24.248z"
+        android:fillColor="#ECA403"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_updates_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_updates_36px_clr.xml
new file mode 100644
index 0000000..4c01b8f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_updates_36px_clr.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:pathData="M11,4.307v15.321c6.665,-0.811,13,3.372,20,-0.307V4C24.332,7.997,17.665,3.496,11,4.307"
+        android:fillColor="#FF5722"/>
+    <path
+        android:pathData="M9,31.5C9,32.329,8.328,33,7.5,33l0,0C6.672,33,6,32.329,6,31.5v-27C6,3.671,6.672,3,7.5,3l0,0             C8.328,3,9,3.671,9,4.5V31.5z"
+        android:fillColor="#FF5722"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_vasquette_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_vasquette_36px.xml
new file mode 100644
index 0000000..5501f62
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_vasquette_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,12h6V6H6V12z M6,21h6v-6H6V21z M15,30h6v-6h-6V30z M6,30h6v-6H6V30z M15,12h6V6h-6V12z M24,21h6v-6h-6V21z M24,6v6h6                 V6H24z M15,21h6v-6h-6V21z M24,30h6v-6h-6V30z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_volume_off.xml b/asset-studio/src/main/java/images/material_design_icons/ic_volume_off.xml
new file mode 100644
index 0000000..fbccb29
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_volume_off.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M33,24c0,-3.53 -2.04,-6.58 -5,-8.05v4.42l4.91,4.91c0.06,-0.42 0.09,-0.85 0.09,-1.28zm5,0c0,1.88 -0.41,3.65 -1.08,5.28l3.03,3.03C41.25,29.82 42,27 42,24c0,-8.56 -5.99,-15.72 -14,-17.54v4.13c5.78,1.72 10,7.07 10,13.41zM8.55,6L6,8.55 15.45,18H6v12h8l10,10V26.55l8.51,8.51c-1.34,1.03 -2.85,1.86 -4.51,2.36v4.13c2.75,-0.63 5.26,-1.89 7.37,-3.62L39.45,42 42,39.45l-18,-18L8.55,6zM24,8l-4.18,4.18L24,16.36V8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_add_to_photos_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_add_to_photos_24dp.xml
new file mode 100644
index 0000000..78eaca1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_add_to_photos_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_adjust_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_adjust_24dp.xml
new file mode 100644
index 0000000..14ea87e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_adjust_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zm3,-8c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_assistant_photo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_assistant_photo_24dp.xml
new file mode 100644
index 0000000..581ba88
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_assistant_photo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_audiotrack_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_audiotrack_24dp.xml
new file mode 100644
index 0000000..ac5d072
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_audiotrack_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_circular_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_circular_24dp.xml
new file mode 100644
index 0000000..a29dbd2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_circular_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM7,9.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm3,7c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm-3,-3c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm3,-6c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-1.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm3,6c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-4c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm2,-3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-3.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_linear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_linear_24dp.xml
new file mode 100644
index 0000000..c52651c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_linear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,17.5c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5 -1.5,0.67 -1.5,1.5 0.67,1.5 1.5,1.5zM9,13c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zM3,21h18v-2H3v2zM5,9.5c0.83,0 1.5,-0.67 1.5,-1.5S5.83,6.5 5,6.5 3.5,7.17 3.5,8 4.17,9.5 5,9.5zm0,4c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5 -1.5,0.67 -1.5,1.5 0.67,1.5 1.5,1.5zM9,17c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm8,-0.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM3,3v2h18V3H3zm14,5.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm0,4c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM13,9c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_off_24dp.xml
new file mode 100644
index 0000000..8511153
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm-0.2,4.48l0.2,0.02c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5 -1.5,0.67 -1.5,1.5l0.02,0.2c0.09,0.67 0.61,1.19 1.28,1.28zM14,3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm-4,0c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm11,7c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm8,8c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm-4,13.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM2.5,5.27l3.78,3.78L6,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1c0,-0.1 -0.03,-0.19 -0.06,-0.28l2.81,2.81c-0.71,0.11 -1.25,0.73 -1.25,1.47 0,0.83 0.67,1.5 1.5,1.5 0.74,0 1.36,-0.54 1.47,-1.25l2.81,2.81c-0.09,-0.03 -0.18,-0.06 -0.28,-0.06 -0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1c0,-0.1 -0.03,-0.19 -0.06,-0.28l3.78,3.78L20,20.23 3.77,4 2.5,5.27zM10,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm11,-3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM3,9.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm7,11c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm-3,-3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_on_24dp.xml
new file mode 100644
index 0000000..000f86e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm-3,0.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm15,5.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm-11,10c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm7,7c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-17c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,5.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm8,0.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm3,8.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM14,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm-4,-12c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,8.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm4,-4.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,-4c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_1_24dp.xml
new file mode 100644
index 0000000..cb98cb8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_2_24dp.xml
new file mode 100644
index 0000000..194732f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,2c-1.82,0 -3.53,0.5 -5,1.35C7.99,5.08 10,8.3 10,12s-2.01,6.92 -5,8.65C6.47,21.5 8.18,22 10,22c5.52,0 10,-4.48 10,-10S15.52,2 10,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_3_24dp.xml
new file mode 100644
index 0000000..c2f2587
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,2c-1.05,0 -2.05,0.16 -3,0.46 4.06,1.27 7,5.06 7,9.54 0,4.48 -2.94,8.27 -7,9.54 0.95,0.3 1.95,0.46 3,0.46 5.52,0 10,-4.48 10,-10S14.52,2 9,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_4_24dp.xml
new file mode 100644
index 0000000..92016a1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-0.89,0 -1.74,-0.2 -2.5,-0.55C11.56,16.5 13,14.42 13,12s-1.44,-4.5 -3.5,-5.45C10.26,6.2 11.11,6 12,6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_5_24dp.xml
new file mode 100644
index 0000000..406a52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_6_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_6_24dp.xml
new file mode 100644
index 0000000..cc63bac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_6_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_7_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_7_24dp.xml
new file mode 100644
index 0000000..c254041
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_7_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zm0,-10c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brush_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brush_24dp.xml
new file mode 100644
index 0000000..3e58fe1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brush_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,14c-1.66,0 -3,1.34 -3,3 0,1.31 -1.16,2 -2,2 0.92,1.22 2.49,2 4,2 2.21,0 4,-1.79 4,-4 0,-1.66 -1.34,-3 -3,-3zm13.71,-9.37l-1.34,-1.34c-0.39,-0.39 -1.02,-0.39 -1.41,0L9,12.25 11.75,15l8.96,-8.96c0.39,-0.39 0.39,-1.02 0,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_24dp.xml
new file mode 100644
index 0000000..880b204
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.4,10.5l4.77,-8.26C13.47,2.09 12.75,2 12,2c-2.4,0 -4.6,0.85 -6.32,2.25l3.66,6.35 0.06,-0.1zM21.54,9c-0.92,-2.92 -3.15,-5.26 -6,-6.34L11.88,9h9.66zm0.26,1h-7.49l0.29,0.5 4.76,8.25C21,16.97 22,14.61 22,12c0,-0.69 -0.07,-1.35 -0.2,-2zM8.54,12l-3.9,-6.75C3.01,7.03 2,9.39 2,12c0,0.69 0.07,1.35 0.2,2h7.49l-1.15,-2zm-6.08,3c0.92,2.92 3.15,5.26 6,6.34L12.12,15H2.46zm11.27,0l-3.9,6.76c0.7,0.15 1.42,0.24 2.17,0.24 2.4,0 4.6,-0.85 6.32,-2.25l-3.66,-6.35 -0.93,1.6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_alt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_alt_24dp.xml
new file mode 100644
index 0000000..8404ea5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_alt_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_front_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_front_24dp.xml
new file mode 100644
index 0000000..3c55d50
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_front_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,20H5v2h5v2l3,-3 -3,-3v2zm4,0v2h5v-2h-5zM12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -1.99,0.9 -1.99,2S10.9,8 12,8zm5,-8H7C5.9,0 5,0.9 5,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V2c0,-1.1 -0.9,-2 -2,-2zM7,2h10v10.5c0,-1.67 -3.33,-2.5 -5,-2.5s-5,0.83 -5,2.5V2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_rear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_rear_24dp.xml
new file mode 100644
index 0000000..397bbbd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_rear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,20H5v2h5v2l3,-3 -3,-3v2zm4,0v2h5v-2h-5zm3,-20H7C5.9,0 5,0.9 5,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V2c0,-1.1 -0.9,-2 -2,-2zm-5,6c-1.11,0 -2,-0.9 -2,-2s0.89,-2 1.99,-2 2,0.9 2,2C14,5.1 13.1,6 12,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_roll_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_roll_24dp.xml
new file mode 100644
index 0000000..de29724
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_roll_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,5c0,-1.1 -0.9,-2 -2,-2h-1V2c0,-0.55 -0.45,-1 -1,-1H6c-0.55,0 -1,0.45 -1,1v1H4c-1.1,0 -2,0.9 -2,2v15c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2h8V5h-8zm-2,13h-2v-2h2v2zm0,-9h-2V7h2v2zm4,9h-2v-2h2v2zm0,-9h-2V7h2v2zm4,9h-2v-2h2v2zm0,-9h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_strong_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_strong_24dp.xml
new file mode 100644
index 0000000..81c29e2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_strong_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm-7,7H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_weak_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_weak_24dp.xml
new file mode 100644
index 0000000..1a57582
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_weak_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,15H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,6c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_collections_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_collections_24dp.xml
new file mode 100644
index 0000000..bf9a0b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_collections_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_color_lens_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_color_lens_24dp.xml
new file mode 100644
index 0000000..42e5dc3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_color_lens_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5H16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zm-5.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zm3,-4C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zm3,4c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_colorize_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_colorize_24dp.xml
new file mode 100644
index 0000000..1e95d1d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_colorize_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3,16.25V21h4.75l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,-0.4 0.4,-1.03 0.01,-1.42zM6.92,19L5,17.08l8.06,-8.06 1.92,1.92L6.92,19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_compare_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_compare_24dp.xml
new file mode 100644
index 0000000..cdd3c90
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_compare_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h5v2h2V1h-2v2zm0,15H5l5,-6v6zm9,-15h-5v2h5v13l-5,-6v9h5c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_24dp.xml
new file mode 100644
index 0000000..b378ea8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1,-5C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_duplicate_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_duplicate_24dp.xml
new file mode 100644
index 0000000..167b1f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_duplicate_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,8h-2v3h-3v2h3v3h2v-3h3v-2h-3zM2,12c0,-2.79 1.64,-5.2 4.01,-6.32V3.52C2.52,4.76 0,8.09 0,12s2.52,7.24 6.01,8.48v-2.16C3.64,17.2 2,14.79 2,12zm13,-9c-4.96,0 -9,4.04 -9,9s4.04,9 9,9 9,-4.04 9,-9 -4.04,-9 -9,-9zm0,16c-3.86,0 -7,-3.14 -7,-7s3.14,-7 7,-7 7,3.14 7,7 -3.14,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_16_9_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_16_9_24dp.xml
new file mode 100644
index 0000000..3965d0a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_16_9_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,6H5c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm0,10H5V8h14v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_24dp.xml
new file mode 100644
index 0000000..9c3175a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,15h2V7c0,-1.1 -0.9,-2 -2,-2H9v2h8v8zM7,17V1H5v4H1v2h4v10c0,1.1 0.9,2 2,2h10v4h2v-4h4v-2H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_3_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_3_2_24dp.xml
new file mode 100644
index 0000000..b78df03
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_3_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,4H5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,14H5V6h14v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_5_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_5_4_24dp.xml
new file mode 100644
index 0000000..78bd307
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_5_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,5H5c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm0,12H5V7h14v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_7_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_7_5_24dp.xml
new file mode 100644
index 0000000..dd5b920
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_7_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,7H5c-1.1,0 -2,0.9 -2,2v6c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V9c0,-1.1 -0.9,-2 -2,-2zm0,8H5V9h14v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_din_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_din_24dp.xml
new file mode 100644
index 0000000..987f14c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_din_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_free_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_free_24dp.xml
new file mode 100644
index 0000000..6876754
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_free_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5v4h2V5h4V3H5c-1.1,0 -2,0.9 -2,2zm2,10H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zm14,4h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zm0,-16h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_landscape_24dp.xml
new file mode 100644
index 0000000..78bd307
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,5H5c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm0,12H5V7h14v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_original_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_original_24dp.xml
new file mode 100644
index 0000000..d789be6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_original_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14zm-5.04,-6.71l-2.75,3.54 -1.96,-2.36L6.5,17h11l-3.54,-4.71z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_portrait_24dp.xml
new file mode 100644
index 0000000..1e0e6bd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_square_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_square_24dp.xml
new file mode 100644
index 0000000..2cef016
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_square_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,4H6c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,14H6V6h12v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_dehaze_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_dehaze_24dp.xml
new file mode 100644
index 0000000..88956e4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_dehaze_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,15.5v2h20v-2H2zm0,-5v2h20v-2H2zm0,-5v2h20v-2H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_details_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_details_24dp.xml
new file mode 100644
index 0000000..931e15e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_details_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,4l9,16 9,-16H3zm3.38,2h11.25L12,16 6.38,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_edit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_edit_24dp.xml
new file mode 100644
index 0000000..0ff336a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_edit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_24dp.xml
new file mode 100644
index 0000000..b40b8bd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,17v2h2v-2h2v-2h-2v-2h-2v2h-2v2h2zm5,-15H4c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM5,5h6v2H5V5zm15,15H4L20,4v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_1_24dp.xml
new file mode 100644
index 0000000..bb35968
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,11v2h8v-2H4zm15,7h-2V7.38L14,8.4V6.7L18.7,5h0.3v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_2_24dp.xml
new file mode 100644
index 0000000..b434d4b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.05,16.29l2.86,-3.07c0.38,-0.39 0.72,-0.79 1.04,-1.18 0.32,-0.39 0.59,-0.78 0.82,-1.17 0.23,-0.39 0.41,-0.78 0.54,-1.17s0.19,-0.79 0.19,-1.18c0,-0.53 -0.09,-1.02 -0.27,-1.46 -0.18,-0.44 -0.44,-0.81 -0.78,-1.11 -0.34,-0.31 -0.77,-0.54 -1.26,-0.71 -0.51,-0.16 -1.08,-0.24 -1.72,-0.24 -0.69,0 -1.31,0.11 -1.85,0.32 -0.54,0.21 -1,0.51 -1.36,0.88 -0.37,0.37 -0.65,0.8 -0.84,1.3 -0.18,0.47 -0.27,0.97 -0.28,1.5h2.14c0.01,-0.31 0.05,-0.6 0.13,-0.87 0.09,-0.29 0.23,-0.54 0.4,-0.75 0.18,-0.21 0.41,-0.37 0.68,-0.49 0.27,-0.12 0.6,-0.18 0.96,-0.18 0.31,0 0.58,0.05 0.81,0.15 0.23,0.1 0.43,0.25 0.59,0.43 0.16,0.18 0.28,0.4 0.37,0.65 0.08,0.25 0.13,0.52 0.13,0.81 0,0.22 -0.03,0.43 -0.08,0.65 -0.06,0.22 -0.15,0.45 -0.29,0.7 -0.14,0.25 -0.32,0.53 -0.56,0.83 -0.23,0.3 -0.52,0.65 -0.88,1.03l-4.17,4.55V18H21v-1.71h-5.95zM2,11v2h8v-2H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_1_24dp.xml
new file mode 100644
index 0000000..3ba74f7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,7H8v4H4v2h4v4h2v-4h4v-2h-4V7zm10,11h-2V7.38L15,8.4V6.7L19.7,5h0.3v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_2_24dp.xml
new file mode 100644
index 0000000..97d7424
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.05,16.29l2.86,-3.07c0.38,-0.39 0.72,-0.79 1.04,-1.18 0.32,-0.39 0.59,-0.78 0.82,-1.17 0.23,-0.39 0.41,-0.78 0.54,-1.17 0.13,-0.39 0.19,-0.79 0.19,-1.18 0,-0.53 -0.09,-1.02 -0.27,-1.46 -0.18,-0.44 -0.44,-0.81 -0.78,-1.11 -0.34,-0.31 -0.77,-0.54 -1.26,-0.71 -0.51,-0.16 -1.08,-0.24 -1.72,-0.24 -0.69,0 -1.31,0.11 -1.85,0.32 -0.54,0.21 -1,0.51 -1.36,0.88 -0.37,0.37 -0.65,0.8 -0.84,1.3 -0.18,0.47 -0.27,0.97 -0.28,1.5h2.14c0.01,-0.31 0.05,-0.6 0.13,-0.87 0.09,-0.29 0.23,-0.54 0.4,-0.75 0.18,-0.21 0.41,-0.37 0.68,-0.49 0.27,-0.12 0.6,-0.18 0.96,-0.18 0.31,0 0.58,0.05 0.81,0.15 0.23,0.1 0.43,0.25 0.59,0.43 0.16,0.18 0.28,0.4 0.37,0.65 0.08,0.25 0.13,0.52 0.13,0.81 0,0.22 -0.03,0.43 -0.08,0.65 -0.06,0.22 -0.15,0.45 -0.29,0.7 -0.14,0.25 -0.32,0.53 -0.56,0.83 -0.23,0.3 -0.52,0.65 -0.88,1.03l-4.17,4.55V18H22v-1.71h-5.95zM8,7H6v4H2v2h4v4h2v-4h4v-2H8V7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_zero_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_zero_24dp.xml
new file mode 100644
index 0000000..8ae50ac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_zero_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.14,12.5c0,1 -0.1,1.85 -0.3,2.55 -0.2,0.7 -0.48,1.27 -0.83,1.7 -0.36,0.44 -0.79,0.75 -1.3,0.95 -0.51,0.2 -1.07,0.3 -1.7,0.3 -0.62,0 -1.18,-0.1 -1.69,-0.3 -0.51,-0.2 -0.95,-0.51 -1.31,-0.95 -0.36,-0.44 -0.65,-1.01 -0.85,-1.7 -0.2,-0.7 -0.3,-1.55 -0.3,-2.55v-2.04c0,-1 0.1,-1.85 0.3,-2.55 0.2,-0.7 0.48,-1.26 0.84,-1.69 0.36,-0.43 0.8,-0.74 1.31,-0.93C10.81,5.1 11.38,5 12,5c0.63,0 1.19,0.1 1.7,0.29 0.51,0.19 0.95,0.5 1.31,0.93 0.36,0.43 0.64,0.99 0.84,1.69 0.2,0.7 0.3,1.54 0.3,2.55v2.04zm-2.11,-2.36c0,-0.64 -0.05,-1.18 -0.13,-1.62 -0.09,-0.44 -0.22,-0.79 -0.4,-1.06 -0.17,-0.27 -0.39,-0.46 -0.64,-0.58 -0.25,-0.13 -0.54,-0.19 -0.86,-0.19 -0.32,0 -0.61,0.06 -0.86,0.18s-0.47,0.31 -0.64,0.58c-0.17,0.27 -0.31,0.62 -0.4,1.06s-0.13,0.98 -0.13,1.62v2.67c0,0.64 0.05,1.18 0.14,1.62 0.09,0.45 0.23,0.81 0.4,1.09s0.39,0.48 0.64,0.61 0.54,0.19 0.87,0.19c0.33,0 0.62,-0.06 0.87,-0.19s0.46,-0.33 0.63,-0.61c0.17,-0.28 0.3,-0.64 0.39,-1.09 0.09,-0.45 0.13,-0.99 0.13,-1.62v-2.66z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_1_24dp.xml
new file mode 100644
index 0000000..e108b9e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm11,10h2V5h-4v2h2v8zm7,-14H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_24dp.xml
new file mode 100644
index 0000000..667505c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.96,10.29l-2.75,3.54 -1.96,-2.36L8.5,15h11l-3.54,-4.71zM3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_2_24dp.xml
new file mode 100644
index 0000000..957c4b6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-4,-4h-4v-2h2c1.1,0 2,-0.89 2,-2V7c0,-1.11 -0.9,-2 -2,-2h-4v2h4v2h-2c-1.1,0 -2,0.89 -2,2v4h6v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_3_24dp.xml
new file mode 100644
index 0000000..aaf5f01
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,1H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zM3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm14,8v-1.5c0,-0.83 -0.67,-1.5 -1.5,-1.5 0.83,0 1.5,-0.67 1.5,-1.5V7c0,-1.11 -0.9,-2 -2,-2h-4v2h4v2h-2v2h2v2h-4v2h4c1.1,0 2,-0.89 2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_4_24dp.xml
new file mode 100644
index 0000000..86e0431
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm12,10h2V5h-2v4h-2V5h-2v6h4v4zm6,-14H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_5_24dp.xml
new file mode 100644
index 0000000..5e6a677
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,1H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zM3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm14,8v-2c0,-1.11 -0.9,-2 -2,-2h-2V7h4V5h-6v6h4v2h-4v2h4c1.1,0 2,-0.89 2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_6_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_6_24dp.xml
new file mode 100644
index 0000000..c347253
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_6_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-8,-2h2c1.1,0 2,-0.89 2,-2v-2c0,-1.11 -0.9,-2 -2,-2h-2V7h4V5h-4c-1.1,0 -2,0.89 -2,2v6c0,1.11 0.9,2 2,2zm0,-4h2v2h-2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_7_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_7_24dp.xml
new file mode 100644
index 0000000..0045c2c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_7_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-8,-2l4,-8V5h-6v2h4l-4,8h2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_8_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_8_24dp.xml
new file mode 100644
index 0000000..8916519
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_8_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-8,-2h2c1.1,0 2,-0.89 2,-2v-1.5c0,-0.83 -0.67,-1.5 -1.5,-1.5 0.83,0 1.5,-0.67 1.5,-1.5V7c0,-1.11 -0.9,-2 -2,-2h-2c-1.1,0 -2,0.89 -2,2v1.5c0,0.83 0.67,1.5 1.5,1.5 -0.83,0 -1.5,0.67 -1.5,1.5V13c0,1.11 0.9,2 2,2zm0,-8h2v2h-2V7zm0,4h2v2h-2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_24dp.xml
new file mode 100644
index 0000000..670eb1b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zM15,5h-2c-1.1,0 -2,0.89 -2,2v2c0,1.11 0.9,2 2,2h2v2h-4v2h4c1.1,0 2,-0.89 2,-2V7c0,-1.11 -0.9,-2 -2,-2zm0,4h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_plus_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_plus_24dp.xml
new file mode 100644
index 0000000..8dc1f04
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_plus_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm11,7V8c0,-1.11 -0.9,-2 -2,-2h-1c-1.1,0 -2,0.89 -2,2v1c0,1.11 0.9,2 2,2h1v1H9v2h3c1.1,0 2,-0.89 2,-2zm-3,-3V8h1v1h-1zm10,-8H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,8h-2V7h-2v2h-2v2h2v2h2v-2h2v6H7V3h14v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_b_and_w_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_b_and_w_24dp.xml
new file mode 100644
index 0000000..0fdc812
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_b_and_w_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16l-7,-8v8H5l7,-8V5h7v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_center_focus_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_center_focus_24dp.xml
new file mode 100644
index 0000000..8a185cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_center_focus_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,15H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_drama_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_drama_24dp.xml
new file mode 100644
index 0000000..695c11b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_drama_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.61,5.64 5.36,8.04 2.35,8.36 0,10.9 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4h2c0,-2.76 -1.86,-5.08 -4.4,-5.78C8.61,6.88 10.2,6 12,6c3.03,0 5.5,2.47 5.5,5.5v0.5H19c1.65,0 3,1.35 3,3s-1.35,3 -3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_frames_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_frames_24dp.xml
new file mode 100644
index 0000000..ffcab6a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_frames_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4h-4l-4,-4 -4,4H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,16H4V6h4.52l3.52,-3.5L15.52,6H20v14zM18,8H6v10h12"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_hdr_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_hdr_24dp.xml
new file mode 100644
index 0000000..c3ad94b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_hdr_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,6l-3.75,5 2.85,3.8 -1.6,1.2C9.81,13.75 7,10 7,10l-6,8h22L14,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_none_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_none_24dp.xml
new file mode 100644
index 0000000..b1d1542
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_none_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_tilt_shift_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_tilt_shift_24dp.xml
new file mode 100644
index 0000000..7b4116a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_tilt_shift_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,4.07V2.05c-2.01,0.2 -3.84,1 -5.32,2.21L7.1,5.69c1.11,-0.86 2.44,-1.44 3.9,-1.62zm7.32,0.19C16.84,3.05 15.01,2.25 13,2.05v2.02c1.46,0.18 2.79,0.76 3.9,1.62l1.42,-1.43zM19.93,11h2.02c-0.2,-2.01 -1,-3.84 -2.21,-5.32L18.31,7.1c0.86,1.11 1.44,2.44 1.62,3.9zM5.69,7.1L4.26,5.68C3.05,7.16 2.25,8.99 2.05,11h2.02c0.18,-1.46 0.76,-2.79 1.62,-3.9zM4.07,13H2.05c0.2,2.01 1,3.84 2.21,5.32l1.43,-1.43c-0.86,-1.1 -1.44,-2.43 -1.62,-3.89zM15,12c0,-1.66 -1.34,-3 -3,-3s-3,1.34 -3,3 1.34,3 3,3 3,-1.34 3,-3zm3.31,4.9l1.43,1.43c1.21,-1.48 2.01,-3.32 2.21,-5.32h-2.02c-0.18,1.45 -0.76,2.78 -1.62,3.89zM13,19.93v2.02c2.01,-0.2 3.84,-1 5.32,-2.21l-1.43,-1.43c-1.1,0.86 -2.43,1.44 -3.89,1.62zm-7.32,-0.19C7.16,20.95 9,21.75 11,21.95v-2.02c-1.46,-0.18 -2.79,-0.76 -3.9,-1.62l-1.42,1.43z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_vintage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_vintage_24dp.xml
new file mode 100644
index 0000000..1fdbd6a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_vintage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.7,12.4c-0.28,-0.16 -0.57,-0.29 -0.86,-0.4 0.29,-0.11 0.58,-0.24 0.86,-0.4 1.92,-1.11 2.99,-3.12 3,-5.19 -1.79,-1.03 -4.07,-1.11 -6,0 -0.28,0.16 -0.54,0.35 -0.78,0.54 0.05,-0.31 0.08,-0.63 0.08,-0.95 0,-2.22 -1.21,-4.15 -3,-5.19C10.21,1.85 9,3.78 9,6c0,0.32 0.03,0.64 0.08,0.95 -0.24,-0.2 -0.5,-0.39 -0.78,-0.55 -1.92,-1.11 -4.2,-1.03 -6,0 0,2.07 1.07,4.08 3,5.19 0.28,0.16 0.57,0.29 0.86,0.4 -0.29,0.11 -0.58,0.24 -0.86,0.4 -1.92,1.11 -2.99,3.12 -3,5.19 1.79,1.03 4.07,1.11 6,0 0.28,-0.16 0.54,-0.35 0.78,-0.54 -0.05,0.32 -0.08,0.64 -0.08,0.96 0,2.22 1.21,4.15 3,5.19 1.79,-1.04 3,-2.97 3,-5.19 0,-0.32 -0.03,-0.64 -0.08,-0.95 0.24,0.2 0.5,0.38 0.78,0.54 1.92,1.11 4.2,1.03 6,0 -0.01,-2.07 -1.08,-4.08 -3,-5.19zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flare_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flare_24dp.xml
new file mode 100644
index 0000000..9827247
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flare_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,11H1v2h6v-2zm2.17,-3.24L7.05,5.64 5.64,7.05l2.12,2.12 1.41,-1.41zM13,1h-2v6h2V1zm5.36,6.05l-1.41,-1.41 -2.12,2.12 1.41,1.41 2.12,-2.12zM17,11v2h6v-2h-6zm-5,-2c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm2.83,7.24l2.12,2.12 1.41,-1.41 -2.12,-2.12 -1.41,1.41zm-9.19,0.71l1.41,1.41 2.12,-2.12 -1.41,-1.41 -2.12,2.12zM11,23h2v-6h-2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_auto_24dp.xml
new file mode 100644
index 0000000..e422d4c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,2v12h3v9l7,-12H9l4,-9H3zm16,0h-2l-3.2,9h1.9l0.7,-2h3.2l0.7,2h1.9L19,2zm-2.15,5.65L18,4l1.15,3.65h-2.3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_off_24dp.xml
new file mode 100644
index 0000000..04d719e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.27,3L2,4.27l5,5V13h3v9l3.58,-6.14L17.73,20 19,18.73 3.27,3zM17,10h-4l4,-8H7v2.18l8.46,8.46L17,10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_on_24dp.xml
new file mode 100644
index 0000000..8bf924f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flip_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flip_24dp.xml
new file mode 100644
index 0000000..ef0bbde
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flip_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,21h2v-2h-2v2zm4,-12h2V7h-2v2zM3,5v14c0,1.1 0.9,2 2,2h4v-2H5V5h4V3H5c-1.1,0 -2,0.9 -2,2zm16,-2v2h2c0,-1.1 -0.9,-2 -2,-2zm-8,20h2V1h-2v22zm8,-6h2v-2h-2v2zM15,5h2V3h-2v2zm4,8h2v-2h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_gradient_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_gradient_24dp.xml
new file mode 100644
index 0000000..2d9fb13
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_gradient_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,9h2v2h-2zm-2,2h2v2H9zm4,0h2v2h-2zm2,-2h2v2h-2zM7,9h2v2H7zm12,-6H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,18H7v-2h2v2zm4,0h-2v-2h2v2zm4,0h-2v-2h2v2zm2,-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V5h14v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_grain_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_grain_24dp.xml
new file mode 100644
index 0000000..185e80d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_grain_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,12c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,-8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm-4,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm4,-4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-4,-4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-4,-4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_off_24dp.xml
new file mode 100644
index 0000000..1ab4ca5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,4v1.45l2,2V4h4v4h-3.45l2,2H14v1.45l2,2V10h4v4h-3.45l2,2H20v1.45l2,2V4c0,-1.1 -0.9,-2 -2,-2H4.55l2,2H8zm8,0h4v4h-4V4zM1.27,1.27L0,2.55l2,2V20c0,1.1 0.9,2 2,2h15.46l2,2 1.27,-1.27L1.27,1.27zM10,12.55L11.45,14H10v-1.45zm-6,-6L5.45,8H4V6.55zM8,20H4v-4h4v4zm0,-6H4v-4h3.45l0.55,0.55V14zm6,6h-4v-4h3.45l0.55,0.54V20zm2,0v-1.46L17.46,20H16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_on_24dp.xml
new file mode 100644
index 0000000..6cc5c86
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM8,20H4v-4h4v4zm0,-6H4v-4h4v4zm0,-6H4V4h4v4zm6,12h-4v-4h4v4zm0,-6h-4v-4h4v4zm0,-6h-4V4h4v4zm6,12h-4v-4h4v4zm0,-6h-4v-4h4v4zm0,-6h-4V4h4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_off_24dp.xml
new file mode 100644
index 0000000..dc47ada
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,17L3.27,2.27 2,3.55l4,4V11H4V7H2v10h2v-4h2v4h2V9.55l1,1V17h4c0.67,0 1.26,-0.33 1.62,-0.84l6.34,6.34 1.27,-1.27L18,17zm-5,-2h-2v-2.45l2,2V15zm5,-2h1l0.82,3.27 0.73,0.73H22l-1.19,-4.17c0.7,-0.31 1.19,-1.01 1.19,-1.83V9c0,-1.1 -0.9,-2 -2,-2h-4v5.45l2,2V13zm0,-4h2v2h-2V9zm-3,2.45V9c0,-1.1 -0.9,-2 -2,-2h-2.45L15,11.45z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_on_24dp.xml
new file mode 100644
index 0000000..891f5bd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,11H4V7H2v10h2v-4h2v4h2V7H6v4zm7,-4H9v10h4c1.1,0 2,-0.9 2,-2V9c0,-1.1 -0.9,-2 -2,-2zm0,8h-2V9h2v6zm9,-4V9c0,-1.1 -0.9,-2 -2,-2h-4v10h2v-4h1l1,4h2l-1.19,-4.17c0.7,-0.31 1.19,-1.01 1.19,-1.83zm-2,0h-2V9h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_strong_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_strong_24dp.xml
new file mode 100644
index 0000000..c37d1dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_strong_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM5,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,6c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_weak_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_weak_24dp.xml
new file mode 100644
index 0000000..bd64aee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_weak_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm12,-2c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zm0,10c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_healing_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_healing_24dp.xml
new file mode 100644
index 0000000..a3e5418
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_healing_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.73,12.02l3.98,-3.98c0.39,-0.39 0.39,-1.02 0,-1.41l-4.34,-4.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.98,3.98L8,2.29C7.8,2.1 7.55,2 7.29,2c-0.25,0 -0.51,0.1 -0.7,0.29L2.25,6.63c-0.39,0.39 -0.39,1.02 0,1.41l3.98,3.98L2.25,16c-0.39,0.39 -0.39,1.02 0,1.41l4.34,4.34c0.39,0.39 1.02,0.39 1.41,0l3.98,-3.98 3.98,3.98c0.2,0.2 0.45,0.29 0.71,0.29 0.26,0 0.51,-0.1 0.71,-0.29l4.34,-4.34c0.39,-0.39 0.39,-1.02 0,-1.41l-3.99,-3.98zM12,9c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm-4.71,1.96L3.66,7.34l3.63,-3.63 3.62,3.62 -3.62,3.63zM10,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm2,2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm2,-4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm2.66,9.34l-3.63,-3.62 3.63,-3.63 3.62,3.62 -3.62,3.63z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_image_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_24dp.xml
new file mode 100644
index 0000000..61dcb63
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_image_aspect_ratio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_aspect_ratio_24dp.xml
new file mode 100644
index 0000000..a59eb87
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_aspect_ratio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,10h-2v2h2v-2zm0,4h-2v2h2v-2zm-8,-4H6v2h2v-2zm4,0h-2v2h2v-2zm8,-6H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,14H4V6h16v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_iso_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_iso_24dp.xml
new file mode 100644
index 0000000..33fffdc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_iso_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM5.5,7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5zM19,19H5L19,5v14zm-2,-2v-1.5h-5V17h5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_landscape_24dp.xml
new file mode 100644
index 0000000..c3ad94b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,6l-3.75,5 2.85,3.8 -1.6,1.2C9.81,13.75 7,10 7,10l-6,8h22L14,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_add_24dp.xml
new file mode 100644
index 0000000..f6ada4e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,3H3v3c1.66,0 3,-1.34 3,-3zm8,0h-2c0,4.97 -4.03,9 -9,9v2c6.08,0 11,-4.93 11,-11zm-4,0H8c0,2.76 -2.24,5 -5,5v2c3.87,0 7,-3.13 7,-7zm0,18h2c0,-4.97 4.03,-9 9,-9v-2c-6.07,0 -11,4.93 -11,11zm8,0h3v-3c-1.66,0 -3,1.34 -3,3zm-4,0h2c0,-2.76 2.24,-5 5,-5v-2c-3.87,0 -7,3.13 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_remove_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_remove_24dp.xml
new file mode 100644
index 0000000..1612b52
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_remove_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,3H8c0,0.37 -0.04,0.72 -0.12,1.06l1.59,1.59C9.81,4.84 10,3.94 10,3zM3,4.27l2.84,2.84C5.03,7.67 4.06,8 3,8v2c1.61,0 3.09,-0.55 4.27,-1.46L8.7,9.97C7.14,11.24 5.16,12 3,12v2c2.71,0 5.19,-0.99 7.11,-2.62l2.5,2.5C10.99,15.81 10,18.29 10,21h2c0,-2.16 0.76,-4.14 2.03,-5.69l1.43,1.43C14.55,17.91 14,19.39 14,21h2c0,-1.06 0.33,-2.03 0.89,-2.84L19.73,21 21,19.73 4.27,3 3,4.27zM14,3h-2c0,1.5 -0.37,2.91 -1.02,4.16l1.46,1.46C13.42,6.98 14,5.06 14,3zm5.94,13.12c0.34,-0.08 0.69,-0.12 1.06,-0.12v-2c-0.94,0 -1.84,0.19 -2.66,0.52l1.6,1.6zm-4.56,-4.56l1.46,1.46C18.09,12.37 19.5,12 21,12v-2c-2.06,0 -3.98,0.58 -5.62,1.56z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_lens_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_lens_24dp.xml
new file mode 100644
index 0000000..5093856
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_lens_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_24dp.xml
new file mode 100644
index 0000000..e242ded
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,10c-3.86,0 -7,3.14 -7,7h2c0,-2.76 2.24,-5 5,-5s5,2.24 5,5h2c0,-3.86 -3.14,-7 -7,-7zm0,-4C5.93,6 1,10.93 1,17h2c0,-4.96 4.04,-9 9,-9s9,4.04 9,9h2c0,-6.07 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_3_24dp.xml
new file mode 100644
index 0000000..d7c35c9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.01,3h-14c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,7.5c0,0.83 -0.67,1.5 -1.5,1.5 0.83,0 1.5,0.67 1.5,1.5V15c0,1.11 -0.9,2 -2,2h-4v-2h4v-2h-2v-2h2V9h-4V7h4c1.1,0 2,0.89 2,2v1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_4_24dp.xml
new file mode 100644
index 0000000..d35d6b2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,14h-2v-4H9V7h2v4h2V7h2v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_5_24dp.xml
new file mode 100644
index 0000000..a19ee0b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,6h-4v2h2c1.1,0 2,0.89 2,2v2c0,1.11 -0.9,2 -2,2H9v-2h4v-2H9V7h6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_6_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_6_24dp.xml
new file mode 100644
index 0000000..94eeb9c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_6_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,15h2v-2h-2v2zm8,-12H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,6h-4v2h2c1.1,0 2,0.89 2,2v2c0,1.11 -0.9,2 -2,2h-2c-1.1,0 -2,-0.89 -2,-2V9c0,-1.11 0.9,-2 2,-2h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_one_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_one_24dp.xml
new file mode 100644
index 0000000..1b731ab
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_one_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-5,14h-2V9h-2V7h4v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_two_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_two_24dp.xml
new file mode 100644
index 0000000..96d3dfd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_two_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,8c0,1.11 -0.9,2 -2,2h-2v2h4v2H9v-4c0,-1.11 0.9,-2 2,-2h2V9H9V7h4c1.1,0 2,0.89 2,2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_loupe_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_loupe_24dp.xml
new file mode 100644
index 0000000..7677b3b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_loupe_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1,-5C6.49,2 2,6.49 2,12s4.49,10 10,10h8c1.1,0 2,-0.9 2,-2v-8c0,-5.51 -4.49,-10 -10,-10zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_movie_creation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_movie_creation_24dp.xml
new file mode 100644
index 0000000..6d4c52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_movie_creation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_24dp.xml
new file mode 100644
index 0000000..6239dbe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,16.12c3.47,-0.41 6.17,-3.36 6.17,-6.95 0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7c0,3.47 2.52,6.34 5.83,6.89V20H5v2h14v-2h-6v-3.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_people_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_people_24dp.xml
new file mode 100644
index 0000000..caeb451
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_people_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22.17,9.17c0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7c0,3.47 2.52,6.34 5.83,6.89V20H6v-3h1v-4c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v4h1v5h16v-2h-3v-3.88c3.47,-0.41 6.17,-3.36 6.17,-6.95zM4.5,11c0.83,0 1.5,-0.67 1.5,-1.5S5.33,8 4.5,8 3,8.67 3,9.5 3.67,11 4.5,11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_before_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_before_24dp.xml
new file mode 100644
index 0000000..ed84de6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_before_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_next_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_next_24dp.xml
new file mode 100644
index 0000000..2ff2653
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_next_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_palette_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_palette_24dp.xml
new file mode 100644
index 0000000..42e5dc3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_palette_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5H16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zm-5.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zm3,-4C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zm3,4c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_24dp.xml
new file mode 100644
index 0000000..297abbd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M23,18V6c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2zM8.5,12.5l2.5,3.01L14.5,11l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_fisheye_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_fisheye_24dp.xml
new file mode 100644
index 0000000..671946f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_fisheye_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_horizontal_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_horizontal_24dp.xml
new file mode 100644
index 0000000..7fe1598
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_horizontal_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6.54v10.91c-2.6,-0.77 -5.28,-1.16 -8,-1.16 -2.72,0 -5.4,0.39 -8,1.16V6.54c2.6,0.77 5.28,1.16 8,1.16 2.72,0.01 5.4,-0.38 8,-1.16M21.43,4c-0.1,0 -0.2,0.02 -0.31,0.06C18.18,5.16 15.09,5.7 12,5.7c-3.09,0 -6.18,-0.55 -9.12,-1.64 -0.11,-0.04 -0.22,-0.06 -0.31,-0.06 -0.34,0 -0.57,0.23 -0.57,0.63v14.75c0,0.39 0.23,0.62 0.57,0.62 0.1,0 0.2,-0.02 0.31,-0.06 2.94,-1.1 6.03,-1.64 9.12,-1.64 3.09,0 6.18,0.55 9.12,1.64 0.11,0.04 0.21,0.06 0.31,0.06 0.33,0 0.57,-0.23 0.57,-0.63V4.63c0,-0.4 -0.24,-0.63 -0.57,-0.63z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_vertical_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_vertical_24dp.xml
new file mode 100644
index 0000000..f5282ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_vertical_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.94,21.12c-1.1,-2.94 -1.64,-6.03 -1.64,-9.12 0,-3.09 0.55,-6.18 1.64,-9.12 0.04,-0.11 0.06,-0.22 0.06,-0.31 0,-0.34 -0.23,-0.57 -0.63,-0.57H4.63c-0.4,0 -0.63,0.23 -0.63,0.57 0,0.1 0.02,0.2 0.06,0.31C5.16,5.82 5.71,8.91 5.71,12c0,3.09 -0.55,6.18 -1.64,9.12 -0.05,0.11 -0.07,0.22 -0.07,0.31 0,0.33 0.23,0.57 0.63,0.57h14.75c0.39,0 0.63,-0.24 0.63,-0.57 -0.01,-0.1 -0.03,-0.2 -0.07,-0.31zM6.54,20c0.77,-2.6 1.16,-5.28 1.16,-8 0,-2.72 -0.39,-5.4 -1.16,-8h10.91c-0.77,2.6 -1.16,5.28 -1.16,8 0,2.72 0.39,5.4 1.16,8H6.54z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_wide_angle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_wide_angle_24dp.xml
new file mode 100644
index 0000000..eebf829
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_wide_angle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,6c2.45,0 4.71,0.2 7.29,0.64 0.47,1.78 0.71,3.58 0.71,5.36 0,1.78 -0.24,3.58 -0.71,5.36 -2.58,0.44 -4.84,0.64 -7.29,0.64s-4.71,-0.2 -7.29,-0.64C4.24,15.58 4,13.78 4,12c0,-1.78 0.24,-3.58 0.71,-5.36C7.29,6.2 9.55,6 12,6m0,-2c-2.73,0 -5.22,0.24 -7.95,0.72l-0.93,0.16 -0.25,0.9C2.29,7.85 2,9.93 2,12s0.29,4.15 0.87,6.22l0.25,0.89 0.93,0.16c2.73,0.49 5.22,0.73 7.95,0.73s5.22,-0.24 7.95,-0.72l0.93,-0.16 0.25,-0.89c0.58,-2.08 0.87,-4.16 0.87,-6.23s-0.29,-4.15 -0.87,-6.22l-0.25,-0.89 -0.93,-0.16C17.22,4.24 14.73,4 12,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_24dp.xml
new file mode 100644
index 0000000..61dcb63
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_album_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_album_24dp.xml
new file mode 100644
index 0000000..f8ff7dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_album_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4zm0,15l3,-3.86 2.14,2.58 3,-3.86L18,19H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_camera_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_camera_24dp.xml
new file mode 100644
index 0000000..8404ea5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_camera_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_library_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_library_24dp.xml
new file mode 100644
index 0000000..bf9a0b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_library_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_portrait_24dp.xml
new file mode 100644
index 0000000..dd73aee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12.25c1.24,0 2.25,-1.01 2.25,-2.25S13.24,7.75 12,7.75 9.75,8.76 9.75,10s1.01,2.25 2.25,2.25zm4.5,4c0,-1.5 -3,-2.25 -4.5,-2.25s-4.5,0.75 -4.5,2.25V17h9v-0.75zM19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_remove_red_eye_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_remove_red_eye_24dp.xml
new file mode 100644
index 0000000..46a571c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_remove_red_eye_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_left_24dp.xml
new file mode 100644
index 0000000..3f0a0eb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.11,8.53L5.7,7.11C4.8,8.27 4.24,9.61 4.07,11h2.02c0.14,-0.87 0.49,-1.72 1.02,-2.47zM6.09,13H4.07c0.17,1.39 0.72,2.73 1.62,3.89l1.41,-1.42c-0.52,-0.75 -0.87,-1.59 -1.01,-2.47zm1.01,5.32c1.16,0.9 2.51,1.44 3.9,1.61V17.9c-0.87,-0.15 -1.71,-0.49 -2.46,-1.03L7.1,18.32zM13,4.07V1L8.45,5.55 13,10V6.09c2.84,0.48 5,2.94 5,5.91s-2.16,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93s-3.05,-7.44 -7,-7.93z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_right_24dp.xml
new file mode 100644
index 0000000..d45fea1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.55,5.55L11,1v3.07C7.06,4.56 4,7.92 4,12s3.05,7.44 7,7.93v-2.02c-2.84,-0.48 -5,-2.94 -5,-5.91s2.16,-5.43 5,-5.91V10l4.55,-4.45zM19.93,11c-0.17,-1.39 -0.72,-2.73 -1.62,-3.89l-1.42,1.42c0.54,0.75 0.88,1.6 1.02,2.47h2.02zM13,17.9v2.02c1.39,-0.17 2.74,-0.71 3.9,-1.61l-1.44,-1.44c-0.75,0.54 -1.59,0.89 -2.46,1.03zm3.89,-2.42l1.42,1.41c0.9,-1.16 1.45,-2.5 1.62,-3.89h-2.02c-0.14,0.87 -0.48,1.72 -1.02,2.48z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_slideshow_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_slideshow_24dp.xml
new file mode 100644
index 0000000..9b26770
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_slideshow_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,8v8l5,-4 -5,-4zm9,-5H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_straighten_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_straighten_24dp.xml
new file mode 100644
index 0000000..f10b101
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_straighten_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,6H3c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm0,10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_style_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_style_24dp.xml
new file mode 100644
index 0000000..14d351e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_style_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2.53,19.65l1.34,0.56v-9.03l-2.43,5.86c-0.41,1.02 0.08,2.19 1.09,2.61zm19.5,-3.7L17.07,3.98c-0.31,-0.75 -1.04,-1.21 -1.81,-1.23 -0.26,0 -0.53,0.04 -0.79,0.15L7.1,5.95c-0.75,0.31 -1.21,1.03 -1.23,1.8 -0.01,0.27 0.04,0.54 0.15,0.8l4.96,11.97c0.31,0.76 1.05,1.22 1.83,1.23 0.26,0 0.52,-0.05 0.77,-0.15l7.36,-3.05c1.02,-0.42 1.51,-1.59 1.09,-2.6zM7.88,8.75c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm-2,11c0,1.1 0.9,2 2,2h1.45l-3.45,-8.34v6.34z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_camera_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_camera_24dp.xml
new file mode 100644
index 0000000..f36ba70
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_camera_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4h-3.17L15,2H9L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-5,11.5V13H9v2.5L5.5,12 9,8.5V11h6V8.5l3.5,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_video_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_video_24dp.xml
new file mode 100644
index 0000000..b29205f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_video_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,9.5V6c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h14c0.55,0 1,-0.45 1,-1v-3.5l4,4v-13l-4,4zm-5,6V13H7v2.5L3.5,12 7,8.5V11h6V8.5l3.5,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_tag_faces_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_tag_faces_24dp.xml
new file mode 100644
index 0000000..1ef868a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_tag_faces_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_texture_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_texture_24dp.xml
new file mode 100644
index 0000000..098a5a5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_texture_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.51,3.08L3.08,19.51c0.09,0.34 0.27,0.65 0.51,0.9 0.25,0.24 0.56,0.42 0.9,0.51L20.93,4.49c-0.19,-0.69 -0.73,-1.23 -1.42,-1.41zM11.88,3L3,11.88v2.83L14.71,3h-2.83zM5,3c-1.1,0 -2,0.9 -2,2v2l4,-4H5zm14,18c0.55,0 1.05,-0.22 1.41,-0.59 0.37,-0.36 0.59,-0.86 0.59,-1.41v-2l-4,4h2zm-9.71,0h2.83L21,12.12V9.29L9.29,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timelapse_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timelapse_24dp.xml
new file mode 100644
index 0000000..20d4ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timelapse_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.24,7.76C15.07,6.59 13.54,6 12,6v6l-4.24,4.24c2.34,2.34 6.14,2.34 8.49,0 2.34,-2.34 2.34,-6.14 -0.01,-8.48zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_10_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_10_24dp.xml
new file mode 100644
index 0000000..ce73d5d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_10_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0,7.72V9.4l3,-1V18h2V6h-0.25L0,7.72zm23.78,6.65c-0.14,-0.28 -0.35,-0.53 -0.63,-0.74 -0.28,-0.21 -0.61,-0.39 -1.01,-0.53s-0.85,-0.27 -1.35,-0.38c-0.35,-0.07 -0.64,-0.15 -0.87,-0.23 -0.23,-0.08 -0.41,-0.16 -0.55,-0.25 -0.14,-0.09 -0.23,-0.19 -0.28,-0.3 -0.05,-0.11 -0.08,-0.24 -0.08,-0.39 0,-0.14 0.03,-0.28 0.09,-0.41 0.06,-0.13 0.15,-0.25 0.27,-0.34 0.12,-0.1 0.27,-0.18 0.45,-0.24s0.4,-0.09 0.64,-0.09c0.25,0 0.47,0.04 0.66,0.11 0.19,0.07 0.35,0.17 0.48,0.29 0.13,0.12 0.22,0.26 0.29,0.42 0.06,0.16 0.1,0.32 0.1,0.49h1.95c0,-0.39 -0.08,-0.75 -0.24,-1.09 -0.16,-0.34 -0.39,-0.63 -0.69,-0.88 -0.3,-0.25 -0.66,-0.44 -1.09,-0.59C21.49,9.07 21,9 20.46,9c-0.51,0 -0.98,0.07 -1.39,0.21 -0.41,0.14 -0.77,0.33 -1.06,0.57 -0.29,0.24 -0.51,0.52 -0.67,0.84 -0.16,0.32 -0.23,0.65 -0.23,1.01s0.08,0.69 0.23,0.96c0.15,0.28 0.36,0.52 0.64,0.73 0.27,0.21 0.6,0.38 0.98,0.53 0.38,0.14 0.81,0.26 1.27,0.36 0.39,0.08 0.71,0.17 0.95,0.26s0.43,0.19 0.57,0.29c0.13,0.1 0.22,0.22 0.27,0.34 0.05,0.12 0.07,0.25 0.07,0.39 0,0.32 -0.13,0.57 -0.4,0.77 -0.27,0.2 -0.66,0.29 -1.17,0.29 -0.22,0 -0.43,-0.02 -0.64,-0.08 -0.21,-0.05 -0.4,-0.13 -0.56,-0.24 -0.17,-0.11 -0.3,-0.26 -0.41,-0.44 -0.11,-0.18 -0.17,-0.41 -0.18,-0.67h-1.89c0,0.36 0.08,0.71 0.24,1.05 0.16,0.34 0.39,0.65 0.7,0.93 0.31,0.27 0.69,0.49 1.15,0.66 0.46,0.17 0.98,0.25 1.58,0.25 0.53,0 1.01,-0.06 1.44,-0.19 0.43,-0.13 0.8,-0.31 1.11,-0.54 0.31,-0.23 0.54,-0.51 0.71,-0.83 0.17,-0.32 0.25,-0.67 0.25,-1.06 -0.02,-0.4 -0.09,-0.74 -0.24,-1.02zm-9.96,-7.32c-0.34,-0.4 -0.75,-0.7 -1.23,-0.88 -0.47,-0.18 -1.01,-0.27 -1.59,-0.27 -0.58,0 -1.11,0.09 -1.59,0.27 -0.48,0.18 -0.89,0.47 -1.23,0.88 -0.34,0.41 -0.6,0.93 -0.79,1.59 -0.18,0.65 -0.28,1.45 -0.28,2.39v1.92c0,0.94 0.09,1.74 0.28,2.39 0.19,0.66 0.45,1.19 0.8,1.6 0.34,0.41 0.75,0.71 1.23,0.89 0.48,0.18 1.01,0.28 1.59,0.28 0.59,0 1.12,-0.09 1.59,-0.28 0.48,-0.18 0.88,-0.48 1.22,-0.89 0.34,-0.41 0.6,-0.94 0.78,-1.6 0.18,-0.65 0.28,-1.45 0.28,-2.39v-1.92c0,-0.94 -0.09,-1.74 -0.28,-2.39 -0.18,-0.66 -0.44,-1.19 -0.78,-1.59zm-0.92,6.17c0,0.6 -0.04,1.11 -0.12,1.53 -0.08,0.42 -0.2,0.76 -0.36,1.02 -0.16,0.26 -0.36,0.45 -0.59,0.57 -0.23,0.12 -0.51,0.18 -0.82,0.18 -0.3,0 -0.58,-0.06 -0.82,-0.18s-0.44,-0.31 -0.6,-0.57c-0.16,-0.26 -0.29,-0.6 -0.38,-1.02 -0.09,-0.42 -0.13,-0.93 -0.13,-1.53v-2.5c0,-0.6 0.04,-1.11 0.13,-1.52 0.09,-0.41 0.21,-0.74 0.38,-1 0.16,-0.25 0.36,-0.43 0.6,-0.55 0.24,-0.11 0.51,-0.17 0.81,-0.17 0.31,0 0.58,0.06 0.81,0.17 0.24,0.11 0.44,0.29 0.6,0.55 0.16,0.25 0.29,0.58 0.37,0.99 0.08,0.41 0.13,0.92 0.13,1.52v2.51z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_24dp.xml
new file mode 100644
index 0000000..0f1425c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,1H9v2h6V1zm-4,13h2V8h-2v6zm8.03,-6.61l1.42,-1.42c-0.43,-0.51 -0.9,-0.99 -1.41,-1.41l-1.42,1.42C16.07,4.74 14.12,4 12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9 9,-4.03 9,-9c0,-2.12 -0.74,-4.07 -1.97,-5.61zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_3_24dp.xml
new file mode 100644
index 0000000..a9b95fe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.61,12.97c-0.16,-0.24 -0.36,-0.46 -0.62,-0.65 -0.25,-0.19 -0.56,-0.35 -0.93,-0.48 0.3,-0.14 0.57,-0.3 0.8,-0.5 0.23,-0.2 0.42,-0.41 0.57,-0.64 0.15,-0.23 0.27,-0.46 0.34,-0.71 0.08,-0.24 0.11,-0.49 0.11,-0.73 0,-0.55 -0.09,-1.04 -0.28,-1.46 -0.18,-0.42 -0.44,-0.77 -0.78,-1.06 -0.33,-0.28 -0.73,-0.5 -1.2,-0.64 -0.45,-0.13 -0.97,-0.2 -1.53,-0.2 -0.55,0 -1.06,0.08 -1.52,0.24 -0.47,0.17 -0.87,0.4 -1.2,0.69 -0.33,0.29 -0.6,0.63 -0.78,1.03 -0.2,0.39 -0.29,0.83 -0.29,1.29h1.98c0,-0.26 0.05,-0.49 0.14,-0.69 0.09,-0.2 0.22,-0.38 0.38,-0.52 0.17,-0.14 0.36,-0.25 0.58,-0.33 0.22,-0.08 0.46,-0.12 0.73,-0.12 0.61,0 1.06,0.16 1.36,0.47 0.3,0.31 0.44,0.75 0.44,1.32 0,0.27 -0.04,0.52 -0.12,0.74 -0.08,0.22 -0.21,0.41 -0.38,0.57 -0.17,0.16 -0.38,0.28 -0.63,0.37 -0.25,0.09 -0.55,0.13 -0.89,0.13H6.72v1.57H7.9c0.34,0 0.64,0.04 0.91,0.11 0.27,0.08 0.5,0.19 0.69,0.35 0.19,0.16 0.34,0.36 0.44,0.61 0.1,0.24 0.16,0.54 0.16,0.87 0,0.62 -0.18,1.09 -0.53,1.42 -0.35,0.33 -0.84,0.49 -1.45,0.49 -0.29,0 -0.56,-0.04 -0.8,-0.13 -0.24,-0.08 -0.44,-0.2 -0.61,-0.36 -0.17,-0.16 -0.3,-0.34 -0.39,-0.56 -0.09,-0.22 -0.14,-0.46 -0.14,-0.72H4.19c0,0.55 0.11,1.03 0.32,1.45 0.21,0.42 0.5,0.77 0.86,1.05s0.77,0.49 1.24,0.63 0.96,0.21 1.48,0.21c0.57,0 1.09,-0.08 1.58,-0.23 0.49,-0.15 0.91,-0.38 1.26,-0.68 0.36,-0.3 0.64,-0.66 0.84,-1.1 0.2,-0.43 0.3,-0.93 0.3,-1.48 0,-0.29 -0.04,-0.58 -0.11,-0.86 -0.08,-0.25 -0.19,-0.51 -0.35,-0.76zm9.26,1.4c-0.14,-0.28 -0.35,-0.53 -0.63,-0.74 -0.28,-0.21 -0.61,-0.39 -1.01,-0.53s-0.85,-0.27 -1.35,-0.38c-0.35,-0.07 -0.64,-0.15 -0.87,-0.23 -0.23,-0.08 -0.41,-0.16 -0.55,-0.25 -0.14,-0.09 -0.23,-0.19 -0.28,-0.3 -0.05,-0.11 -0.08,-0.24 -0.08,-0.39s0.03,-0.28 0.09,-0.41c0.06,-0.13 0.15,-0.25 0.27,-0.34 0.12,-0.1 0.27,-0.18 0.45,-0.24s0.4,-0.09 0.64,-0.09c0.25,0 0.47,0.04 0.66,0.11 0.19,0.07 0.35,0.17 0.48,0.29 0.13,0.12 0.22,0.26 0.29,0.42 0.06,0.16 0.1,0.32 0.1,0.49h1.95c0,-0.39 -0.08,-0.75 -0.24,-1.09 -0.16,-0.34 -0.39,-0.63 -0.69,-0.88 -0.3,-0.25 -0.66,-0.44 -1.09,-0.59 -0.43,-0.15 -0.92,-0.22 -1.46,-0.22 -0.51,0 -0.98,0.07 -1.39,0.21 -0.41,0.14 -0.77,0.33 -1.06,0.57 -0.29,0.24 -0.51,0.52 -0.67,0.84 -0.16,0.32 -0.23,0.65 -0.23,1.01s0.08,0.68 0.23,0.96c0.15,0.28 0.37,0.52 0.64,0.73 0.27,0.21 0.6,0.38 0.98,0.53 0.38,0.14 0.81,0.26 1.27,0.36 0.39,0.08 0.71,0.17 0.95,0.26s0.43,0.19 0.57,0.29c0.13,0.1 0.22,0.22 0.27,0.34 0.05,0.12 0.07,0.25 0.07,0.39 0,0.32 -0.13,0.57 -0.4,0.77 -0.27,0.2 -0.66,0.29 -1.17,0.29 -0.22,0 -0.43,-0.02 -0.64,-0.08 -0.21,-0.05 -0.4,-0.13 -0.56,-0.24 -0.17,-0.11 -0.3,-0.26 -0.41,-0.44 -0.11,-0.18 -0.17,-0.41 -0.18,-0.67h-1.89c0,0.36 0.08,0.71 0.24,1.05 0.16,0.34 0.39,0.65 0.7,0.93 0.31,0.27 0.69,0.49 1.15,0.66 0.46,0.17 0.98,0.25 1.58,0.25 0.53,0 1.01,-0.06 1.44,-0.19 0.43,-0.13 0.8,-0.31 1.11,-0.54 0.31,-0.23 0.54,-0.51 0.71,-0.83 0.17,-0.32 0.25,-0.67 0.25,-1.06 -0.02,-0.4 -0.09,-0.74 -0.24,-1.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_auto_24dp.xml
new file mode 100644
index 0000000..25160fe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,10c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.67 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_off_24dp.xml
new file mode 100644
index 0000000..9b58d048
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.04,4.55l-1.42,1.42C16.07,4.74 14.12,4 12,4c-1.83,0 -3.53,0.55 -4.95,1.48l1.46,1.46C9.53,6.35 10.73,6 12,6c3.87,0 7,3.13 7,7 0,1.27 -0.35,2.47 -0.94,3.49l1.45,1.45C20.45,16.53 21,14.83 21,13c0,-2.12 -0.74,-4.07 -1.97,-5.61l1.42,-1.42 -1.41,-1.42zM15,1H9v2h6V1zm-4,8.44l2,2V8h-2v1.44zM3.02,4L1.75,5.27 4.5,8.03C3.55,9.45 3,11.16 3,13c0,4.97 4.02,9 9,9 1.84,0 3.55,-0.55 4.98,-1.5l2.5,2.5 1.27,-1.27 -7.71,-7.71L3.02,4zM12,20c-3.87,0 -7,-3.13 -7,-7 0,-1.28 0.35,-2.48 0.95,-3.52l9.56,9.56c-1.03,0.61 -2.23,0.96 -3.51,0.96z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_tonality_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_tonality_24dp.xml
new file mode 100644
index 0000000..ece3cb2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_tonality_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-1,17.93c-3.94,-0.49 -7,-3.85 -7,-7.93s3.05,-7.44 7,-7.93v15.86zm2,-15.86c1.03,0.13 2,0.45 2.87,0.93H13v-0.93zM13,7h5.24c0.25,0.31 0.48,0.65 0.68,1H13V7zm0,3h6.74c0.08,0.33 0.15,0.66 0.19,1H13v-1zm0,9.93V19h2.87c-0.87,0.48 -1.84,0.8 -2.87,0.93zM18.24,17H13v-1h5.92c-0.2,0.35 -0.43,0.69 -0.68,1zm1.5,-3H13v-1h6.93c-0.04,0.34 -0.11,0.67 -0.19,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_transform_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_transform_24dp.xml
new file mode 100644
index 0000000..ac407b7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_transform_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,18v-2H8V4h2L7,1 4,4h2v2H2v2h4v8c0,1.1 0.9,2 2,2h8v2h-2l3,3 3,-3h-2v-2h4zM10,8h6v6h2V8c0,-1.1 -0.9,-2 -2,-2h-6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_tune_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_tune_24dp.xml
new file mode 100644
index 0000000..553b593
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_tune_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17v2h6v-2H3zM3,5v2h10V5H3zm10,16v-2h8v-2h-8v-2h-2v6h2zM7,9v2H3v2h4v2h2V9H7zm14,4v-2H11v2h10zm-6,-4h2V7h4V5h-4V3h-2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_auto_24dp.xml
new file mode 100644
index 0000000..15887e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.85,12.65h2.3L8,9l-1.15,3.65zM22,7l-1.2,6.29L19.3,7h-1.6l-1.49,6.29L15,7h-0.76C12.77,5.17 10.53,4 8,4c-4.42,0 -8,3.58 -8,8s3.58,8 8,8c3.13,0 5.84,-1.81 7.15,-4.43l0.1,0.43H17l1.5,-6.1L20,16h1.75l2.05,-9H22zm-11.7,9l-0.7,-2H6.4l-0.7,2H3.8L7,7h2l3.2,9h-1.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_cloudy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_cloudy_24dp.xml
new file mode 100644
index 0000000..3502765
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_cloudy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.36,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.64,-4.96z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_incandescent_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_incandescent_24dp.xml
new file mode 100644
index 0000000..e82fd89
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_incandescent_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8zM11,22.45h2V19.5h-2v2.95zM4,10.5H1v2h3v-2zm11,-4.19V1.5H9v4.81C7.21,7.35 6,9.28 6,11.5c0,3.31 2.69,6 6,6s6,-2.69 6,-6c0,-2.22 -1.21,-4.15 -3,-5.19zm5,4.19v2h3v-2h-3zm-2.76,7.66l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_irradescent_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_irradescent_24dp.xml
new file mode 100644
index 0000000..a943060
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_irradescent_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,14.5h14v-6H5v6zM11,0.55V3.5h2V0.55h-2zm8.04,2.5l-1.79,1.79 1.41,1.41 1.8,-1.79 -1.42,-1.41zM13,22.45V19.5h-2v2.95h2zm7.45,-3.91l-1.8,-1.79 -1.41,1.41 1.79,1.8 1.42,-1.42zM3.55,4.46l1.79,1.79 1.41,-1.41 -1.79,-1.79 -1.41,1.41zm1.41,15.49l1.79,-1.8 -1.41,-1.41 -1.79,1.79 1.41,1.42z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_sunny_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_sunny_24dp.xml
new file mode 100644
index 0000000..b8c40fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_sunny_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5H1v2h3v-2zm9,-9.95h-2V3.5h2V0.55zm7.45,3.91l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zm-3.21,13.7l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zm-8,-5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zm-1,16.95h2V19.5h-2v2.95zm-7.45,-3.91l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_beenhere_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_beenhere_24dp.xml
new file mode 100644
index 0000000..bd622df
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_beenhere_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,1H5c-1.1,0 -1.99,0.9 -1.99,2L3,15.93c0,0.69 0.35,1.3 0.88,1.66L12,23l8.11,-5.41c0.53,-0.36 0.88,-0.97 0.88,-1.66L21,3c0,-1.1 -0.9,-2 -2,-2zm-9,15l-5,-5 1.41,-1.41L10,13.17l7.59,-7.59L19,7l-9,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_24dp.xml
new file mode 100644
index 0000000..ced648c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21.71,11.29l-9,-9c-0.39,-0.39 -1.02,-0.39 -1.41,0l-9,9c-0.39,0.39 -0.39,1.02 0,1.41l9,9c0.39,0.39 1.02,0.39 1.41,0l9,-9c0.39,-0.38 0.39,-1.01 0,-1.41zM14,14.5V12h-4v3H8v-4c0,-0.55 0.45,-1 1,-1h5V7.5l3.5,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bike_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bike_24dp.xml
new file mode 100644
index 0000000..d7c0992
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bike_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,4.8c0.99,0 1.8,-0.81 1.8,-1.8s-0.81,-1.8 -1.8,-1.8c-1,0 -1.8,0.81 -1.8,1.8S15,4.8 16,4.8zm3,7.2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zm0,8.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5zM14.8,10H19V8.2h-3.2l-1.93,-3.27c-0.3,-0.5 -0.84,-0.83 -1.46,-0.83 -0.47,0 -0.89,0.19 -1.2,0.5l-3.7,3.7c-0.32,0.3 -0.51,0.73 -0.51,1.2 0,0.63 0.33,1.16 0.85,1.47L11.2,13v5H13v-6.48l-2.25,-1.67 2.32,-2.33L14.8,10zM5,12c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zm0,8.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bus_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bus_24dp.xml
new file mode 100644
index 0000000..ca91981
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bus_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,16c0,0.88 0.39,1.67 1,2.22V20c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h8v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1.78c0.61,-0.55 1,-1.34 1,-2.22V6c0,-3.5 -3.58,-4 -8,-4s-8,0.5 -8,4v10zm3.5,1c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zm9,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm1.5,-6H6V6h12v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_car_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_car_24dp.xml
new file mode 100644
index 0000000..252a94f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_car_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_ferry_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_ferry_24dp.xml
new file mode 100644
index 0000000..c304e8a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_ferry_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,21c-1.39,0 -2.78,-0.47 -4,-1.32 -2.44,1.71 -5.56,1.71 -8,0C6.78,20.53 5.39,21 4,21H2v2h2c1.38,0 2.74,-0.35 4,-0.99 2.52,1.29 5.48,1.29 8,0 1.26,0.65 2.62,0.99 4,0.99h2v-2h-2zM3.95,19H4c1.6,0 3.02,-0.88 4,-2 0.98,1.12 2.4,2 4,2s3.02,-0.88 4,-2c0.98,1.12 2.4,2 4,2h0.05l1.89,-6.68c0.08,-0.26 0.06,-0.54 -0.06,-0.78s-0.34,-0.42 -0.6,-0.5L20,10.62V6c0,-1.1 -0.9,-2 -2,-2h-3V1H9v3H6c-1.1,0 -2,0.9 -2,2v4.62l-1.29,0.42c-0.26,0.08 -0.48,0.26 -0.6,0.5s-0.15,0.52 -0.06,0.78L3.95,19zM6,6h12v3.97L12,8 6,9.97V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_subway_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_subway_24dp.xml
new file mode 100644
index 0000000..a40cea8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_subway_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2c-4.42,0 -8,0.5 -8,4v9.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h12v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5V6c0,-3.5 -3.58,-4 -8,-4zM7.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zm3.5,-6H6V6h5v5zm5.5,6c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm1.5,-6h-5V6h5v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_train_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_train_24dp.xml
new file mode 100644
index 0000000..a029014
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_train_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,15.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h12v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5V5c0,-3.5 -3.58,-4 -8,-4s-8,0.5 -8,4v10.5zm8,1.5c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zm6,-7H6V5h12v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_transit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_transit_24dp.xml
new file mode 100644
index 0000000..a40cea8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_transit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2c-4.42,0 -8,0.5 -8,4v9.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h12v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5V6c0,-3.5 -3.58,-4 -8,-4zM7.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zm3.5,-6H6V6h5v5zm5.5,6c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm1.5,-6h-5V6h5v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_walk_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_walk_24dp.xml
new file mode 100644
index 0000000..3b92462
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_walk_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,3.8c0.99,0 1.8,-0.81 1.8,-1.8 0,-1 -0.81,-1.8 -1.8,-1.8 -1,0 -1.8,0.81 -1.8,1.8S13,3.8 14,3.8zm0.12,6.2H19V8.2h-3.62l-2,-3.33c-0.3,-0.5 -0.84,-0.83 -1.46,-0.83 -0.17,0 -0.34,0.03 -0.49,0.07L6,5.8V11h1.8V7.33l2.11,-0.66L6,22h1.8l2.87,-8.11L13,17v5h1.8v-6.41l-2.49,-4.54 0.73,-2.87L14.12,10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_flight_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_flight_24dp.xml
new file mode 100644
index 0000000..3aa5af7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_flight_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.18,9"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_hotel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_hotel_24dp.xml
new file mode 100644
index 0000000..3d2e1a0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_hotel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,13c1.66,0 3,-1.34 3,-3S8.66,7 7,7s-3,1.34 -3,3 1.34,3 3,3zm12,-6h-8v7H3V5H1v15h2v-3h18v3h2v-9c0,-2.21 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_24dp.xml
new file mode 100644
index 0000000..430ec18
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_clear_24dp.xml
new file mode 100644
index 0000000..8516033
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.81,14.99l1.19,-0.92 -1.43,-1.43 -1.19,0.92 1.43,1.43zm-0.45,-4.72L21,9l-9,-7 -2.91,2.27 7.87,7.88 2.4,-1.88zM3.27,1L2,2.27l4.22,4.22L3,9l1.63,1.27L12,16l2.1,-1.63 1.43,1.43L12,18.54l-7.37,-5.73L3,14.07l9,7 4.95,-3.85L20.73,21 22,19.73 3.27,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_airport_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_airport_24dp.xml
new file mode 100644
index 0000000..c63e2de
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_airport_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_atm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_atm_24dp.xml
new file mode 100644
index 0000000..eb1dc04
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_atm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,17h2v-1h1c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1h-3v-1h4V8h-2V7h-2v1h-1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1h3v1H9v2h2v1zm9,-13H4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6c0,-1.11 -0.89,-2 -2,-2zm0,14H4V6h16v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_attraction_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_attraction_24dp.xml
new file mode 100644
index 0000000..a75c432
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_attraction_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,12c0,-1.1 0.9,-2 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -1.99,0.9 -1.99,2v4c1.1,0 1.99,0.9 1.99,2s-0.89,2 -2,2v4c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-4c-1.1,0 -2,-0.9 -2,-2zm-4.42,4.8L12,14.5l-3.58,2.3 1.08,-4.12 -3.29,-2.69 4.24,-0.25L12,5.8l1.54,3.95 4.24,0.25 -3.29,2.69 1.09,4.11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_bar_24dp.xml
new file mode 100644
index 0000000..e6eba39
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,13v6H6v2h12v-2h-5v-6l8,-8V3H3v2l8,8zM7.5,7l-2,-2h13l-2,2h-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_cafe_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_cafe_24dp.xml
new file mode 100644
index 0000000..1b0a6d6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_cafe_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,3H4v10c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.11,0 2,-0.89 2,-2V5c0,-1.11 -0.89,-2 -2,-2zm0,5h-2V5h2v3zM2,21h18v-2H2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_car_wash_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_car_wash_24dp.xml
new file mode 100644
index 0000000..f94b7ff
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_car_wash_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,5c0.83,0 1.5,-0.67 1.5,-1.5 0,-1 -1.5,-2.7 -1.5,-2.7s-1.5,1.7 -1.5,2.7c0,0.83 0.67,1.5 1.5,1.5zm-5,0c0.83,0 1.5,-0.67 1.5,-1.5 0,-1 -1.5,-2.7 -1.5,-2.7s-1.5,1.7 -1.5,2.7c0,0.83 0.67,1.5 1.5,1.5zM7,5c0.83,0 1.5,-0.67 1.5,-1.5C8.5,2.5 7,0.8 7,0.8S5.5,2.5 5.5,3.5C5.5,4.33 6.17,5 7,5zm11.92,3.01C18.72,7.42 18.16,7 17.5,7h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,14v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,18c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,15 6.5,15s1.5,0.67 1.5,1.5S7.33,18 6.5,18zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,13l1.5,-4.5h11L19,13H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_convenience_store_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_convenience_store_24dp.xml
new file mode 100644
index 0000000..b1259d2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_convenience_store_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,7V4H5v3H2v13h8v-4h4v4h8V7h-3zm-8,3H9v1h2v1H8V9h2V8H8V7h3v3zm5,2h-1v-2h-2V7h1v2h1V7h1v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_drink_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_drink_24dp.xml
new file mode 100644
index 0000000..36a980a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_drink_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,2l2.01,18.23C5.13,21.23 5.97,22 7,22h10c1.03,0 1.87,-0.77 1.99,-1.77L21,2H3zm9,17c-1.66,0 -3,-1.34 -3,-3 0,-2 3,-5.4 3,-5.4s3,3.4 3,5.4c0,1.66 -1.34,3 -3,3zm6.33,-11H5.67l-0.44,-4h13.53l-0.43,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_florist_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_florist_24dp.xml
new file mode 100644
index 0000000..0932396
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_florist_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,22c4.97,0 9,-4.03 9,-9 -4.97,0 -9,4.03 -9,9zM5.6,10.25c0,1.38 1.12,2.5 2.5,2.5 0.53,0 1.01,-0.16 1.42,-0.44l-0.02,0.19c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5l-0.02,-0.19c0.4,0.28 0.89,0.44 1.42,0.44 1.38,0 2.5,-1.12 2.5,-2.5 0,-1 -0.59,-1.85 -1.43,-2.25 0.84,-0.4 1.43,-1.25 1.43,-2.25 0,-1.38 -1.12,-2.5 -2.5,-2.5 -0.53,0 -1.01,0.16 -1.42,0.44l0.02,-0.19C14.5,2.12 13.38,1 12,1S9.5,2.12 9.5,3.5l0.02,0.19c-0.4,-0.28 -0.89,-0.44 -1.42,-0.44 -1.38,0 -2.5,1.12 -2.5,2.5 0,1 0.59,1.85 1.43,2.25 -0.84,0.4 -1.43,1.25 -1.43,2.25zM12,5.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5S9.5,9.38 9.5,8s1.12,-2.5 2.5,-2.5zM3,13c0,4.97 4.03,9 9,9 0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_gas_station_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_gas_station_24dp.xml
new file mode 100644
index 0000000..9c10a2c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_gas_station_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.77,7.23l0.01,-0.01 -3.72,-3.72L15,4.56l2.11,2.11c-0.94,0.36 -1.61,1.26 -1.61,2.33 0,1.38 1.12,2.5 2.5,2.5 0.36,0 0.69,-0.08 1,-0.21v7.21c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V14c0,-1.1 -0.9,-2 -2,-2h-1V5c0,-1.1 -0.9,-2 -2,-2H6c-1.1,0 -2,0.9 -2,2v16h10v-7.5h1.5v5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V9c0,-0.69 -0.28,-1.32 -0.73,-1.77zM12,10H6V5h6v5zm6,0c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_grocery_store_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_grocery_store_24dp.xml
new file mode 100644
index 0000000..f28015e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_grocery_store_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1H5.21l-0.94,-2H1zm16,16c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hospital_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hospital_24dp.xml
new file mode 100644
index 0000000..5ae2b34
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hospital_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-1,11h-4v4h-4v-4H6v-4h4V6h4v4h4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hotel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hotel_24dp.xml
new file mode 100644
index 0000000..3d2e1a0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hotel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,13c1.66,0 3,-1.34 3,-3S8.66,7 7,7s-3,1.34 -3,3 1.34,3 3,3zm12,-6h-8v7H3V5H1v15h2v-3h18v3h2v-9c0,-2.21 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_laundry_service_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_laundry_service_24dp.xml
new file mode 100644
index 0000000..7f03ce3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_laundry_service_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.17,16.83c1.56,1.56 4.1,1.56 5.66,0 1.56,-1.56 1.56,-4.1 0,-5.66l-5.66,5.66zM18,2.01L6,2c-1.11,0 -2,0.89 -2,2v16c0,1.11 0.89,2 2,2h12c1.11,0 2,-0.89 2,-2V4c0,-1.11 -0.89,-1.99 -2,-1.99zM10,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM7,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm5,16c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_library_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_library_24dp.xml
new file mode 100644
index 0000000..a396913
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_library_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,11.55C9.64,9.35 6.48,8 3,8v11c3.48,0 6.64,1.35 9,3.55 2.36,-2.19 5.52,-3.55 9,-3.55V8c-3.48,0 -6.64,1.35 -9,3.55zM12,8c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3 1.34,3 3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_mall_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_mall_24dp.xml
new file mode 100644
index 0000000..bce7f6f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_mall_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,6h-2c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6H5c-1.1,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm-7,-3c1.66,0 3,1.34 3,3H9c0,-1.66 1.34,-3 3,-3zm0,10c-2.76,0 -5,-2.24 -5,-5h2c0,1.66 1.34,3 3,3s3,-1.34 3,-3h2c0,2.76 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_movies_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_movies_24dp.xml
new file mode 100644
index 0000000..7c662e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_movies_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8,17H6v-2h2v2zm0,-4H6v-2h2v2zm0,-4H6V7h2v2zm10,8h-2v-2h2v2zm0,-4h-2v-2h2v2zm0,-4h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_offer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_offer_24dp.xml
new file mode 100644
index 0000000..7b14cab
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_offer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21.41,11.58l-9,-9C12.05,2.22 11.55,2 11,2H4c-1.1,0 -2,0.9 -2,2v7c0,0.55 0.22,1.05 0.59,1.42l9,9c0.36,0.36 0.86,0.58 1.41,0.58 0.55,0 1.05,-0.22 1.41,-0.59l7,-7c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-0.55 -0.23,-1.06 -0.59,-1.42zM5.5,7C4.67,7 4,6.33 4,5.5S4.67,4 5.5,4 7,4.67 7,5.5 6.33,7 5.5,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_parking_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_parking_24dp.xml
new file mode 100644
index 0000000..145b78f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_parking_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,3H6v18h4v-6h3c3.31,0 6,-2.69 6,-6s-2.69,-6 -6,-6zm0.2,8H10V7h3.2c1.1,0 2,0.9 2,2s-0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pharmacy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pharmacy_24dp.xml
new file mode 100644
index 0000000..68b81de
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pharmacy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M21,5h-2.64l1.14,-3.14L17.15,1l-1.46,4H3v2l2,6 -2,6v2h18v-2l-2,-6 2,-6V5zm-5,9h-3v3h-2v-3H8v-2h3V9h2v3h3v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_phone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_phone_24dp.xml
new file mode 100644
index 0000000..3a3ba47
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_phone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pizza_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pizza_24dp.xml
new file mode 100644
index 0000000..15efc5e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pizza_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C8.43,2 5.23,3.54 3.01,6L12,22l8.99,-16C18.78,3.55 15.57,2 12,2zM7,7c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.9,2 -2,2 -2,-0.9 -2,-2zm5,8c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_play_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_play_24dp.xml
new file mode 100644
index 0000000..a75c432
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_play_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,12c0,-1.1 0.9,-2 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -1.99,0.9 -1.99,2v4c1.1,0 1.99,0.9 1.99,2s-0.89,2 -2,2v4c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-4c-1.1,0 -2,-0.9 -2,-2zm-4.42,4.8L12,14.5l-3.58,2.3 1.08,-4.12 -3.29,-2.69 4.24,-0.25L12,5.8l1.54,3.95 4.24,0.25 -3.29,2.69 1.09,4.11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_post_office_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_post_office_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_post_office_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_print_shop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_print_shop_24dp.xml
new file mode 100644
index 0000000..9bfb690
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_print_shop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,8H5c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zm-3,11H8v-5h8v5zm3,-7c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm-1,-9H6v4h12V3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_restaurant_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_restaurant_24dp.xml
new file mode 100644
index 0000000..4c41998
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_restaurant_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zm6.78,-1.81c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_see_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_see_24dp.xml
new file mode 100644
index 0000000..8404ea5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_see_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_shipping_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_shipping_24dp.xml
new file mode 100644
index 0000000..57d8b29
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_shipping_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,8h-3V4H3c-1.1,0 -2,0.9 -2,2v11h2c0,1.66 1.34,3 3,3s3,-1.34 3,-3h6c0,1.66 1.34,3 3,3s3,-1.34 3,-3h2v-5l-3,-4zM6,18.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm13.5,-9l1.96,2.5H17V9.5h2.5zm-1.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_taxi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_taxi_24dp.xml
new file mode 100644
index 0000000..bf051cf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_taxi_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5H15V3H9v2H6.5c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_location_history_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_location_history_24dp.xml
new file mode 100644
index 0000000..f72c0cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_location_history_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,2H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,3.3c1.49,0 2.7,1.21 2.7,2.7 0,1.49 -1.21,2.7 -2.7,2.7 -1.49,0 -2.7,-1.21 -2.7,-2.7 0,-1.49 1.21,-2.7 2.7,-2.7zM18,16H6v-0.9c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v0.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_map_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_map_24dp.xml
new file mode 100644
index 0000000..da420ca
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_map_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48V20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48V3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM15,19l-6,-2.11V5l6,2.11V19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_my_location_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_my_location_24dp.xml
new file mode 100644
index 0000000..2a7817e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_my_location_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm8.94,3c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_navigation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_navigation_24dp.xml
new file mode 100644
index 0000000..bb6a1de
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_navigation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_pin_drop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_pin_drop_24dp.xml
new file mode 100644
index 0000000..f2c728d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_pin_drop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,8c0,-3.31 -2.69,-6 -6,-6S6,4.69 6,8c0,4.5 6,11 6,11s6,-6.5 6,-11zm-8,0c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.89,2 -2,2c-1.1,0 -2,-0.9 -2,-2zM5,20v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_place_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_place_24dp.xml
new file mode 100644
index 0000000..04b8734
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_place_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_rate_review_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_rate_review_24dp.xml
new file mode 100644
index 0000000..740409d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_rate_review_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,14v-2.47l6.88,-6.88c0.2,-0.2 0.51,-0.2 0.71,0l1.77,1.77c0.2,0.2 0.2,0.51 0,0.71L8.47,14H6zm12,0h-7.5l2,-2H18v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_restaurant_menu_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_restaurant_menu_24dp.xml
new file mode 100644
index 0000000..4c41998
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_restaurant_menu_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zm6.78,-1.81c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_satellite_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_satellite_24dp.xml
new file mode 100644
index 0000000..1f89d3c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_satellite_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM5,4.99h3C8,6.65 6.66,8 5,8V4.99zM5,12v-2c2.76,0 5,-2.25 5,-5.01h2C12,8.86 8.87,12 5,12zm0,6l3.5,-4.5 2.5,3.01L14.5,12l4.5,6H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_store_mall_directory_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_store_mall_directory_24dp.xml
new file mode 100644
index 0000000..cef78e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_store_mall_directory_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4H4v2h16V4zm1,10v-2l-1,-5H4l-1,5v2h1v6h10v-6h4v6h2v-6h1zm-9,4H6v-4h6v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_terrain_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_terrain_24dp.xml
new file mode 100644
index 0000000..c3ad94b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_terrain_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14,6l-3.75,5 2.85,3.8 -1.6,1.2C9.81,13.75 7,10 7,10l-6,8h22L14,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_traffic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_traffic_24dp.xml
new file mode 100644
index 0000000..61f7523
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_traffic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,10h-3V8.86c1.72,-0.45 3,-2 3,-3.86h-3V4c0,-0.55 -0.45,-1 -1,-1H8c-0.55,0 -1,0.45 -1,1v1H4c0,1.86 1.28,3.41 3,3.86V10H4c0,1.86 1.28,3.41 3,3.86V15H4c0,1.86 1.28,3.41 3,3.86V20c0,0.55 0.45,1 1,1h8c0.55,0 1,-0.45 1,-1v-1.14c1.72,-0.45 3,-2 3,-3.86h-3v-1.14c1.72,-0.45 3,-2 3,-3.86zm-8,9c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2c1.1,0 2,0.9 2,2s-0.89,2 -2,2zm0,-5c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2c1.1,0 2,0.9 2,2s-0.89,2 -2,2zm0,-5c-1.11,0 -2,-0.9 -2,-2 0,-1.11 0.89,-2 2,-2 1.1,0 2,0.89 2,2 0,1.1 -0.89,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/microphone.xml b/asset-studio/src/main/java/images/material_design_icons/microphone.xml
new file mode 100644
index 0000000..b999099
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/microphone.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="m19,11l-1.7,0c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zm-4.02,0.17c0,-0.06 0.02,-0.11 0.02,-0.17l0,-6c0,-1.66 -1.34,-3 -3,-3s-3,1.34 -3,3l0,0.18l5.98,5.99zm-10.71,-8.17l-1.27,1.27l6.01,6.01l0,0.72c0,1.66 1.33,3 2.99,3c0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52c-2.76,0 -5.3,-2.1 -5.3,-5.1l-1.7,0c0,3.41 2.72,6.23 6,6.72l0,3.28l2,0l0,-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9l4.19,4.18l1.27,-1.27l-16.73,-16.73z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_24dp.xml
new file mode 100644
index 0000000..9bb7960
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,8h4V4H4v4zm6,12h4v-4h-4v4zm-6,0h4v-4H4v4zm0,-6h4v-4H4v4zm6,0h4v-4h-4v4zm6,-10v4h4V4h-4zm-6,4h4V4h-4v4zm6,6h4v-4h-4v4zm0,6h4v-4h-4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_36px.xml
new file mode 100644
index 0000000..b0a62d1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,12h6V6H6v6zm9,18h6v-6h-6v6zm-9,0h6v-6H6v6zm0,-9h6v-6H6v6zm9,0h6v-6h-6v6zm9,-15v6h6V6h-6zm-9,6h6V6h-6v6zm9,9h6v-6h-6v6zm0,9h6v-6h-6v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_24dp.xml
new file mode 100644
index 0000000..2ac850b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_36px.xml
new file mode 100644
index 0000000..97f4c9e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M30,16.5H11.74l8.38,-8.38L18,6 6,18l12,12 2.12,-2.12 -8.38,-8.38H30v-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_24dp.xml
new file mode 100644
index 0000000..4ec66aa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,10l5,5 5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_36px.xml
new file mode 100644
index 0000000..1a6474a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.5,15l7.5,7.5 7.5,-7.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_circle_24dp.xml
new file mode 100644
index 0000000..beb8569
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,12l-4,-4h8l-4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_24dp.xml
new file mode 100644
index 0000000..33c90ee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,14l5,-5 5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_36px.xml
new file mode 100644
index 0000000..0c2f255
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10.5,21l7.5,-7.5 7.5,7.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_24dp.xml
new file mode 100644
index 0000000..4087dcd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_36px.xml
new file mode 100644
index 0000000..a2aa943
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,6l-2.12,2.12 8.38,8.38H6v3h18.26l-8.38,8.38L18,30l12,-12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_24dp.xml
new file mode 100644
index 0000000..8ea1d98
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zm5,13.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_36px.xml
new file mode 100644
index 0000000..c682aef
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,3C9.71,3 3,9.71 3,18s6.71,15 15,15 15,-6.71 15,-15S26.29,3 18,3zm7.5,20.38l-2.12,2.12L18,20.12l-5.38,5.38 -2.12,-2.12L15.88,18l-5.38,-5.38 2.12,-2.12L18,15.88l5.38,-5.38 2.12,2.12L20.12,18l5.38,5.38z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_24dp.xml
new file mode 100644
index 0000000..02e8321
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_36px.xml
new file mode 100644
index 0000000..8b6a857
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.5,24.26L7.24,18l-2.12,2.12 8.38,8.38 18,-18 -2.12,-2.12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_24dp.xml
new file mode 100644
index 0000000..ed84de6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_36px.xml
new file mode 100644
index 0000000..bd4dcaa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M23.12,11.12L21,9l-9,9 9,9 2.12,-2.12L16.24,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_24dp.xml
new file mode 100644
index 0000000..2ff2653
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_36px.xml
new file mode 100644
index 0000000..c6a6a74
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,9l-2.12,2.12L19.76,18l-6.88,6.88L15,27l9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_24dp.xml
new file mode 100644
index 0000000..88f04ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_36px.xml
new file mode 100644
index 0000000..ad67228
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M28.5,9.62L26.38,7.5 18,15.88 9.62,7.5 7.5,9.62 15.88,18 7.5,26.38l2.12,2.12L18,20.12l8.38,8.38 2.12,-2.12L20.12,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_24dp.xml
new file mode 100644
index 0000000..6f1c0d5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_36px.xml
new file mode 100644
index 0000000..12bcbc0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,12l-9,9 2.12,2.12L18,16.24l6.88,6.88L27,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_24dp.xml
new file mode 100644
index 0000000..98b7d7c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_36px.xml
new file mode 100644
index 0000000..e9d17e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.88,12.88L18,19.76l-6.88,-6.88L9,15l9,9 9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_24dp.xml
new file mode 100644
index 0000000..e35574f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,14H5v5h5v-2H7v-3zm-2,-4h2V7h3V5H5v5zm12,7h-3v2h5v-5h-2v3zM14,5v2h3v3h2V5h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_36px.xml
new file mode 100644
index 0000000..f0d60c4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,21H7v8h8v-3h-5v-5zm-3,-6h3v-5h5V7H7v8zm19,11h-5v3h8v-8h-3v5zM21,7v3h5v5h3V7h-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_24dp.xml
new file mode 100644
index 0000000..ad11915
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,16h3v3h2v-5H5v2zm3,-8H5v2h5V5H8v3zm6,11h2v-3h3v-2h-5v5zm2,-11V5h-2v5h5V8h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_36px.xml
new file mode 100644
index 0000000..9e779e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7,24h5v5h3v-8H7v3zm5,-12H7v3h8V7h-3v5zm9,17h3v-5h5v-3h-8v8zm3,-17V7h-3v8h8v-3h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_24dp.xml
new file mode 100644
index 0000000..60e9e71
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,18h18v-2H3v2zm0,-5h18v-2H3v2zm0,-7v2h18V6H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_36px.xml
new file mode 100644
index 0000000..b90b53a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,27h28v-3H4v3zm0,-8h28v-3H4v3zM4,8v3h28V8H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_24dp.xml
new file mode 100644
index 0000000..74c9367
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_36px.xml
new file mode 100644
index 0000000..6a3165e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm18,0c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm-9,0c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_24dp.xml
new file mode 100644
index 0000000..9c7516e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm0,2c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_36px.xml
new file mode 100644
index 0000000..43afde6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,12c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3 1.34,3 3,3zm0,3c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm0,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_24dp.xml
new file mode 100644
index 0000000..19991a9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_36px.xml
new file mode 100644
index 0000000..a4d1eed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M26.47,9.53C24.3,7.35 21.32,6 18,6 11.37,6 6,11.37 6,18s5.37,12 12,12c5.94,0 10.85,-4.33 11.81,-10h-3.04c-0.91,4.01 -4.49,7 -8.77,7 -4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9c2.49,0 4.71,1.03 6.34,2.66L20,16h10V6l-3.53,3.53z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_24dp.xml
new file mode 100644
index 0000000..459c655
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M7.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zm9.18,-13.18L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_36px.xml
new file mode 100644
index 0000000..d7cf4e9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.12,27.88L13.24,30 18,25.24 22.76,30l2.12,-2.12L18,21l-6.88,6.88zM24.88,8.12L22.76,6 18,10.76 13.24,6l-2.12,2.12L18,15l6.88,-6.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_24dp.xml
new file mode 100644
index 0000000..b912880
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zm0,12.34L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_36px.xml
new file mode 100644
index 0000000..61f093a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,9.24L22.76,14l2.12,-2.12L18,5l-6.88,6.88L13.24,14 18,9.24zm0,17.52L13.24,22l-2.12,2.12L18,31l6.88,-6.88L22.76,22 18,26.76z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_adb_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_adb_24dp.xml
new file mode 100644
index 0000000..b34e4d4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_adb_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4H5v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm6,0c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_bluetooth_audio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_bluetooth_audio_24dp.xml
new file mode 100644
index 0000000..d09fb6f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_bluetooth_audio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zm5.29,-5.3l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zm-3.82,1L10,2H9v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59V5.83zm1.88,10.46L11,18.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_disc_full_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_disc_full_24dp.xml
new file mode 100644
index 0000000..1bc6710
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_disc_full_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,16h2v-2h-2v2zm0,-9v5h2V7h-2zM10,4c-4.42,0 -8,3.58 -8,8s3.58,8 8,8 8,-3.58 8,-8 -3.58,-8 -8,-8zm0,10c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_dnd_forwardslash_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_dnd_forwardslash_24dp.xml
new file mode 100644
index 0000000..b8dafd2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_dnd_forwardslash_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM4,12c0,-4.4 3.6,-8 8,-8 1.8,0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4,13.8 4,12zm8,8c-1.8,0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20,10.2 20,12c0,4.4 -3.6,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_do_not_disturb_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_do_not_disturb_24dp.xml
new file mode 100644
index 0000000..90eae2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_do_not_disturb_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_drive_eta_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_drive_eta_24dp.xml
new file mode 100644
index 0000000..8550cf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_drive_eta_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.92,5.01C18.72,4.42 18.16,4 17.5,4h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,11v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,12 6.5,12s1.5,0.67 1.5,1.5S7.33,15 6.5,15zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,10l1.5,-4.5h11L19,10H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_available_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_available_24dp.xml
new file mode 100644
index 0000000..3071e64
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_available_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.53,11.06L15.47,10l-4.88,4.88 -2.12,-2.12 -1.06,1.06L10.59,17l5.94,-5.94zM19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_busy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_busy_24dp.xml
new file mode 100644
index 0000000..6fb38ce
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_busy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9.31,17l2.44,-2.44L14.19,17l1.06,-1.06 -2.44,-2.44 2.44,-2.44L14.19,10l-2.44,2.44L9.31,10l-1.06,1.06 2.44,2.44 -2.44,2.44L9.31,17zM19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_note_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_note_24dp.xml
new file mode 100644
index 0000000..7aea2ac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_note_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,10H7v2h10v-2zm2,-7h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11zm-5,-5H7v2h7v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_folder_special_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_folder_special_24dp.xml
new file mode 100644
index 0000000..d62bb4f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_folder_special_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-8l-2,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm-6.42,12L10,15.9 6.42,18l0.95,-4.07 -3.16,-2.74 4.16,-0.36L10,7l1.63,3.84 4.16,0.36 -3.16,2.74 0.95,4.06z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_mms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_mms_24dp.xml
new file mode 100644
index 0000000..4f8b65f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_mms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM5,14l3.5,-4.5 2.5,3.01L14.5,8l4.5,6H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_more_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_more_24dp.xml
new file mode 100644
index 0000000..060b18d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_more_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,3H7c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.97,0.89 1.66,0.89H22c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_network_locked_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_network_locked_24dp.xml
new file mode 100644
index 0000000..cb260cc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_network_locked_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.5,10c0.17,0 0.33,0.03 0.5,0.05V1L1,20h13v-3c0,-0.89 0.39,-1.68 1,-2.23v-0.27c0,-2.48 2.02,-4.5 4.5,-4.5zm2.5,6v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S17,13.12 17,14.5V16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zm-1,0h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5V16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_bluetooth_speaker_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_bluetooth_speaker_24dp.xml
new file mode 100644
index 0000000..58978a5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_bluetooth_speaker_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.71,9.5L17,7.21V11h0.5l2.85,-2.85L18.21,6l2.15,-2.15L17.5,1H17v3.79L14.71,2.5l-0.71,0.71L16.79,6 14,8.79l0.71,0.71zM18,2.91l0.94,0.94 -0.94,0.94V2.91zm0,4.3l0.94,0.94 -0.94,0.94V7.21zm2,8.29c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_forwarded_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_forwarded_24dp.xml
new file mode 100644
index 0000000..7a544b5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_forwarded_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,11l5,-5 -5,-5v3h-4v4h4v3zm2,4.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_in_talk_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_in_talk_24dp.xml
new file mode 100644
index 0000000..6e22cac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_in_talk_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,12h2c0,-4.97 -4.03,-9 -9,-9v2c3.87,0 7,3.13 7,7zm-4,0h2c0,-2.76 -2.24,-5 -5,-5v2c1.66,0 3,1.34 3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_locked_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_locked_24dp.xml
new file mode 100644
index 0000000..236bef8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_locked_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM20,4v-0.5C20,2.12 18.88,1 17.5,1S15,2.12 15,3.5V4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1V5c0,-0.55 -0.45,-1 -1,-1zm-0.8,0h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_missed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_missed_24dp.xml
new file mode 100644
index 0000000..c03efaf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_missed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.5,5.5L12,11l7,-7 -1,-1 -6,6 -4.5,-4.5H11V3H5v6h1.5V5.5zm17.21,11.17C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71s0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73 1.6,0 3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.67,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71s-0.12,-0.52 -0.3,-0.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_paused_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_paused_24dp.xml
new file mode 100644
index 0000000..b4d51d8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_paused_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,3h-2v7h2V3zm3,12.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,3v7h2V3h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_download_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_download_24dp.xml
new file mode 100644
index 0000000..f18f833
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_download_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-4V4l-2,-2h-4L8,4v2H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zM10,4h4v2h-4V4zm2,15l-5,-5h3v-4h4v4h3l-5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_install_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_install_24dp.xml
new file mode 100644
index 0000000..a14f860
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_install_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,6h-4V4l-2,-2h-4L8,4v2H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zM10,4h4v2h-4V4zm0.5,13.5L7,14l1.41,-1.41 2.09,2.09 5.18,-5.18 1.41,1.41 -6.59,6.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sd_card_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sd_card_24dp.xml
new file mode 100644
index 0000000..74e0e41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sd_card_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,6h-2V4h2v4zm3,0h-2V4h2v4zm3,0h-2V4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sim_card_alert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sim_card_alert_24dp.xml
new file mode 100644
index 0000000..ef5a545
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sim_card_alert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-5,15h-2v-2h2v2zm0,-4h-2V8h2v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_24dp.xml
new file mode 100644
index 0000000..796a028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM9,11H7V9h2v2zm4,0h-2V9h2v2zm4,0h-2V9h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_failed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_failed_24dp.xml
new file mode 100644
index 0000000..d9adbb3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_failed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,12h-2v-2h2v2zm0,-4h-2V6h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_24dp.xml
new file mode 100644
index 0000000..5ad1561
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,4V1L8,5l4,4V6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zm0,14c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_disabled_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_disabled_24dp.xml
new file mode 100644
index 0000000..14e2932
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_disabled_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,6.35V4.26c-0.8,0.21 -1.55,0.54 -2.23,0.96l1.46,1.46c0.25,-0.12 0.5,-0.24 0.77,-0.33zm-7.14,-0.94l2.36,2.36C4.45,8.99 4,10.44 4,12c0,2.21 0.91,4.2 2.36,5.64L4,20h6v-6l-2.24,2.24C6.68,15.15 6,13.66 6,12c0,-1 0.25,-1.94 0.68,-2.77l8.08,8.08c-0.25,0.13 -0.5,0.25 -0.77,0.34v2.09c0.8,-0.21 1.55,-0.54 2.23,-0.96l2.36,2.36 1.27,-1.27L4.14,4.14 2.86,5.41zM20,4h-6v6l2.24,-2.24C17.32,8.85 18,10.34 18,12c0,1 -0.25,1.94 -0.68,2.77l1.46,1.46C19.55,15.01 20,13.56 20,12c0,-2.21 -0.91,-4.2 -2.36,-5.64L20,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_problem_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_problem_24dp.xml
new file mode 100644
index 0000000..53a6dcd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_problem_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,12c0,2.21 0.91,4.2 2.36,5.64L3,20h6v-6l-2.24,2.24C5.68,15.15 5,13.66 5,12c0,-2.61 1.67,-4.83 4,-5.65V4.26C5.55,5.15 3,8.27 3,12zm8,5h2v-2h-2v2zM21,4h-6v6l2.24,-2.24C18.32,8.85 19,10.34 19,12c0,2.61 -1.67,4.83 -4,5.65v2.09c3.45,-0.89 6,-4.01 6,-7.74 0,-2.21 -0.91,-4.2 -2.36,-5.64L21,4zm-10,9h2V7h-2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_system_update_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_system_update_24dp.xml
new file mode 100644
index 0000000..9f2103e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_system_update_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14zm-1,-6h-3V8h-2v5H8l4,4 4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_tap_and_play_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_tap_and_play_24dp.xml
new file mode 100644
index 0000000..df5e24a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_tap_and_play_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,16v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm0,4v3h3c0,-1.66 -1.34,-3 -3,-3zm0,-8v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.92,-11 -11,-11zM17,1.01L7,1c-1.1,0 -2,0.9 -2,2v7.37c0.69,0.16 1.36,0.37 2,0.64V5h10v13h-3.03c0.52,1.25 0.84,2.59 0.95,4H17c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_time_to_leave_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_time_to_leave_24dp.xml
new file mode 100644
index 0000000..8550cf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_time_to_leave_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.92,5.01C18.72,4.42 18.16,4 17.5,4h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,11v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,12 6.5,12s1.5,0.67 1.5,1.5S7.33,15 6.5,15zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,10l1.5,-4.5h11L19,10H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_vibration_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vibration_24dp.xml
new file mode 100644
index 0000000..ce1d2c5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vibration_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0,15h2V9H0v6zm3,2h2V7H3v10zm19,-8v6h2V9h-2zm-3,8h2V7h-2v10zM16.5,3h-9C6.67,3 6,3.67 6,4.5v15c0,0.83 0.67,1.5 1.5,1.5h9c0.83,0 1.5,-0.67 1.5,-1.5v-15c0,-0.83 -0.67,-1.5 -1.5,-1.5zM16,19H8V5h8v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_voice_chat_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_voice_chat_24dp.xml
new file mode 100644
index 0000000..51c8fb6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_voice_chat_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-2,12l-4,-3.2V14H6V6h8v3.2L18,6v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_vpn_lock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vpn_lock_24dp.xml
new file mode 100644
index 0000000..8a4cff7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vpn_lock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5V4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1V5c0,-0.55 -0.45,-1 -1,-1zm-0.8,0h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7V4zm-2.28,8c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1H7v-2h2c0.55,0 1,-0.45 1,-1V8h2c1.1,0 2,-0.9 2,-2V3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_cake_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_cake_24dp.xml
new file mode 100644
index 0000000..19280f1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_cake_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,6c1.11,0 2,-0.9 2,-2 0,-0.38 -0.1,-0.73 -0.29,-1.03L12,0l-1.71,2.97c-0.19,0.3 -0.29,0.65 -0.29,1.03 0,1.1 0.9,2 2,2zm4.6,9.99l-1.07,-1.07 -1.08,1.07c-1.3,1.3 -3.58,1.31 -4.89,0l-1.07,-1.07 -1.09,1.07C6.75,16.64 5.88,17 4.96,17c-0.73,0 -1.4,-0.23 -1.96,-0.61V21c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-4.61c-0.56,0.38 -1.23,0.61 -1.96,0.61 -0.92,0 -1.79,-0.36 -2.44,-1.01zM18,9h-5V7h-2v2H6c-1.66,0 -3,1.34 -3,3v1.54c0,1.08 0.88,1.96 1.96,1.96 0.52,0 1.02,-0.2 1.38,-0.57l2.14,-2.13 2.13,2.13c0.74,0.74 2.03,0.74 2.77,0l2.14,-2.13 2.13,2.13c0.37,0.37 0.86,0.57 1.38,0.57 1.08,0 1.96,-0.88 1.96,-1.96V12C21,10.34 19.66,9 18,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_domain_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_domain_24dp.xml
new file mode 100644
index 0000000..62bc39f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_domain_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,7V3H2v18h20V7H12zM6,19H4v-2h2v2zm0,-4H4v-2h2v2zm0,-4H4V9h2v2zm0,-4H4V5h2v2zm4,12H8v-2h2v2zm0,-4H8v-2h2v2zm0,-4H8V9h2v2zm0,-4H8V5h2v2zm10,12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2,-8h-2v2h2v-2zm0,4h-2v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_group_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_24dp.xml
new file mode 100644
index 0000000..8b0643f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm-8,0c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zm0,2c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zm8,0c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_group_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_add_24dp.xml
new file mode 100644
index 0000000..ba42e71
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M8,10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10,1c1.66,0 2.99,-1.34 2.99,-3S19.66,5 18,5c-0.32,0 -0.63,0.05 -0.91,0.14 0.57,0.81 0.9,1.79 0.9,2.86s-0.34,2.04 -0.9,2.86c0.28,0.09 0.59,0.14 0.91,0.14zm-5,0c1.66,0 2.99,-1.34 2.99,-3S14.66,5 13,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm6.62,2.16c0.83,0.73 1.38,1.66 1.38,2.84v2h3v-2c0,-1.54 -2.37,-2.49 -4.38,-2.84zM13,13c-2,0 -6,1 -6,3v2h12v-2c0,-2 -4,-3 -6,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_location_city_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_location_city_24dp.xml
new file mode 100644
index 0000000..0b0e88f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_location_city_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,11V5l-3,-3 -3,3v2H3v14h18V11h-6zm-8,8H5v-2h2v2zm0,-4H5v-2h2v2zm0,-4H5V9h2v2zm6,8h-2v-2h2v2zm0,-4h-2v-2h2v2zm0,-4h-2V9h2v2zm0,-4h-2V5h2v2zm6,12h-2v-2h2v2zm0,-4h-2v-2h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_mood_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_mood_24dp.xml
new file mode 100644
index 0000000..1ef868a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_mood_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_24dp.xml
new file mode 100644
index 0000000..8902d2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_none_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_none_24dp.xml
new file mode 100644
index 0000000..6fbff40
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_none_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2zm-2,1H7v-6.5C7,8.01 9.01,6 11.5,6S16,8.01 16,10.5V17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_off_24dp.xml
new file mode 100644
index 0000000..86c52d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,10.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-0.51,0.12 -0.99,0.32 -1.45,0.56L18,14.18V10.5zm-0.27,8.5l2,2L21,19.73 4.27,3 3,4.27l2.92,2.92C5.34,8.16 5,9.29 5,10.5V16l-2,2v1h14.73z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_on_24dp.xml
new file mode 100644
index 0000000..d484b8a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.58,3.58L5.15,2.15C2.76,3.97 1.18,6.8 1.03,10h2c0.15,-2.65 1.51,-4.97 3.55,-6.42zM19.97,10h2c-0.15,-3.2 -1.73,-6.03 -4.13,-7.85l-1.43,1.43c2.05,1.45 3.41,3.77 3.56,6.42zm-1.97,0.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2v-5.5zM11.5,22c0.14,0 0.27,-0.01 0.4,-0.04 0.65,-0.13 1.19,-0.58 1.44,-1.18 0.1,-0.24 0.16,-0.5 0.16,-0.78h-4c0,1.1 0.9,2 2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_paused_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_paused_24dp.xml
new file mode 100644
index 0000000..5a4d741
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_paused_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2zm-4,-6.2l-2.8,3.4H14V15H9v-1.8l2.8,-3.4H9V8h5v1.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_pages_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_pages_24dp.xml
new file mode 100644
index 0000000..65b27bf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_pages_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,5v6h5L7,7l4,1V3H5c-1.1,0 -2,0.9 -2,2zm5,8H3v6c0,1.1 0.9,2 2,2h6v-5l-4,1 1,-4zm9,4l-4,-1v5h6c1.1,0 2,-0.9 2,-2v-6h-5l1,4zm2,-14h-6v5l4,-1 -1,4h5V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_party_mode_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_party_mode_24dp.xml
new file mode 100644
index 0000000..cfb41d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_party_mode_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4h-3.17L15,2H9L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,3c1.63,0 3.06,0.79 3.98,2H12c-1.66,0 -3,1.34 -3,3 0,0.35 0.07,0.69 0.18,1H7.1c-0.06,-0.32 -0.1,-0.66 -0.1,-1 0,-2.76 2.24,-5 5,-5zm0,10c-1.63,0 -3.06,-0.79 -3.98,-2H12c1.66,0 3,-1.34 3,-3 0,-0.35 -0.07,-0.69 -0.18,-1h2.08c0.07,0.32 0.1,0.66 0.1,1 0,2.76 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_people_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_24dp.xml
new file mode 100644
index 0000000..8b0643f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm-8,0c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zm0,2c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zm8,0c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_people_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_outline_24dp.xml
new file mode 100644
index 0000000..6cea149
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.5,13c-1.2,0 -3.07,0.34 -4.5,1 -1.43,-0.67 -3.3,-1 -4.5,-1C5.33,13 1,14.08 1,16.25V19h22v-2.75c0,-2.17 -4.33,-3.25 -6.5,-3.25zm-4,4.5h-10v-1.25c0,-0.54 2.56,-1.75 5,-1.75s5,1.21 5,1.75v1.25zm9,0H14v-1.25c0,-0.46 -0.2,-0.86 -0.52,-1.22 0.88,-0.3 1.96,-0.53 3.02,-0.53 2.44,0 5,1.21 5,1.75v1.25zM7.5,12c1.93,0 3.5,-1.57 3.5,-3.5S9.43,5 7.5,5 4,6.57 4,8.5 5.57,12 7.5,12zm0,-5.5c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zm9,5.5c1.93,0 3.5,-1.57 3.5,-3.5S18.43,5 16.5,5 13,6.57 13,8.5s1.57,3.5 3.5,3.5zm0,-5.5c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_person_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_24dp.xml
new file mode 100644
index 0000000..587e14c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zm0,2c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_person_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_add_24dp.xml
new file mode 100644
index 0000000..e43603c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zm-9,-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9,4c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_person_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_outline_24dp.xml
new file mode 100644
index 0000000..a34bd73
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1H5.9V17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,9c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_plus_one_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_plus_one_24dp.xml
new file mode 100644
index 0000000..9b8344a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_plus_one_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,8H8v4H4v2h4v4h2v-4h4v-2h-4zm4.5,-1.92V7.9l2.5,-0.5V18h2V5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_poll_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_poll_24dp.xml
new file mode 100644
index 0000000..1d393b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_poll_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2V7h2v10zm4,0h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_public_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_public_24dp.xml
new file mode 100644
index 0000000..d6e43bb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_public_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-1,17.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zm6.9,-2.54c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1H8v-2h2c0.55,0 1,-0.45 1,-1V7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_school_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_school_24dp.xml
new file mode 100644
index 0000000..4ed1f28
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_school_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5,13.18v4L12,21l7,-3.82v-4L12,17l-7,-3.82zM12,3L1,9l11,6 9,-4.91V17h2V9L12,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_share_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_share_24dp.xml
new file mode 100644
index 0000000..737da72
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_share_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_whatshot_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_whatshot_24dp.xml
new file mode 100644
index 0000000..b2dd494
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_whatshot_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/test_path_01.xml b/asset-studio/src/main/java/images/material_design_icons/test_path_01.xml
new file mode 100644
index 0000000..5ec99ab
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/test_path_01.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="800dp"
+        android:height="800dp"
+        android:viewportWidth="800.0"
+        android:viewportHeight="800.0">
+    <path
+        android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
+        android:fillColor="#00000000"
+        android:strokeColor="#FF0000"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/test_path_02.xml b/asset-studio/src/main/java/images/material_design_icons/test_path_02.xml
new file mode 100644
index 0000000..7db7f33
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/test_path_02.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="800dp"
+        android:height="800dp"
+        android:viewportWidth="800.0"
+        android:viewportHeight="800.0">
+    <path
+        android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
+        android:fillColor="#00000000"
+        android:strokeColor="#FF0000"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/test_qt_01.xml b/asset-studio/src/main/java/images/material_design_icons/test_qt_01.xml
new file mode 100644
index 0000000..3d1e007
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/test_qt_01.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="800dp"
+        android:height="600dp"
+        android:viewportWidth="800.0"
+        android:viewportHeight="600.0">
+    <path
+        android:pathData="m0,200q200,250 200,0t200,0Q500,400,600,200T800,200"
+        android:fillColor="#000000"
+        android:strokeColor="#000000"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_24dp.xml
new file mode 100644
index 0000000..7c34d3e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2V5c0,-1.1 -0.89,-2 -2,-2zm-9,14l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_outline_blank_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_outline_blank_24dp.xml
new file mode 100644
index 0000000..ce7d078
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_outline_blank_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_off_24dp.xml
new file mode 100644
index 0000000..3ea75d6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_on_24dp.xml
new file mode 100644
index 0000000..7eb8099
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zm0,-5C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_24dp.xml
new file mode 100644
index 0000000..e2c53b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_half_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_half_24dp.xml
new file mode 100644
index 0000000..80f11db
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_half_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,9.74l-7.19,-0.62L12,2.5 9.19,9.13 2,9.74l5.46,4.73 -1.64,7.03L12,17.77l6.18,3.73 -1.63,-7.03L22,9.74zM12,15.9V6.6l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_outline_24dp.xml
new file mode 100644
index 0000000..4ca7ae3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
+</vector>
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java
index ed5ea04..b18912f 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 
 @SuppressWarnings("javadoc")
-public class ActionBarIconGeneratorTest extends GeneratorTest {
+public class ActionBarIconGeneratorTest extends BitmapGeneratorTest {
     private void checkGraphic(String baseName, Theme theme) throws IOException {
         ActionBarOptions options = new ActionBarOptions();
         options.theme = theme;
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/BitmapGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/BitmapGeneratorTest.java
new file mode 100644
index 0000000..c10a778
--- /dev/null
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/BitmapGeneratorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.assetstudiolib;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Shared test infrastructure for bitmap generator
+ */
+public abstract class BitmapGeneratorTest extends GeneratorTest implements GraphicGeneratorContext {
+    private static final String TEST_DATA_REL_PATH =
+            "tools/base/asset-studio/src/test/java/com/android/assetstudiolib/testdata";
+
+    @Override
+    protected String getTestDataRelPath() {
+        return TEST_DATA_REL_PATH;
+    };
+
+    protected void checkGraphic(int expectedFileCount, String folderName, String baseName,
+            GraphicGenerator generator, GraphicGenerator.Options options)
+            throws IOException {
+        Map<String, Map<String, BufferedImage>> categoryMap =
+                new HashMap<String, Map<String, BufferedImage>>();
+        options.sourceImage = GraphicGenerator.getClipartImage("android.png");
+        generator.generate(null, categoryMap, this, options, baseName);
+
+        File targetDir = getTargetDir();
+
+        List<String> errors = new ArrayList<String>();
+        int fileCount = 0;
+        for (Map<String, BufferedImage> previews : categoryMap.values()) {
+            for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) {
+                String relativePath = entry.getKey();
+                BufferedImage image = entry.getValue();
+
+                if (image == null) continue;
+
+                String path = "testdata" + File.separator + folderName + File.separator
+                        + relativePath;
+                InputStream is = BitmapGeneratorTest.class.getResourceAsStream(path);
+                if (is == null) {
+                    String filePath = folderName + File.separator + relativePath;
+                    String generatedFilePath = generateGoldenImage(targetDir, image, path, filePath);
+                    errors.add("File did not exist, created " + generatedFilePath);
+                } else {
+                    BufferedImage goldenImage = ImageIO.read(is);
+                    assertImageSimilar(relativePath, goldenImage, image, 5.0f);
+                }
+            }
+
+            fileCount += previews.values().size();
+        }
+        if (!errors.isEmpty()) {
+            fail(errors.toString());
+        }
+
+        assertEquals("Wrong number of generated files", expectedFileCount, fileCount);
+    }
+
+    @Override
+    public BufferedImage loadImageResource(String path) {
+        try {
+            return GraphicGenerator.getStencilImage(path);
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+
+        return null;
+    }
+}
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java
index c8e2b3a..32cba47 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * 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.eclipse.org/org/documents/epl-v10.php
+ *      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,
@@ -13,216 +13,166 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.assetstudiolib;
 
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.imageio.ImageIO;
-
 import junit.framework.TestCase;
 
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
 /**
- * Shared test infrastructure for code generator
+ * Shared test infrastructure for asset (either bitmap or vector) generator.
+ * TODO: Merge this file with the duplicated one in sdk-common.
  */
-public abstract class GeneratorTest extends TestCase implements GraphicGeneratorContext {
-    private static final String TEST_DATA_REL_PATH =
-            "tools/base/asset-studio/src/test/java/com/android/assetstudiolib/testdata";
+public abstract class GeneratorTest extends TestCase {
+  protected abstract String getTestDataRelPath();
 
-    protected void checkGraphic(int expectedFileCount, String folderName, String baseName,
-            GraphicGenerator generator, GraphicGenerator.Options options)
-            throws IOException {
-        Map<String, Map<String, BufferedImage>> categoryMap =
-                new HashMap<String, Map<String, BufferedImage>>();
-        options.sourceImage = GraphicGenerator.getClipartImage("android.png");
-        generator.generate(null, categoryMap, this, options, baseName);
-
-        File targetDir = getTargetDir();
-
-        List<String> errors = new ArrayList<String>();
-        int fileCount = 0;
-        for (Map<String, BufferedImage> previews : categoryMap.values()) {
-            for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) {
-                String relativePath = entry.getKey();
-                BufferedImage image = entry.getValue();
-
-                if (image == null) continue;
-
-                String path = "testdata" + File.separator + folderName + File.separator
-                        + relativePath;
-                InputStream is = GeneratorTest.class.getResourceAsStream(path);
-                if (is == null) {
-                    if (targetDir == null) {
-                        fail("Did not find " + path
-                                + ". Set $ANDROID_SRC to have it created automatically");
-                    }
-                    File fileName = new File(targetDir, folderName + File.separator
-                            + relativePath);
-                    assertFalse(fileName.exists());
-                    if (!fileName.getParentFile().exists()) {
-                        boolean mkdir = fileName.getParentFile().mkdirs();
-                        assertTrue(fileName.getParent(), mkdir);
-                    }
-
-                    ImageIO.write(image, "PNG", fileName);
-                    errors.add("File did not exist, created " + fileName.getPath());
-                } else {
-                    BufferedImage goldenImage = ImageIO.read(is);
-                    assertImageSimilar(relativePath, goldenImage, image, 5.0f);
-                }
-            }
-
-            fileCount += previews.values().size();
-        }
-        if (errors.size() > 0) {
-            fail(errors.toString());
-        }
-
-        assertEquals("Wrong number of generated files", expectedFileCount, fileCount);
+  protected String generateGoldenImage(File targetDir,
+                                       BufferedImage goldenImage,
+                                       String missingFilePath,
+                                       String filePath) throws IOException {
+    if (targetDir == null) {
+      fail(
+        "Did not find " + missingFilePath + ". Set $ANDROID_SRC to have it created automatically");
+    }
+    File fileName = new File(targetDir, filePath);
+    assertFalse(fileName.exists());
+    if (!fileName.getParentFile().exists()) {
+      boolean mkdir = fileName.getParentFile().mkdirs();
+      assertTrue(fileName.getParent(), mkdir);
     }
 
-    public static void assertImageSimilar(String imageName, BufferedImage goldenImage,
-            BufferedImage image, float maxPercentDifferent) throws IOException {
-        assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getWidth()
-                - image.getWidth()) < 2);
-        assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getHeight()
-                - image.getHeight()) < 2);
+    ImageIO.write(goldenImage, "PNG", fileName);
+    return fileName.getPath();
+  }
 
-        assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
+  public static void assertImageSimilar(String imageName,
+                                        BufferedImage goldenImage,
+                                        BufferedImage image,
+                                        float maxPercentDifferent) throws IOException {
+    assertTrue("Widths differ too much for " + imageName,
+               Math.abs(goldenImage.getWidth() - image.getWidth()) < 2);
+    assertTrue("Widths differ too much for " + imageName,
+               Math.abs(goldenImage.getHeight() - image.getHeight()) < 2);
 
-        if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
-            BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
-                    BufferedImage.TYPE_INT_ARGB);
-            temp.getGraphics().drawImage(goldenImage, 0, 0, null);
-            goldenImage = temp;
-        }
-        assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
+    assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
 
-        int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
-        int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
+    if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
+      BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
+                                             BufferedImage.TYPE_INT_ARGB);
+      temp.getGraphics().drawImage(goldenImage, 0, 0, null);
+      goldenImage = temp;
+    }
+    assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
 
-        // Blur the images to account for the scenarios where there are pixel
-        // differences
-        // in where a sharp edge occurs
-        // goldenImage = blur(goldenImage, 6);
-        // image = blur(image, 6);
+    int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
+    int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
 
-        int width = 3 * imageWidth;
-        int height = imageHeight;
-        BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-        Graphics g = deltaImage.getGraphics();
+    // Blur the images to account for the scenarios where there are pixel
+    // differences
+    // in where a sharp edge occurs
+    // goldenImage = blur(goldenImage, 6);
+    // image = blur(image, 6);
 
-        // Compute delta map
-        long delta = 0;
-        for (int y = 0; y < imageHeight; y++) {
-            for (int x = 0; x < imageWidth; x++) {
-                int goldenRgb = goldenImage.getRGB(x, y);
-                int rgb = image.getRGB(x, y);
-                if (goldenRgb == rgb) {
-                    deltaImage.setRGB(imageWidth + x, y, 0x00808080);
-                    continue;
-                }
+    int width = 3 * imageWidth;
+    int height = imageHeight;
+    BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+    Graphics g = deltaImage.getGraphics();
 
-                // If the pixels have no opacity, don't delta colors at all
-                if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
-                    deltaImage.setRGB(imageWidth + x, y, 0x00808080);
-                    continue;
-                }
-
-                int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
-                int newR = 128 + deltaR & 0xFF;
-                int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
-                int newG = 128 + deltaG & 0xFF;
-                int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
-                int newB = 128 + deltaB & 0xFF;
-
-                int avgAlpha = ((((goldenRgb & 0xFF000000) >>> 24)
-                        + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
-
-                int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
-                deltaImage.setRGB(imageWidth + x, y, newRGB);
-
-                delta += Math.abs(deltaR);
-                delta += Math.abs(deltaG);
-                delta += Math.abs(deltaB);
-            }
+    // Compute delta map
+    long delta = 0;
+    for (int y = 0; y < imageHeight; y++) {
+      for (int x = 0; x < imageWidth; x++) {
+        int goldenRgb = goldenImage.getRGB(x, y);
+        int rgb = image.getRGB(x, y);
+        if (goldenRgb == rgb) {
+          deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+          continue;
         }
 
-        // 3 different colors, 256 color levels
-        long total = imageHeight * imageWidth * 3L * 256L;
-        float percentDifference = (float) (delta * 100 / (double) total);
-
-        if (percentDifference > maxPercentDifferent) {
-            // Expected on the left
-            // Golden on the right
-            g.drawImage(goldenImage, 0, 0, null);
-            g.drawImage(image, 2 * imageWidth, 0, null);
-
-            // Labels
-            if (imageWidth > 80) {
-                g.setColor(Color.RED);
-                g.drawString("Expected", 10, 20);
-                g.drawString("Actual", 2 * imageWidth + 10, 20);
-            }
-
-            File output = new File(getTempDir(), "delta-"
-                    + imageName.replace(File.separatorChar, '_'));
-            if (output.exists()) {
-                output.delete();
-            }
-            ImageIO.write(deltaImage, "PNG", output);
-            String message = String.format("Images differ (by %.1f%%) - see details in %s",
-                    percentDifference, output);
-            System.out.println(message);
-            fail(message);
+        // If the pixels have no opacity, don't delta colors at all
+        if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
+          deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+          continue;
         }
 
-        g.dispose();
+        int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
+        int newR = 128 + deltaR & 0xFF;
+        int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
+        int newG = 128 + deltaG & 0xFF;
+        int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
+        int newB = 128 + deltaB & 0xFF;
+
+        int avgAlpha =
+          ((((goldenRgb & 0xFF000000) >>> 24) + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
+
+        int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
+        deltaImage.setRGB(imageWidth + x, y, newRGB);
+
+        delta += Math.abs(deltaR);
+        delta += Math.abs(deltaG);
+        delta += Math.abs(deltaB);
+      }
     }
 
-    protected static File getTempDir() {
-        if (System.getProperty("os.name").equals("Mac OS X")) {
-            return new File("/tmp"); //$NON-NLS-1$
-        }
+    // 3 different colors, 256 color levels
+    long total = imageHeight * imageWidth * 3L * 256L;
+    float percentDifference = (float)(delta * 100 / (double)total);
 
-        return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+    if (percentDifference > maxPercentDifferent) {
+      // Expected on the left
+      // Golden on the right
+      g.drawImage(goldenImage, 0, 0, null);
+      g.drawImage(image, 2 * imageWidth, 0, null);
+
+      // Labels
+      if (imageWidth > 80) {
+        g.setColor(Color.RED);
+        g.drawString("Expected", 10, 20);
+        g.drawString("Actual", 2 * imageWidth + 10, 20);
+      }
+
+      File output = new File(getTempDir(), "delta-" + imageName.replace(File.separatorChar, '_'));
+      if (output.exists()) {
+        output.delete();
+      }
+      ImageIO.write(deltaImage, "PNG", output);
+      String message =
+        String.format("Images differ (by %.1f%%) - see details in %s", percentDifference, output);
+      System.out.println(message);
+      fail(message);
     }
 
-    @Override
-    public BufferedImage loadImageResource(String path) {
-        try {
-            return GraphicGenerator.getStencilImage(path);
-        } catch (IOException e) {
-            fail(e.toString());
-        }
+    g.dispose();
+  }
 
-        return null;
+  protected static File getTempDir() {
+    if (System.getProperty("os.name").equals("Mac OS X")) {
+      return new File("/tmp"); //$NON-NLS-1$
     }
 
-    /** Get the location to write missing golden files to */
-    protected File getTargetDir() {
-        // Set $ANDROID_SRC to point to your git AOSP working tree
-        String sdk = System.getenv("ANDROID_SRC");
-        if (sdk != null) {
-            File root = new File(sdk);
-            if (root.exists()) {
-                File testData = new File(root, TEST_DATA_REL_PATH.replace('/',
-                        File.separatorChar));
-                if (testData.exists()) {
-                    return testData;
-                }
-            }
-        }
+    return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+  }
 
-        return null;
+  /**
+   * Get the location to write missing golden files to
+   */
+  protected File getTargetDir() {
+    // Set $ANDROID_SRC to point to your git AOSP working tree
+    String sdk = System.getenv("ANDROID_SRC");
+    if (sdk != null) {
+      File root = new File(sdk);
+      if (root.exists()) {
+        File testData = new File(root, getTestDataRelPath().replace('/', File.separatorChar));
+        if (testData.exists()) {
+          return testData;
+        }
+      }
     }
+
+    return null;
+  }
 }
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java
index 085221f..4bcba0f 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java
@@ -21,7 +21,7 @@
 import java.io.IOException;
 
 @SuppressWarnings("javadoc")
-public class LauncherIconGeneratorTest extends GeneratorTest {
+public class LauncherIconGeneratorTest extends BitmapGeneratorTest {
     private void checkGraphic(String baseName,
             GraphicGenerator.Shape shape, GraphicGenerator.Style style,
             boolean crop, int background, boolean isWebGraphic) throws IOException {
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java
index 700be4b..2ba8f55 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java
@@ -19,7 +19,7 @@
 import java.io.IOException;
 
 @SuppressWarnings("javadoc")
-public class MenuIconGeneratorTest extends GeneratorTest {
+public class MenuIconGeneratorTest extends BitmapGeneratorTest {
     private void checkGraphic(String baseName) throws IOException {
         MenuIconGenerator generator = new MenuIconGenerator();
         checkGraphic(4, "menus", baseName, generator, new GraphicGenerator.Options());
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java
index 1880d2b..d37a96d 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java
@@ -21,7 +21,7 @@
 import java.io.IOException;
 
 @SuppressWarnings("javadoc")
-public class NotificationIconGeneratorTest extends GeneratorTest {
+public class NotificationIconGeneratorTest extends BitmapGeneratorTest {
     private void checkGraphic(String baseName, int minSdk, String folderName,
             int expectedCount) throws IOException {
         NotificationOptions options = new NotificationOptions();
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java
index 4231f54..bf3fe28 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java
@@ -19,7 +19,7 @@
 import java.io.IOException;
 
 @SuppressWarnings("javadoc")
-public class TabIconGeneratorTest extends GeneratorTest {
+public class TabIconGeneratorTest extends BitmapGeneratorTest {
     private void checkGraphic(String folderName, String baseName, int minSdk,
             int expectedFileCount) throws IOException {
         TabIconGenerator generator = new TabIconGenerator();
diff --git a/build-system/builder-model/build.gradle b/build-system/builder-model/build.gradle
index 0f8e9ec..0f2fc5f 100644
--- a/build-system/builder-model/build.gradle
+++ b/build-system/builder-model/build.gradle
@@ -1,12 +1,3 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
 apply plugin: 'clone-artifacts'
 apply plugin: 'sdk-java-lib'
diff --git a/build-system/builder-model/src/main/java/com/android/build/OutputFile.java b/build-system/builder-model/src/main/java/com/android/build/OutputFile.java
index 1a382a8..bcfd8cd 100644
--- a/build-system/builder-model/src/main/java/com/android/build/OutputFile.java
+++ b/build-system/builder-model/src/main/java/com/android/build/OutputFile.java
@@ -17,7 +17,6 @@
 package com.android.build;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 
 import java.io.File;
 import java.util.Collection;
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java b/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java
index 483a208..9f92de9 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java
@@ -16,8 +16,6 @@
 
 package com.android.builder.model;
 
-import com.android.annotations.NonNull;
-
 import java.util.Collection;
 
 /**
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
index 53ba933..4350079 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
@@ -102,6 +102,12 @@
     Set<String> getAbiFilters();
 
     /**
+     * Returns the native libraries associated with the artifact.
+     */
+    @Nullable
+    Collection<NativeLibrary> getNativeLibraries();
+
+    /**
      * Map of Build Config Fields where the key is the field name.
      *
      * @return a non-null map of class fields (possibly empty).
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
index 6ef2155..3e8f5ca 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
@@ -156,4 +156,15 @@
      */
     @NonNull
     File getPublicResources();
+
+    /**
+     * Returns whether the library is considered optional, meaning that it may or may not
+     * be present in the final APK.
+     *
+     * If the library is optional, then:
+     * - if the consumer is a library, it'll get skipped from resource merging and won't show up
+     *   in the consumer R.txt
+     * - if the consumer is a separate test project, all the resources gets skipped from merging.
+     */
+    boolean isOptional();
 }
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
index 9747e41..068f365 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
@@ -165,6 +165,14 @@
     Collection<File> getFrameworkSources();
 
     /**
+     * Returns the collection of toolchains used to create any native libraries.
+     *
+     * @return collection of toolchains.
+     */
+    @NonNull
+    Collection<NativeToolchain> getNativeToolchains();
+
+    /**
      * Returns a list of {@link SigningConfig}.
      */
     @NonNull
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java b/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
index 1057cf2..aa6da40 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
@@ -21,6 +21,7 @@
 
 import java.io.File;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -48,28 +49,28 @@
     Map<String, ClassField> getResValues();
 
     /**
-     * Returns the list of proguard rule files.
+     * Returns the collection of proguard rule files.
      *
      * <p>These files are only applied to the production code.
      *
-     * @return a non-null list of files.
+     * @return a non-null collection of files.
      * @see #getTestProguardFiles()
      */
     @NonNull
     Collection<File> getProguardFiles();
 
     /**
-     * Returns the list of proguard rule files for consumers of the library to use.
+     * Returns the collection of proguard rule files for consumers of the library to use.
      *
-     * @return a non-null list of files.
+     * @return a non-null collection of files.
      */
     @NonNull
     Collection<File> getConsumerProguardFiles();
 
     /**
-     * Returns the list of proguard rule files to use for the test APK.
+     * Returns the collection of proguard rule files to use for the test APK.
      *
-     * @return a non-null list of files.
+     * @return a non-null collection of files.
      */
     @NonNull
     Collection<File> getTestProguardFiles();
@@ -96,4 +97,14 @@
 
     @Nullable
     File getMultiDexKeepProguard();
+
+    /**
+     * Returns the optional jarjar rule files, or empty if jarjar should be skipped.
+     * If more than one file is provided, the rule files will be merged in order with last one
+     * win in case of rule redefinition.
+     * Can only be used with Jack toolchain.
+     * @return the optional jarjar rule file.
+     */
+    @NonNull
+    List<File> getJarJarRuleFiles();
 }
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/DimensionAware.java b/build-system/builder-model/src/main/java/com/android/builder/model/DimensionAware.java
new file mode 100644
index 0000000..be34319
--- /dev/null
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/DimensionAware.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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;
+
+/**
+ * Interface for objects has a dimension.
+ */
+public interface DimensionAware {
+
+    /**
+     * Returns the dimension or null if the object do not have a dimension.
+     */
+    @Nullable
+    String getDimension();
+
+}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java b/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
index 2e6466b..4fd39e9 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
@@ -16,8 +16,18 @@
 
 package com.android.builder.model;
 
+import com.android.annotations.Nullable;
+
+import java.io.File;
+
 /**
  * The information for a generated Java artifact.
  */
 public interface JavaArtifact extends BaseArtifact {
+
+    /**
+     * Path to the mockable platform jar generated for this {@link JavaArtifact}, if present.
+     */
+    @Nullable
+    File getMockablePlatformJar();
 }
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/NativeLibrary.java b/build-system/builder-model/src/main/java/com/android/builder/model/NativeLibrary.java
new file mode 100644
index 0000000..6e9148e
--- /dev/null
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/NativeLibrary.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 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 java.io.File;
+import java.util.List;
+
+/**
+ * A Native Library.  The configurations used to create a shared object.
+ */
+public interface NativeLibrary {
+
+    /**
+     * Returns the name of the native library.
+     *
+     * A native library "libfoo.so" would have the name of "foo".
+     *
+     * @return name of the native library.
+     */
+    @NonNull
+    String getName();
+
+    /**
+     * Returns the ABI of the library.
+     *
+     * @return abi of the library.
+     */
+    @NonNull
+    String getAbi();
+
+    /**
+     * Returns the name of the toolchain used to compile the native library.
+     *
+     * @return name of the toolchain.
+     */
+    @NonNull
+    String getToolchainName();
+
+    /**
+     * A list of include directories for compiling C code.
+     *
+     * @return list of include directories.
+     */
+    @NonNull
+    List<File> getCIncludeDirs();
+
+    /**
+     * A list of include directories for compiling C++ code.
+     *
+     * @return list of include directories.
+     */
+    @NonNull
+    List<File> getCppIncludeDirs();
+
+    /**
+     * A list of system include directories for compiling C code.
+     *
+     * @return list of include directories.
+     */
+    @NonNull
+    List<File> getCSystemIncludeDirs();
+
+    /**
+     * A list of system include directories for compiling C++ code.
+     *
+     * @return list of include directories.
+     */
+    @NonNull
+    List<File> getCppSystemIncludeDirs();
+
+    /**
+     * A list of defines for C code.
+     *
+     * @return list of defines.
+     */
+    @NonNull
+    List<String> getCDefines();
+
+    /**
+     * A list of defines for C++ code.
+     *
+     * @return list of defines.
+     */
+    @NonNull
+    List<String> getCppDefines();
+
+    /**
+     * A list of compiler flags for C code.
+     *
+     * @return list of compiler flags.
+     */
+    @NonNull
+    List<String> getCCompilerFlags();
+
+    /**
+     * A list of compiler flags for C++ code.
+     *
+     * @return list of compiler flags.
+     */
+    @NonNull
+    List<String> getCppCompilerFlags();
+
+    /**
+     * The folders containing built libraries with debug information.
+     *
+     * @return list of paths to locate shared objects with debug information.
+     */
+    @NonNull
+    List<File> getDebuggableLibraryFolders();
+
+}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/NativeToolchain.java b/build-system/builder-model/src/main/java/com/android/builder/model/NativeToolchain.java
new file mode 100644
index 0000000..e1c387c
--- /dev/null
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/NativeToolchain.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 java.io.File;
+
+/**
+ * Toolchain for building a native library.
+ */
+public interface NativeToolchain {
+
+    /**
+     * Returns the name of the toolchain.
+     *
+     * e.g. "x86_64", "arm-linux-androideabi"
+     *
+     * @return name of the toolchain.
+     */
+    @NonNull
+    String getName();
+
+    /**
+     * Returns the full path of the C compiler.
+     *
+     * @return the C compiler path.
+     */
+    @NonNull
+    File getCCompilerExecutable();
+
+    /**
+     * Returns the full path of the C++ compiler.
+     *
+     * @return the C++ compiler path.
+     */
+    @NonNull
+    File getCppCompilerExecutable();
+}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java b/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java
index b69e00a..2ea4f0e 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java
@@ -29,4 +29,7 @@
 
     @NonNull
     Set<String> getPickFirsts();
+
+    @NonNull
+    Set<String> getMerges();
 }
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java b/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
index a252842..04f408a 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 
 import java.util.Collection;
+import java.util.Map;
 
 /**
  * a Product Flavor. This is only the configuration of the flavor.
@@ -30,7 +31,7 @@
  * @see ProductFlavorContainer
  * @see BaseArtifact#getDependencies()
  */
-public interface ProductFlavor extends BaseConfig {
+public interface ProductFlavor extends BaseConfig, DimensionAware {
 
     /**
      * Returns the name of the flavor.
@@ -42,12 +43,6 @@
     String getName();
 
     /**
-     * Returns the flavor dimension or null if not applicable.
-     */
-    @Nullable
-    String getDimension();
-
-    /**
      * Returns the name of the product flavor. This is only the value set on this product flavor.
      * To get the final application id name, use {@link AndroidArtifact#getApplicationId()}.
      *
@@ -146,6 +141,12 @@
     String getTestInstrumentationRunner();
 
     /**
+     * Returns the arguments for the test instrumentation runner.
+     */
+    @NonNull
+    Map<String, String> getTestInstrumentationRunnerArguments();
+
+    /**
      * Returns the handlingProfile value. This is only the value set on this product flavor.
      *
      *  @return the handlingProfile value.
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java b/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java
index 59d12ec..85f0d1e 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java
@@ -59,7 +59,10 @@
      */
     int TYPE_MISMATCH_DEP             = 9;
 
-    int TYPE_MAX                      = 10; // increment when adding new types.
+    // data is dependency coordinate
+    int TYPE_OPTIONAL_LIB_NOT_FOUND   = 10;
+
+    int TYPE_MAX                      = 11; // increment when adding new types.
 
     /**
      * Returns the severity of the issue.
diff --git a/build-system/builder-test-api/build.gradle b/build-system/builder-test-api/build.gradle
index b41a68c..d24b00f 100644
--- a/build-system/builder-test-api/build.gradle
+++ b/build-system/builder-test-api/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'clone-artifacts'
 
 dependencies {
diff --git a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java
index 3a261ce..3f19bc6 100644
--- a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java
+++ b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java
@@ -23,7 +23,6 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Locale;
diff --git a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java
index b93d79a..a60d214 100644
--- a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java
+++ b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java
@@ -17,7 +17,6 @@
 package com.android.builder.testing.api;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 import com.google.common.annotations.Beta;
 
 /**
diff --git a/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java b/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java
index 8d473bc..6bbe759 100644
--- a/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java
+++ b/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java
@@ -21,8 +21,6 @@
 
 import com.google.common.collect.ImmutableList;
 
-import junit.framework.TestCase;
-
 import org.junit.Test;
 
 import java.util.ArrayList;
diff --git a/build-system/builder/build.gradle b/build-system/builder/build.gradle
index 05e41d0..050a4a1 100644
--- a/build-system/builder/build.gradle
+++ b/build-system/builder/build.gradle
@@ -1,14 +1,6 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
 apply plugin: 'clone-artifacts'
+apply plugin: 'jacoco'
 
 evaluationDependsOn(':base:builder-model')
 evaluationDependsOn(':base:builder-test-api')
@@ -23,12 +15,14 @@
     compile project(':base:manifest-merger')
     compile project(':base:ddmlib')
 
+    compile project(':base:jack:jack-api')
+    compile project(':base:jack:jill-api')
+
     compile 'com.squareup:javawriter:2.5.0'
     compile 'org.bouncycastle:bcpkix-jdk15on:1.48'
     compile 'org.bouncycastle:bcprov-jdk15on:1.48'
     compile 'org.ow2.asm:asm:5.0.3'
     compile 'org.ow2.asm:asm-tree:5.0.3'
-    compile 'com.android.tools.jack:jack-api:0.1'
 
     testCompile 'junit:junit:4.12'
     testCompile 'org.mockito:mockito-all:1.9.5'
@@ -99,6 +93,7 @@
 }
 
 sourceSets.main.compileClasspath += [configurations.provided]
+sourceSets.test.runtimeClasspath += [configurations.provided]
 tasks.compileJava.dependsOn(configurations.provided)
 tasks.sourcesJar.dependsOn(configurations.sourcesProvided)
 
diff --git a/build-system/builder/builder.iml b/build-system/builder/builder.iml
index 7c62a1c..0484483 100644
--- a/build-system/builder/builder.iml
+++ b/build-system/builder/builder.iml
@@ -25,6 +25,8 @@
     <orderEntry type="module" module-name="manifest-merger-base" />
     <orderEntry type="library" exported="" name="asm-tools" level="project" />
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
-    <orderEntry type="library" name="jack-api-0.1" level="project" />
+    <orderEntry type="module" module-name="assetstudio-base" />
+    <orderEntry type="module" module-name="jack-api" exported="" />
+    <orderEntry type="module" module-name="jill-api" exported="" />
   </component>
 </module>
\ No newline at end of file
diff --git a/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java b/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java
index fa62aeb..0e5d399 100644
--- a/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java
+++ b/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java
@@ -20,6 +20,7 @@
 import com.android.builder.internal.incremental.DependencyData;
 
 import java.io.File;
+import java.io.IOException;
 
 /**
  * A Class that processes a dependency file after a compilation.
@@ -31,8 +32,8 @@
  *
  * The instance will be called for each dependency file that is created during compilation.
  *
- * @see com.android.builder.core.AndroidBuilder#compileAllAidlFiles(java.util.List, java.io.File, java.io.File, java.util.List, DependencyFileProcessor)
- * @see com.android.builder.core.AndroidBuilder#compileAidlFile(java.io.File, java.io.File, java.io.File, java.io.File, java.util.List, DependencyFileProcessor)
+ * @see com.android.builder.core.AndroidBuilder#compileAllAidlFiles(java.util.List, File, File, java.util.List, DependencyFileProcessor, com.android.ide.common.process.ProcessOutputHandler)
+ * @see com.android.builder.core.AndroidBuilder#compileAidlFile(File, File, File, File, java.util.List, DependencyFileProcessor, com.android.ide.common.process.ProcessOutputHandler)
  */
 public interface DependencyFileProcessor {
 
@@ -41,5 +42,5 @@
      * @param dependencyFile the dependency file.
      * @return the dependency data that was created.
      */
-    DependencyData processFile(@NonNull File dependencyFile);
+    DependencyData processFile(@NonNull File dependencyFile) throws IOException;
 }
diff --git a/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java b/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java
index 23f21bf..5b6aed6 100644
--- a/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java
+++ b/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java
@@ -16,10 +16,11 @@
 package com.android.builder.compiling;
 
 import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_TRANSLATABLE;
 import static com.android.SdkConstants.ATTR_TYPE;
-import static com.android.SdkConstants.TAG_STRING;
 import static com.android.SdkConstants.TAG_ITEM;
 import static com.android.SdkConstants.TAG_RESOURCES;
+import static com.android.SdkConstants.VALUE_FALSE;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.android.annotations.NonNull;
@@ -148,6 +149,12 @@
                     itemNode.getAttributes().setNamedItem(typeAttr);
                 }
 
+                if (type == ResourceType.STRING) {
+                    Attr translatable = document.createAttribute(ATTR_TRANSLATABLE);
+                    translatable.setValue(VALUE_FALSE);
+                    itemNode.getAttributes().setNamedItem(translatable);
+                }
+
                 if (!field.getValue().isEmpty()) {
                     itemNode.appendChild(document.createTextNode(field.getValue()));
                 }
@@ -164,7 +171,7 @@
         try {
             content = XmlPrettyPrinter.prettyPrint(document, true);
         } catch (Throwable t) {
-            content = XmlUtils.toXml(document, false);
+            content = XmlUtils.toXml(document);
         }
 
         Files.write(content, resFile, Charsets.UTF_8);
diff --git a/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java b/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java
index 69dac44..b64fb82 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java
@@ -379,7 +379,6 @@
             resourceConfigs.add(mPreferredDensity);
             // when adding a density filter, also always add the nodpi option.
             resourceConfigs.add(Density.NODPI.getResourceValue());
-            resourceConfigs.add(Density.ANYDPI.getResourceValue());
         }
 
 
diff --git a/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java b/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java
index 22f69f1..ce77d74 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java
@@ -29,7 +29,6 @@
 
 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.core.BuildToolsServiceLoader.BuildToolServiceLoader;
 import com.android.builder.dependency.ManifestDependency;
@@ -45,11 +44,11 @@
 import com.android.builder.internal.compiler.RenderScriptProcessor;
 import com.android.builder.internal.compiler.SourceSearcher;
 import com.android.builder.internal.incremental.DependencyData;
-import com.android.builder.internal.packaging.JavaResourceProcessor;
 import com.android.builder.internal.packaging.Packager;
 import com.android.builder.model.ClassField;
 import com.android.builder.model.PackagingOptions;
 import com.android.builder.model.SigningConfig;
+import com.android.builder.model.SyncIssue;
 import com.android.builder.packaging.DuplicateFileException;
 import com.android.builder.packaging.PackagerException;
 import com.android.builder.packaging.SealedPackageException;
@@ -58,7 +57,6 @@
 import com.android.builder.sdk.TargetInfo;
 import com.android.builder.signing.SignedJarBuilder;
 import com.android.ide.common.internal.AaptCruncher;
-import com.android.ide.common.internal.CommandLineRunner;
 import com.android.ide.common.internal.LoggedErrorException;
 import com.android.ide.common.internal.PngCruncher;
 import com.android.ide.common.process.CachedProcessOutputHandler;
@@ -82,6 +80,9 @@
 import com.android.jack.api.v01.MultiDexKind;
 import com.android.jack.api.v01.ReporterKind;
 import com.android.jack.api.v01.UnrecoverableException;
+import com.android.jill.api.JillProvider;
+import com.android.jill.api.v01.Api01TranslationTask;
+import com.android.jill.api.v01.TranslationException;
 import com.android.manifmerger.ManifestMerger2;
 import com.android.manifmerger.MergingReport;
 import com.android.manifmerger.PlaceholderEncoder;
@@ -89,6 +90,7 @@
 import com.android.manifmerger.XmlDocument;
 import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
 import com.android.sdklib.repository.FullRevision;
 import com.android.utils.ILogger;
 import com.android.utils.Pair;
@@ -98,11 +100,10 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
-import com.google.common.hash.HashCode;
-import com.google.common.hash.Hashing;
 import com.google.common.io.Files;
 
 import java.io.ByteArrayOutputStream;
@@ -116,10 +117,12 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
-import java.util.ServiceLoader;
 import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
 /**
  * This is the main builder class. It is given all the data to process the build (such as
@@ -127,15 +130,15 @@
  * build steps.
  *
  * To use:
- * create a builder with {@link #AndroidBuilder(String, CommandLineRunner, ProcessExecutor, JavaProcessExecutor, ProcessOutputHandler, ILogger, boolean)}
+ * create a builder with {@link #AndroidBuilder(String, String, ProcessExecutor, JavaProcessExecutor, ErrorReporter, ILogger, boolean)}
  *
  * then build steps can be done with
  * {@link #mergeManifests(File, List, List, String, int, String, String, String, Integer, String, String, ManifestMerger2.MergeType, Map, File)}
- * {@link #processTestManifest(String, String, String, String, String, Boolean, Boolean, java.io.File, java.util.List, java.io.File, java.io.File)}
- * {@link #processResources(AaptPackageProcessBuilder, boolean)}
- * {@link #compileAllAidlFiles(java.util.List, java.io.File, java.io.File, java.util.List, com.android.builder.compiling.DependencyFileProcessor)}
- * {@link #convertByteCode(Collection, Collection, File, boolean, boolean, File, DexOptions, List, File, boolean, boolean)}
- * {@link #packageApk(String, java.io.File, java.util.Collection, java.util.Collection, String, java.util.Collection, java.util.Set, boolean, com.android.builder.model.SigningConfig, com.android.builder.model.PackagingOptions, String)}
+ * {@link #processTestManifest(String, String, String, String, String, Boolean, Boolean, File, List, Map, File, File)}
+ * {@link #processResources(AaptPackageProcessBuilder, boolean, ProcessOutputHandler)}
+ * {@link #compileAllAidlFiles(List, File, File, List, DependencyFileProcessor, ProcessOutputHandler)}
+ * {@link #convertByteCode(Collection, Collection, File, boolean, File, DexOptions, List, File, boolean, boolean, ProcessOutputHandler)}
+ * {@link #packageApk(String, File, Collection, Collection, String, Collection, File, Set, boolean, SigningConfig, PackagingOptions, SignedJarBuilder.IZipEntryFilter, String)}
  *
  * Java compilation is not handled but the builder provides the bootclasspath with
  * {@link #getBootClasspath()}.
@@ -150,12 +153,18 @@
         }
     };
 
-    @NonNull private final String mProjectId;
-    @NonNull private final ILogger mLogger;
+    @NonNull
+    private final String mProjectId;
+    @NonNull
+    private final ILogger mLogger;
 
-    @NonNull private final ProcessExecutor mProcessExecutor;
-    @NonNull private final JavaProcessExecutor mJavaProcessExecutor;
-    @NonNull private final ProcessOutputHandler mProcessOutputHandler;
+    @NonNull
+    private final ProcessExecutor mProcessExecutor;
+    @NonNull
+    private final JavaProcessExecutor mJavaProcessExecutor;
+    @NonNull
+    private final ErrorReporter mErrorReporter;
+
     private final boolean mVerboseExec;
 
     @Nullable private String mCreatedBy;
@@ -164,6 +173,10 @@
     private SdkInfo mSdkInfo;
     private TargetInfo mTargetInfo;
 
+    private List<File> mBootClasspath;
+    @NonNull
+    private List<LibraryRequest> mLibraryRequests = ImmutableList.of();
+
     /**
      * Creates an AndroidBuilder.
      * <p/>
@@ -179,31 +192,14 @@
             @Nullable String createdBy,
             @NonNull ProcessExecutor processExecutor,
             @NonNull JavaProcessExecutor javaProcessExecutor,
-            @NonNull ProcessOutputHandler processOutputHandler,
+            @NonNull ErrorReporter errorReporter,
             @NonNull ILogger logger,
             boolean verboseExec) {
-        mProjectId = projectId;
+        mProjectId = checkNotNull(projectId);
         mCreatedBy = createdBy;
-        mProcessExecutor = processExecutor;
-        mJavaProcessExecutor = javaProcessExecutor;
-        mProcessOutputHandler = processOutputHandler;
-        mLogger = checkNotNull(logger);
-        mVerboseExec = verboseExec;
-    }
-
-    @VisibleForTesting
-    AndroidBuilder(
-            @NonNull String projectId,
-            @NonNull CommandLineRunner cmdLineRunner,
-            @NonNull ProcessExecutor processExecutor,
-            @NonNull JavaProcessExecutor javaProcessExecutor,
-            @NonNull ProcessOutputHandler processOutputHandler,
-            @NonNull ILogger logger,
-            boolean verboseExec) {
-        mProjectId = projectId;
         mProcessExecutor = checkNotNull(processExecutor);
         mJavaProcessExecutor = checkNotNull(javaProcessExecutor);
-        mProcessOutputHandler = processOutputHandler;
+        mErrorReporter = checkNotNull(errorReporter);
         mLogger = checkNotNull(logger);
         mVerboseExec = verboseExec;
     }
@@ -217,7 +213,10 @@
      *
      * @see com.android.builder.sdk.SdkLoader
      */
-    public void setTargetInfo(@NonNull SdkInfo sdkInfo, @NonNull TargetInfo targetInfo) {
+    public void setTargetInfo(
+            @NonNull SdkInfo sdkInfo,
+            @NonNull TargetInfo targetInfo,
+            @NonNull Collection<LibraryRequest> libraryRequests) {
         mSdkInfo = sdkInfo;
         mTargetInfo = targetInfo;
 
@@ -226,6 +225,8 @@
                     "The SDK Build Tools revision (%1$s) is too low for project '%2$s'. Minimum required is %3$s",
                     mTargetInfo.getBuildTools().getRevision(), mProjectId, MIN_BUILD_TOOLS_REV));
         }
+
+        mLibraryRequests = ImmutableList.copyOf(libraryRequests);
     }
 
     /**
@@ -249,6 +250,11 @@
         return mLogger;
     }
 
+    @NonNull
+    public ErrorReporter getErrorReporter() {
+        return mErrorReporter;
+    }
+
     /**
      * Returns the compilation target, if set.
      */
@@ -286,31 +292,75 @@
      */
     @NonNull
     public List<File> getBootClasspath() {
-        checkState(mTargetInfo != null,
-                "Cannot call getBootClasspath() before setTargetInfo() is called.");
+        if (mBootClasspath == null) {
+            checkState(mTargetInfo != null,
+                    "Cannot call getBootClasspath() before setTargetInfo() is called.");
 
-        List<File> classpath = Lists.newArrayList();
+            List<File> classpath = Lists.newArrayList();
 
-        IAndroidTarget target = mTargetInfo.getTarget();
+            IAndroidTarget target = mTargetInfo.getTarget();
 
-        for (String p : target.getBootClasspath()) {
-            classpath.add(new File(p));
+            for (String p : target.getBootClasspath()) {
+                classpath.add(new File(p));
+            }
+
+            List<LibraryRequest> requestedLibs = Lists.newArrayList(mLibraryRequests);
+
+            // add additional libraries if any
+            List<OptionalLibrary> libs = target.getAdditionalLibraries();
+            for (OptionalLibrary lib : libs) {
+                // add it always for now
+                classpath.add(lib.getJar());
+
+                // remove from list of requested if match
+                LibraryRequest requestedLib = findMatchingLib(lib.getName(), requestedLibs);
+                if (requestedLib != null) {
+                    requestedLibs.remove(requestedLib);
+                }
+            }
+
+            // add optional libraries if needed.
+            List<OptionalLibrary> optionalLibraries = target.getOptionalLibraries();
+            for (OptionalLibrary lib : optionalLibraries) {
+                // search if requested
+                LibraryRequest requestedLib = findMatchingLib(lib.getName(), requestedLibs);
+                if (requestedLib != null) {
+                    // add to classpath
+                    classpath.add(lib.getJar());
+
+                    // remove from requested list.
+                    requestedLibs.remove(requestedLib);
+                }
+            }
+
+            // look for not found requested libraries.
+            for (LibraryRequest library : requestedLibs) {
+                mErrorReporter.handleSyncError(
+                        library.getName(),
+                        SyncIssue.TYPE_OPTIONAL_LIB_NOT_FOUND,
+                        "Unable to find optional library: " + library.getName());
+            }
+
+            // add annotations.jar if needed.
+            if (target.getVersion().getApiLevel() <= 15) {
+                classpath.add(mSdkInfo.getAnnotationsJar());
+            }
+
+            mBootClasspath = ImmutableList.copyOf(classpath);
         }
 
-        // add optional libraries if any
-        IAndroidTarget.IOptionalLibrary[] libs = target.getOptionalLibraries();
-        if (libs != null) {
-            for (IAndroidTarget.IOptionalLibrary lib : libs) {
-                classpath.add(new File(lib.getJarPath()));
+        return mBootClasspath;
+    }
+
+    @Nullable
+    private static LibraryRequest findMatchingLib(@NonNull String name, @NonNull List<LibraryRequest> libraries) {
+        for (LibraryRequest library : libraries) {
+            if (name.equals(library.getName())) {
+                return library;
             }
         }
 
-        // add annotations.jar if needed.
-        if (target.getVersion().getApiLevel() <= 15) {
-            classpath.add(mSdkInfo.getAnnotationsJar());
-        }
-
-        return classpath;
+        return null;
     }
 
     /**
@@ -318,29 +368,15 @@
      */
     @NonNull
     public List<String> getBootClasspathAsStrings() {
-        checkState(mTargetInfo != null,
-                "Cannot call getBootClasspath() before setTargetInfo() is called.");
+        List<File> classpath = getBootClasspath();
 
-        List<String> classpath = Lists.newArrayList();
-
-        IAndroidTarget target = mTargetInfo.getTarget();
-
-        classpath.addAll(target.getBootClasspath());
-
-        // add optional libraries if any
-        IAndroidTarget.IOptionalLibrary[] libs = target.getOptionalLibraries();
-        if (libs != null) {
-            for (IAndroidTarget.IOptionalLibrary lib : libs) {
-                classpath.add(lib.getJarPath());
-            }
+        // convert to Strings.
+        List<String> results = Lists.newArrayListWithCapacity(classpath.size());
+        for (File f : classpath) {
+            results.add(f.getAbsolutePath());
         }
 
-        // add annotations.jar if needed.
-        if (target.getVersion().getApiLevel() <= 15) {
-            classpath.add(mSdkInfo.getAnnotationsJar().getPath());
-        }
-
-        return classpath;
+        return results;
     }
 
     /**
@@ -350,7 +386,7 @@
      *
      * @return the jar file, or null.
      *
-     * @see #setTargetInfo(com.android.builder.sdk.SdkInfo, com.android.builder.sdk.TargetInfo)
+     * @see #setTargetInfo(SdkInfo, TargetInfo, Collection)
      */
     @Nullable
     public File getRenderScriptSupportJar() {
@@ -418,7 +454,7 @@
      *
      * @return the folder, or null.
      *
-     * @see #setTargetInfo(com.android.builder.sdk.SdkInfo, com.android.builder.sdk.TargetInfo)
+     * @see #setTargetInfo(SdkInfo, TargetInfo, Collection)
      */
     @Nullable
     public File getSupportNativeLibFolder() {
@@ -435,13 +471,13 @@
      * @return an PngCruncher object
      */
     @NonNull
-    public PngCruncher getAaptCruncher() {
+    public PngCruncher getAaptCruncher(ProcessOutputHandler processOutputHandler) {
         checkState(mTargetInfo != null,
                 "Cannot call getAaptCruncher() before setTargetInfo() is called.");
         return new AaptCruncher(
                 mTargetInfo.getBuildTools().getPath(BuildToolInfo.PathId.AAPT),
                 mProcessExecutor,
-                mProcessOutputHandler);
+                processOutputHandler);
     }
 
     @NonNull
@@ -450,11 +486,6 @@
     }
 
     @NonNull
-    public ProcessResult executeProcess(@NonNull ProcessInfo processInfo) {
-        return executeProcess(processInfo, mProcessOutputHandler);
-    }
-
-    @NonNull
     public ProcessResult executeProcess(@NonNull ProcessInfo processInfo,
             @NonNull ProcessOutputHandler handler) {
         return mProcessExecutor.execute(processInfo, handler);
@@ -780,7 +811,8 @@
      */
     public void processResources(
             @NonNull AaptPackageProcessBuilder aaptCommand,
-            boolean enforceUniquePackageName)
+            boolean enforceUniquePackageName,
+            @NonNull ProcessOutputHandler processOutputHandler)
             throws IOException, InterruptedException, ProcessException {
 
         checkState(mTargetInfo != null,
@@ -790,7 +822,7 @@
         ProcessInfo processInfo = aaptCommand.build(
                 mTargetInfo.getBuildTools(), mTargetInfo.getTarget(), mLogger);
 
-        ProcessResult result = mProcessExecutor.execute(processInfo, mProcessOutputHandler);
+        ProcessResult result = mProcessExecutor.execute(processInfo, processOutputHandler);
         result.rethrowFailure().assertNormalExitValue();
 
         // now if the project has libraries, R needs to be created for each libraries,
@@ -812,6 +844,9 @@
             Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create();
 
             for (SymbolFileProvider lib : aaptCommand.getLibraries()) {
+                if (lib.isOptional()) {
+                    continue;
+                }
                 String packageName = VariantConfiguration.getManifestPackage(lib.getManifest());
                 if (appPackageName == null) {
                     continue;
@@ -833,7 +868,6 @@
                 // if the library has no resource, this file won't exist.
                 if (rFile.isFile()) {
 
-
                     // load the full values if that's not already been done.
                     // Doing it lazily allow us to support the case where there's no
                     // resources anywhere.
@@ -958,7 +992,8 @@
                                     @NonNull File sourceOutputDir,
                                     @Nullable File parcelableOutputDir,
                                     @NonNull List<File> importFolders,
-                                    @Nullable DependencyFileProcessor dependencyFileProcessor)
+                                    @Nullable DependencyFileProcessor dependencyFileProcessor,
+                                    @NonNull ProcessOutputHandler processOutputHandler)
             throws IOException, InterruptedException, LoggedErrorException, ProcessException {
         checkNotNull(sourceFolders, "sourceFolders cannot be null.");
         checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.");
@@ -988,7 +1023,7 @@
                 dependencyFileProcessor != null ?
                         dependencyFileProcessor : sNoOpDependencyFileProcessor,
                 mProcessExecutor,
-                mProcessOutputHandler);
+                processOutputHandler);
 
         SourceSearcher searcher = new SourceSearcher(sourceFolders, "aidl");
         searcher.setUseExecutor(true);
@@ -1012,7 +1047,8 @@
                                 @NonNull File sourceOutputDir,
                                 @Nullable File parcelableOutputDir,
                                 @NonNull List<File> importFolders,
-                                @Nullable DependencyFileProcessor dependencyFileProcessor)
+                                @Nullable DependencyFileProcessor dependencyFileProcessor,
+                                @NonNull ProcessOutputHandler processOutputHandler)
             throws IOException, InterruptedException, LoggedErrorException, ProcessException {
         checkNotNull(aidlFile, "aidlFile cannot be null.");
         checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.");
@@ -1037,7 +1073,7 @@
                 dependencyFileProcessor != null ?
                         dependencyFileProcessor : sNoOpDependencyFileProcessor,
                 mProcessExecutor,
-                mProcessOutputHandler);
+                processOutputHandler);
 
         processor.processFile(sourceFolder, aidlFile);
     }
@@ -1076,7 +1112,8 @@
                                             int optimLevel,
                                             boolean ndkMode,
                                             boolean supportMode,
-                                            @Nullable Set<String> abiFilters)
+                                            @Nullable Set<String> abiFilters,
+                                            @NonNull ProcessOutputHandler processOutputHandler)
             throws InterruptedException, ProcessException, LoggedErrorException, IOException {
         checkNotNull(sourceFolders, "sourceFolders cannot be null.");
         checkNotNull(importFolders, "importFolders cannot be null.");
@@ -1106,7 +1143,7 @@
                 ndkMode,
                 supportMode,
                 abiFilters);
-        processor.build(mProcessExecutor, mProcessOutputHandler);
+        processor.build(mProcessExecutor, processOutputHandler);
     }
 
     /**
@@ -1170,13 +1207,14 @@
             @NonNull Collection<File> preDexedLibraries,
             @NonNull File outDexFolder,
                      boolean multidex,
-                     boolean multidexLegacy,
             @Nullable File mainDexList,
             @NonNull DexOptions dexOptions,
             @Nullable List<String> additionalParameters,
             @NonNull File tmpFolder,
             boolean incremental,
-            boolean optimize) throws IOException, InterruptedException, ProcessException {
+            boolean optimize,
+            @NonNull ProcessOutputHandler processOutputHandler)
+            throws IOException, InterruptedException, ProcessException {
         checkNotNull(inputs, "inputs cannot be null.");
         checkNotNull(preDexedLibraries, "preDexedLibraries cannot be null.");
         checkNotNull(outDexFolder, "outDexFolder cannot be null.");
@@ -1187,6 +1225,13 @@
         checkState(mTargetInfo != null,
                 "Cannot call convertByteCode() before setTargetInfo() is called.");
 
+        ImmutableList.Builder<File> verifiedInputs = ImmutableList.builder();
+        for (File input : inputs) {
+            if (checkLibraryClassesJar(input)) {
+                verifiedInputs.add(input);
+            }
+        }
+
         BuildToolInfo buildToolInfo = mTargetInfo.getBuildTools();
         DexProcessBuilder builder = new DexProcessBuilder(outDexFolder);
 
@@ -1196,7 +1241,7 @@
                 .setMultiDex(multidex)
                 .setMainDexList(mainDexList)
                 .addInputs(preDexedLibraries)
-                .addInputs(inputs);
+                .addInputs(verifiedInputs.build());
 
         if (additionalParameters != null) {
             builder.additionalParameters(additionalParameters);
@@ -1204,7 +1249,7 @@
 
         JavaProcessInfo javaProcessInfo = builder.build(buildToolInfo, dexOptions);
 
-        ProcessResult result = mJavaProcessExecutor.execute(javaProcessInfo, mProcessOutputHandler);
+        ProcessResult result = mJavaProcessExecutor.execute(javaProcessInfo, processOutputHandler);
         result.rethrowFailure().assertNormalExitValue();
     }
 
@@ -1252,7 +1297,8 @@
             @NonNull File inputFile,
             @NonNull File outFile,
                      boolean multiDex,
-            @NonNull DexOptions dexOptions)
+            @NonNull DexOptions dexOptions,
+            @NonNull ProcessOutputHandler processOutputHandler)
             throws IOException, InterruptedException, ProcessException {
         checkState(mTargetInfo != null,
                 "Cannot call preDexLibrary() before setTargetInfo() is called.");
@@ -1267,7 +1313,7 @@
                 buildToolInfo,
                 mVerboseExec,
                 mJavaProcessExecutor,
-                mProcessOutputHandler);
+                processOutputHandler);
     }
 
     /**
@@ -1284,7 +1330,7 @@
      * @throws ProcessException
      */
     @NonNull
-    public static List<File> preDexLibrary(
+    public static ImmutableList<File> preDexLibrary(
             @NonNull File inputFile,
             @NonNull File outFile,
             boolean multiDex,
@@ -1298,6 +1344,15 @@
         checkNotNull(outFile, "outFile cannot be null.");
         checkNotNull(dexOptions, "dexOptions cannot be null.");
 
+
+
+        try {
+            if (!checkLibraryClassesJar(inputFile)) {
+                return ImmutableList.of();
+            }
+        } catch(IOException e) {
+            throw new RuntimeException("Exception while checking library jar", e);
+        }
         DexProcessBuilder builder = new DexProcessBuilder(outFile);
 
         builder.setVerbose(verbose)
@@ -1321,13 +1376,64 @@
                 throw new RuntimeException("No dex files created at " + outFile.getAbsolutePath());
             }
 
-            return Lists.newArrayList(files);
+            return ImmutableList.copyOf(files);
         } else {
-            return Collections.singletonList(outFile);
+            return ImmutableList.of(outFile);
         }
     }
 
     /**
+     * Returns true if the library (jar or folder) contains class files, false otherwise.
+     */
+    private static boolean checkLibraryClassesJar(@NonNull File input) throws IOException {
+
+        if (!input.exists()) {
+            return false;
+        }
+
+        if (input.isDirectory()) {
+            return checkFolder(input);
+        }
+
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(input);
+            Enumeration<? extends ZipEntry> entries = zipFile.entries();
+            while(entries.hasMoreElements()) {
+                if (entries.nextElement().getName().endsWith(".class")) {
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            if (zipFile != null) {
+                zipFile.close();
+            }
+        }
+    }
+
+    /**
+     * Returns true if this folder or one of its subfolder contains a class file, false otherwise.
+     */
+    private static boolean checkFolder(@NonNull File folder) {
+        File[] subFolders = folder.listFiles();
+        if (subFolders != null) {
+            for (File childFolder : subFolders) {
+                if (childFolder.isFile() && childFolder.getName().endsWith(".class")) {
+                    return true;
+                }
+                if (childFolder.isDirectory()) {
+                    // if childFolder returns false, continue search otherwise return success.
+                    if (checkFolder(childFolder)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Converts java source code into android byte codes using the jack integration APIs.
      * Jack will run in memory.
      */
@@ -1339,6 +1445,9 @@
             @NonNull Collection<File> sourceFiles,
             @Nullable Collection<File> proguardFiles,
             @Nullable File mappingFile,
+            @NonNull Collection<File> jarJarRulesFiles,
+            @Nullable File incrementalDir,
+            @Nullable File javaResourcesFolder,
             boolean multiDex,
             int minSdkVersion) {
 
@@ -1349,7 +1458,7 @@
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         try {
             Optional<JackProvider> jackProvider = buildToolServiceLoader
-                    .getSingleService(BuildToolsServiceLoader.JACK);
+                    .getSingleService(getLogger(), BuildToolsServiceLoader.JACK);
             if (jackProvider.isPresent()) {
                 Api01Config config;
 
@@ -1366,6 +1475,10 @@
                         config.setProguardConfigFiles(new ArrayList<File>(proguardFiles));
                     }
 
+                    if (!jarJarRulesFiles.isEmpty()) {
+                        config.setJarJarConfigFiles(ImmutableList.copyOf(jarJarRulesFiles));
+                    }
+
                     if (multiDex) {
                         if (minSdkVersion < BuildToolInfo.SDK_LEVEL_FOR_MULTIDEX_NATIVE_SUPPORT) {
                             config.setMultiDexKind(MultiDexKind.LEGACY);
@@ -1384,6 +1497,22 @@
 
                     config.setReporter(ReporterKind.DEFAULT, outputStream);
 
+                    // set the incremental dir if set and either already exists or can be created.
+                    if (incrementalDir != null) {
+                        if (!incrementalDir.exists() && !incrementalDir.mkdirs()) {
+                            mLogger.warning("Cannot create %1$s directory, "
+                                    + "jack incremental support disabled", incrementalDir);
+                        }
+                        if (incrementalDir.exists()) {
+                            config.setIncrementalDir(incrementalDir);
+                        }
+                    }
+                    if (javaResourcesFolder != null) {
+                        ArrayList<File> folders = Lists.newArrayListWithExpectedSize(3);
+                        folders.add(javaResourcesFolder);
+                        config.setResourceDirs(folders);
+                    }
+
                     compilationTask = config.getTask();
                 } catch (ConfigNotSupportedException e1) {
                     mLogger.warning("Jack APIs v01 not supported");
@@ -1425,10 +1554,12 @@
             @NonNull File ecjOptionFile,
             @Nullable Collection<File> proguardFiles,
             @Nullable File mappingFile,
+            @NonNull Collection<File> jarJarRuleFiles,
             boolean multiDex,
             int minSdkVersion,
             boolean debugLog,
-            String javaMaxHeapSize) throws ProcessException {
+            String javaMaxHeapSize,
+            @NonNull ProcessOutputHandler processOutputHandler) throws ProcessException {
         JackProcessBuilder builder = new JackProcessBuilder();
 
         builder.setDebugLog(debugLog)
@@ -1448,8 +1579,12 @@
             builder.setMultiDex(true).setMinSdkVersion(minSdkVersion);
         }
 
+        if (jarJarRuleFiles != null) {
+            builder.setJarJarRuleFiles(jarJarRuleFiles);
+        }
+
         mJavaProcessExecutor.execute(
-                builder.build(mTargetInfo.getBuildTools()), mProcessOutputHandler)
+                builder.build(mTargetInfo.getBuildTools()), processOutputHandler)
                 .rethrowFailure().assertNormalExitValue();
     }
 
@@ -1466,7 +1601,8 @@
     public void convertLibraryToJack(
             @NonNull File inputFile,
             @NonNull File outFile,
-            @NonNull DexOptions dexOptions)
+            @NonNull DexOptions dexOptions,
+            @NonNull ProcessOutputHandler processOutputHandler)
             throws ProcessException, IOException, InterruptedException {
         checkState(mTargetInfo != null,
                 "Cannot call preJackLibrary() before setTargetInfo() is called.");
@@ -1480,7 +1616,54 @@
                 buildToolInfo,
                 mVerboseExec,
                 mJavaProcessExecutor,
-                mProcessOutputHandler);
+                processOutputHandler,
+                mLogger);
+    }
+
+    public static List<File> convertLibaryToJackUsingApis(
+            @NonNull File inputFile,
+            @NonNull File outFile,
+            @NonNull DexOptions dexOptions,
+            @NonNull BuildToolInfo buildToolInfo,
+            boolean verbose,
+            @NonNull JavaProcessExecutor processExecutor,
+            @NonNull ProcessOutputHandler processOutputHandler,
+            @NonNull ILogger logger) throws ProcessException {
+
+        BuildToolServiceLoader buildToolServiceLoader = BuildToolsServiceLoader.INSTANCE
+                .forVersion(buildToolInfo);
+        if (System.getenv("USE_JACK_API") != null) {
+            try {
+                Optional<JillProvider> jillProviderOptional = buildToolServiceLoader
+                        .getSingleService(logger, BuildToolsServiceLoader.JILL);
+
+                if (jillProviderOptional.isPresent()) {
+                    com.android.jill.api.v01.Api01Config config =
+                            jillProviderOptional.get().createConfig(
+                                    com.android.jill.api.v01.Api01Config.class);
+
+                    config.setInputJavaBinaryFile(inputFile);
+                    config.setOutputJackFile(outFile);
+                    config.setVerbose(verbose);
+
+                    Api01TranslationTask translationTask = config.getTask();
+                    translationTask.run();
+
+                    return ImmutableList.of(outFile);
+                }
+
+            } catch (ClassNotFoundException e) {
+                logger.warning("Cannot find the jill tool in the classpath, reverting to native");
+            } catch (com.android.jill.api.ConfigNotSupportedException e) {
+                logger.warning(e.getMessage() + ", reverting to native");
+            } catch (com.android.jill.api.v01.ConfigurationException e) {
+                logger.warning(e.getMessage() + ", reverting to native");
+            } catch (TranslationException e) {
+                logger.error(e, "In process translation failed, reverting to native, file a bug");
+            }
+        }
+        return convertLibraryToJack(inputFile, outFile, dexOptions, buildToolInfo, verbose,
+                processExecutor, processOutputHandler, logger);
     }
 
     public static List<File> convertLibraryToJack(
@@ -1490,7 +1673,8 @@
             @NonNull BuildToolInfo buildToolInfo,
             boolean verbose,
             @NonNull JavaProcessExecutor processExecutor,
-            @NonNull ProcessOutputHandler processOutputHandler)
+            @NonNull ProcessOutputHandler processOutputHandler,
+            @NonNull ILogger logger)
             throws ProcessException {
         checkNotNull(inputFile, "inputFile cannot be null.");
         checkNotNull(outFile, "outFile cannot be null.");
@@ -1518,6 +1702,7 @@
             builder.addArgs("--verbose");
         }
 
+        logger.verbose(builder.toString());
         JavaProcessInfo javaProcessInfo = builder.createJavaProcess();
         ProcessResult result = processExecutor.execute(javaProcessInfo, processOutputHandler);
         result.rethrowFailure().assertNormalExitValue();
@@ -1534,6 +1719,7 @@
      * @param packagedJars the jars that are packaged (libraries + jar dependencies)
      * @param javaResourcesLocation the processed Java resource folder
      * @param jniLibsFolders the folders containing jni shared libraries
+     * @param mergingFolder folder to contain files that are being merged
      * @param abiFilters optional ABI filter
      * @param jniDebugBuild whether the app should include jni debug data
      * @param signingConfig the signing configuration
@@ -1554,10 +1740,12 @@
             @NonNull Collection<File> packagedJars,
             @Nullable String javaResourcesLocation,
             @Nullable Collection<File> jniLibsFolders,
+            @NonNull File mergingFolder,
             @Nullable Set<String> abiFilters,
             boolean jniDebugBuild,
             @Nullable SigningConfig signingConfig,
             @Nullable PackagingOptions packagingOptions,
+            @Nullable SignedJarBuilder.IZipEntryFilter packagingOptionsFilter,
             @NonNull String outApkLocation)
             throws DuplicateFileException, FileNotFoundException,
             KeytoolException, PackagerException, SigningException {
@@ -1566,7 +1754,7 @@
 
         CertificateInfo certificateInfo = null;
         if (signingConfig != null && signingConfig.isSigningReady()) {
-            //noinspection ConstantConditions
+            //noinspection ConstantConditions - isSigningReady() called above.
             certificateInfo = KeystoreHelper.getCertificateInfo(signingConfig.getStoreType(),
                     signingConfig.getStoreFile(), signingConfig.getStorePassword(),
                     signingConfig.getKeyPassword(), signingConfig.getKeyAlias());
@@ -1577,8 +1765,8 @@
 
         try {
             Packager packager = new Packager(
-                    outApkLocation, androidResPkgLocation,
-                    certificateInfo, mCreatedBy, packagingOptions, mLogger);
+                    outApkLocation, androidResPkgLocation, mergingFolder,
+                    certificateInfo, mCreatedBy, packagingOptions, packagingOptionsFilter, mLogger);
 
             // add dex folder to the apk root.
             if (dexFolder != null) {
@@ -1590,29 +1778,12 @@
 
             packager.setJniDebugMode(jniDebugBuild);
 
-            // figure out conflicts!
-            JavaResourceProcessor resProcessor = new JavaResourceProcessor(packager);
-
-            if (javaResourcesLocation != null) {
-                resProcessor.addSourceFolder(javaResourcesLocation);
+            if (javaResourcesLocation != null && !packagedJars.isEmpty()) {
+                throw new PackagerException("javaResourcesLocation and packagedJars both provided");
             }
-
-            // add the resources from the jar files.
-            Set<String> hashs = Sets.newHashSet();
-
-            for (File jar : packagedJars) {
-                // TODO remove once we can properly add a library as a dependency of its test.
-                String hash = getFileHash(jar);
-                if (hash == null) {
-                    throw new PackagerException("Unable to compute hash of " + jar.getAbsolutePath());
-                }
-                if (hashs.contains(hash)) {
-                    continue;
-                }
-
-                hashs.add(hash);
-
-                packager.addResourcesFromJar(jar);
+            if (javaResourcesLocation != null || !packagedJars.isEmpty()) {
+                packager.addResources(javaResourcesLocation != null
+                        ? new File(javaResourcesLocation) : Iterables.getOnlyElement(packagedJars));
             }
 
             // also add resources from library projects and jars
@@ -1650,6 +1821,7 @@
 
         CertificateInfo certificateInfo = null;
         if (signingConfig != null && signingConfig.isSigningReady()) {
+            //noinspection ConstantConditions - isSigningReady() called above.
             certificateInfo = KeystoreHelper.getCertificateInfo(signingConfig.getStoreType(),
                     signingConfig.getStoreFile(), signingConfig.getStorePassword(),
                     signingConfig.getKeyPassword(), signingConfig.getKeyAlias());
@@ -1665,26 +1837,8 @@
                 Packager.getLocalVersion(), mCreatedBy);
 
 
-        signedJarBuilder.writeZip(new FileInputStream(in), null);
+        signedJarBuilder.writeZip(new FileInputStream(in));
         signedJarBuilder.close();
 
     }
-
-    /**
-     * Returns the hash of a file.
-     * @param file the file to hash
-     * @return the hash or null if an error happened
-     */
-    @Nullable
-    private static String getFileHash(@NonNull File file) {
-        try {
-            HashCode hashCode = Files.hash(file, Hashing.sha1());
-            return hashCode.toString();
-        } catch (IOException ignored) {
-
-        }
-
-        return null;
-    }
-
 }
diff --git a/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java b/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java
index 56e683e..bdbafb9 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java
@@ -17,10 +17,13 @@
 package com.android.builder.core;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 import com.android.jack.api.JackProvider;
+import com.android.jill.api.JillProvider;
 import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.repository.FullRevision;
+import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 
@@ -101,7 +104,7 @@
      *
      * @param <T> the type of service.
      */
-    public abstract static class Service<T> {
+    public static class Service<T> {
 
         private final Collection<String> classpath;
         private final Class<T> serviceClass;
@@ -117,18 +120,27 @@
         public Class<T> getServiceClass() {
             return serviceClass;
         }
+
+        @Override
+        public String toString() {
+            return Objects.toStringHelper(this)
+                    .add("serviceClass", serviceClass)
+                    .add("classpath", Joiner.on(",").join(classpath))
+                    .toString();
+        }
     }
 
     /**
      * Jack service description.
      */
-    public static final Service<JackProvider> JACK = new Jack();
+    public static final Service<JackProvider> JACK =
+            new Service<JackProvider>(ImmutableList.of("jack.jar"), JackProvider.class);
 
-    private static final class Jack extends Service<JackProvider> {
-        Jack() {
-            super(ImmutableList.of("jack.jar", "jill.jar"), JackProvider.class);
-        }
-    }
+    /**
+     * Jill service description.
+     */
+    public static final Service<JillProvider> JILL =
+            new Service<JillProvider>(ImmutableList.of("jill.jar"), JillProvider.class);
 
     /**
      * build-tools version specific {@link ServiceLoader} helper.
@@ -191,7 +203,7 @@
                     throw new RuntimeException(e);
                 }
             }
-            ClassLoader cl = new URLClassLoader(urls, getClass().getClassLoader());
+            ClassLoader cl = new URLClassLoader(urls, serviceType.getServiceClass().getClassLoader());
             ServiceLoader<T> serviceLoader = ServiceLoader.load(serviceType.getServiceClass(), cl);
             loadedServicesLoaders.add(new LoadedServiceLoader<T>(
                     serviceType.getServiceClass(), serviceLoader));
@@ -201,16 +213,31 @@
         /**
          * Return the first service instance for the requested service type or
          * {@link Optional#absent()} if none exist.
+         * @param logger to log resolution.
          * @param serviceType the requested service type encapsulation.
          * @param <T> the requested service class type.
          * @return the instance of T or null of none exist in this context.
          * @throws ClassNotFoundException
          */
         @NonNull
-        public <T> Optional<T> getSingleService(Service<T> serviceType) throws ClassNotFoundException {
+        public synchronized <T> Optional<T> getSingleService(
+                ILogger logger,
+                Service<T> serviceType) throws ClassNotFoundException {
+            logger.verbose("Looking for %1$s", serviceType);
             ServiceLoader<T> serviceLoader = getServiceLoader(serviceType);
+            logger.verbose("Got a serviceLoader %1$d",
+                    Integer.toHexString(System.identityHashCode(serviceLoader)));
             Iterator<T> serviceIterator = serviceLoader.iterator();
-            return Optional.fromNullable(serviceIterator.hasNext() ? serviceIterator.next() : null);
+            logger.verbose("Service Iterator =  %1$s ", serviceIterator);
+            if (serviceIterator.hasNext()) {
+                T service = serviceIterator.next();
+                logger.verbose("Got it from %1$s, loaded service = %2$s, type = %3$s",
+                        serviceIterator, service, service.getClass());
+                return Optional.of(service);
+            } else {
+                logger.info("Cannot find service implementation %1$s" + serviceType);
+                return Optional.absent();
+            }
         }
 
         @NonNull
diff --git a/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java b/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java
index f072845..412d460 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java
@@ -64,6 +64,9 @@
         return this;
     }
 
+    /**
+     * Name of this build type.
+     */
     @Override
     @NonNull
     public String getName() {
@@ -89,6 +92,22 @@
         mTestCoverageEnabled = testCoverageEnabled;
     }
 
+    /**
+     * Whether test coverage is enabled for this build type.
+     *
+     * <p>If enabled this uses Jacoco to capture coverage and creates a report in the build
+     * directory.
+     *
+     * <p>The version of Jacoco can be configured with:
+     * <pre>
+     * android {
+     *   jacoco {
+     *     version = '0.6.2.201302030002'
+     *   }
+     * }
+     * </pre>
+     *
+     */
     @Override
     public boolean isTestCoverageEnabled() {
         return mTestCoverageEnabled;
@@ -98,6 +117,12 @@
         mPseudoLocalesEnabled = pseudoLocalesEnabled;
     }
 
+    /**
+     * Whether to generate pseudo locale in the APK.
+     *
+     * <p>If enabled, 2 fake pseudo locales (en-XA and ar-XB) will be added to the APK to help
+     * test internationalization support in the app.
+     */
     @Override
     public boolean isPseudoLocalesEnabled() {
         return mPseudoLocalesEnabled;
@@ -136,7 +161,9 @@
         return this;
     }
 
-    /** Optimization level to use by the renderscript compiler. */
+    /**
+     * Optimization level to use by the renderscript compiler.
+     */
     @Override
     public int getRenderscriptOptimLevel() {
         return mRenderscriptOptimLevel;
@@ -220,6 +247,18 @@
         return mSigningConfig;
     }
 
+    /**
+     * Whether a linked Android Wear app should be embedded in variant using this build type.
+     *
+     * <p>Wear apps can be linked with the following code:
+     *
+     * <pre>
+     * dependencies {
+     *   freeWearApp project(:wear:free') // applies to variant using the free flavor
+     *   wearApp project(':wear:base') // applies to all other variants
+     * }
+     * </pre>
+     */
     @Override
     public boolean isEmbedMicroApp() {
         return mEmbedMicroApp;
@@ -237,49 +276,38 @@
 
         DefaultBuildType buildType = (DefaultBuildType) o;
 
-        if (!mName.equals(buildType.mName)) return false;
-        if (mDebuggable != buildType.mDebuggable) return false;
-        if (mTestCoverageEnabled != buildType.mTestCoverageEnabled) return false;
-        if (mJniDebuggable != buildType.mJniDebuggable) return false;
-        if (mPseudoLocalesEnabled != buildType.mPseudoLocalesEnabled) return false;
-        if (mRenderscriptDebuggable != buildType.mRenderscriptDebuggable) return false;
-        if (mRenderscriptOptimLevel != buildType.mRenderscriptOptimLevel) return false;
-        if (mMinifyEnabled != buildType.mMinifyEnabled) return false;
-        if (mZipAlignEnabled != buildType.mZipAlignEnabled) return false;
-        if (mApplicationIdSuffix != null ?
-                !mApplicationIdSuffix.equals(buildType.mApplicationIdSuffix) :
-                buildType.mApplicationIdSuffix != null)
-            return false;
-        if (mVersionNameSuffix != null ?
-                !mVersionNameSuffix.equals(buildType.mVersionNameSuffix) :
-                buildType.mVersionNameSuffix != null)
-            return false;
-        if (mSigningConfig != null ?
-                !mSigningConfig.equals(buildType.mSigningConfig) :
-                buildType.mSigningConfig != null)
-            return false;
-        if (mEmbedMicroApp != buildType.mEmbedMicroApp) return false;
-
-        return true;
+        return Objects.equal(mName, buildType.mName) &&
+                mDebuggable == buildType.mDebuggable &&
+                mTestCoverageEnabled == buildType.mTestCoverageEnabled &&
+                mJniDebuggable == buildType.mJniDebuggable &&
+                mPseudoLocalesEnabled == buildType.mPseudoLocalesEnabled &&
+                mRenderscriptDebuggable == buildType.mRenderscriptDebuggable &&
+                mRenderscriptOptimLevel == buildType.mRenderscriptOptimLevel &&
+                mMinifyEnabled == buildType.mMinifyEnabled &&
+                mZipAlignEnabled == buildType.mZipAlignEnabled &&
+                mEmbedMicroApp == buildType.mEmbedMicroApp &&
+                Objects.equal(mApplicationIdSuffix, buildType.mApplicationIdSuffix) &&
+                Objects.equal(mVersionNameSuffix, buildType.mVersionNameSuffix) &&
+                Objects.equal(mSigningConfig, buildType.mSigningConfig);
     }
 
     @Override
     public int hashCode() {
-        int result = super.hashCode();
-        result = 31 * result + (mName.hashCode());
-        result = 31 * result + (mDebuggable ? 1 : 0);
-        result = 31 * result + (mTestCoverageEnabled ? 1 : 0);
-        result = 31 * result + (mJniDebuggable ? 1 : 0);
-        result = 31 * result + (mPseudoLocalesEnabled ? 1 : 0);
-        result = 31 * result + (mRenderscriptDebuggable ? 1 : 0);
-        result = 31 * result + mRenderscriptOptimLevel;
-        result = 31 * result + (mApplicationIdSuffix != null ? mApplicationIdSuffix.hashCode() : 0);
-        result = 31 * result + (mVersionNameSuffix != null ? mVersionNameSuffix.hashCode() : 0);
-        result = 31 * result + (mMinifyEnabled ? 1 : 0);
-        result = 31 * result + (mZipAlignEnabled ? 1 : 0);
-        result = 31 * result + (mSigningConfig != null ? mSigningConfig.hashCode() : 0);
-        result = 31 * result + (mEmbedMicroApp ? 1 : 0);
-        return result;
+        return Objects.hashCode(
+                super.hashCode(),
+                mName,
+                mDebuggable,
+                mTestCoverageEnabled,
+                mJniDebuggable,
+                mPseudoLocalesEnabled,
+                mRenderscriptDebuggable,
+                mRenderscriptOptimLevel,
+                mApplicationIdSuffix,
+                mVersionNameSuffix,
+                mMinifyEnabled,
+                mZipAlignEnabled,
+                mSigningConfig,
+                mEmbedMicroApp);
     }
 
     @Override
diff --git a/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java b/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java
index 184c81c..0d1f9f1 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java
@@ -66,13 +66,13 @@
     @NonNull
     public synchronized int getVersionCode(@NonNull File manifestFile) {
         if (mVersionCode == null) {
+            mVersionCode = Optional.absent();
             try {
                 String value = getStringValue(manifestFile, "/manifest/@android:versionCode");
                 if (value != null) {
                     mVersionCode = Optional.of(Integer.valueOf(value));
                 }
             } catch (NumberFormatException ignored) {
-                mVersionCode = Optional.absent();
             }
         }
         return mVersionCode.or(-1);
diff --git a/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java b/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java
index 84984e3..6bed799 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java
@@ -16,6 +16,8 @@
 
 package com.android.builder.core;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.builder.internal.BaseConfigImpl;
@@ -23,10 +25,14 @@
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
 import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
+import java.io.File;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -63,6 +69,8 @@
     private String mTestApplicationId;
     @Nullable
     private String mTestInstrumentationRunner;
+    @NonNull
+    private Map<String, String> mTestInstrumentationRunnerArguments = Maps.newHashMap();
     @Nullable
     private Boolean mTestHandleProfiling;
     @Nullable
@@ -187,7 +195,7 @@
 
     /** Sets the targetSdkVersion to the given value. */
     @NonNull
-    public ProductFlavor setTargetSdkVersion(ApiVersion targetSdkVersion) {
+    public ProductFlavor setTargetSdkVersion(@Nullable ApiVersion targetSdkVersion) {
         mTargetSdkVersion = targetSdkVersion;
         return this;
     }
@@ -292,6 +300,36 @@
         return mTestInstrumentationRunner;
     }
 
+    /** Sets the test instrumentation runner custom arguments. */
+    @NonNull
+    public ProductFlavor setTestInstrumentationRunnerArguments(
+            @NonNull Map<String, String> testInstrumentationRunnerArguments) {
+        mTestInstrumentationRunnerArguments = checkNotNull(testInstrumentationRunnerArguments);
+        return this;
+    }
+
+    /**
+     * Test instrumentation runner custom arguments.
+     *
+     * e.g. <code>[key: "value"]</code> will give
+     * <code>adb shell am instrument -w <b>-e key value</b> com.example</code>...".
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html">
+     * instrumentation</a>.
+     *
+     * <p>Test runner arguments can also be specified from the command line:
+     *
+     * <p><pre>
+     * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest
+     * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar
+     * </pre>
+     */
+    @Override
+    @NonNull
+    public Map<String, String> getTestInstrumentationRunnerArguments() {
+        return mTestInstrumentationRunnerArguments;
+    }
+
     /**
      * See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html">
      * instrumentation</a>.
@@ -340,6 +378,9 @@
         return this;
     }
 
+    /**
+     * Adds a res config filter (for instance 'hdpi')
+     */
     public void addResourceConfiguration(@NonNull String configuration) {
         if (mResourceConfiguration == null) {
             mResourceConfiguration = Sets.newHashSet();
@@ -348,6 +389,9 @@
         mResourceConfiguration.add(configuration);
     }
 
+    /**
+     * Adds a res config filter (for instance 'hdpi')
+     */
     public void addResourceConfigurations(@NonNull String... configurations) {
         if (mResourceConfiguration == null) {
             mResourceConfiguration = Sets.newHashSet();
@@ -356,6 +400,9 @@
         mResourceConfiguration.addAll(Arrays.asList(configurations));
     }
 
+    /**
+     * Adds a res config filter (for instance 'hdpi')
+     */
     public void addResourceConfigurations(@NonNull Collection<String> configurations) {
         if (mResourceConfiguration == null) {
             mResourceConfiguration = Sets.newHashSet();
@@ -364,6 +411,9 @@
         mResourceConfiguration.addAll(configurations);
     }
 
+    /**
+     * Adds a res config filter (for instance 'hdpi')
+     */
     @NonNull
     @Override
     public Collection<String> getResourceConfigurations() {
@@ -421,6 +471,11 @@
                 overlay.getTestInstrumentationRunner(),
                 base.getTestInstrumentationRunner());
 
+        flavor.mTestInstrumentationRunnerArguments.putAll(
+                base.getTestInstrumentationRunnerArguments());
+        flavor.mTestInstrumentationRunnerArguments.putAll(
+                overlay.getTestInstrumentationRunnerArguments());
+
         flavor.mTestHandleProfiling = chooseNotNull(
                 overlay.getTestHandleProfiling(),
                 base.getTestHandleProfiling());
@@ -453,6 +508,12 @@
 
         flavor.setMultiDexKeepProguard(chooseNotNull(
                 overlay.getMultiDexKeepProguard(), base.getMultiDexKeepProguard()));
+
+        flavor.setJarJarRuleFiles(ImmutableList.<File>builder()
+                .addAll(overlay.getJarJarRuleFiles())
+                .addAll(base.getJarJarRuleFiles())
+                .build());
+
         return flavor;
     }
 
@@ -482,6 +543,7 @@
 
         flavor.mTestApplicationId = productFlavor.getTestApplicationId();
         flavor.mTestInstrumentationRunner = productFlavor.getTestInstrumentationRunner();
+        flavor.mTestInstrumentationRunnerArguments = productFlavor.getTestInstrumentationRunnerArguments();
         flavor.mTestHandleProfiling = productFlavor.getTestHandleProfiling();
         flavor.mTestFunctionalTest = productFlavor.getTestFunctionalTest();
 
@@ -497,6 +559,7 @@
 
         flavor.setMultiDexKeepFile(productFlavor.getMultiDexKeepFile());
         flavor.setMultiDexKeepProguard(productFlavor.getMultiDexKeepProguard());
+        flavor.setJarJarRuleFiles(ImmutableList.copyOf(productFlavor.getJarJarRuleFiles()));
 
         return flavor;
     }
@@ -519,109 +582,50 @@
 
         DefaultProductFlavor that = (DefaultProductFlavor) o;
 
-        if (mDimension !=null ? !mDimension.equals(that.mDimension) :
-                that.mDimension != null) {
-            return false;
-        }
-
-        if (mApplicationId != null ? !mApplicationId.equals(that.mApplicationId)
-                : that.mApplicationId != null) {
-            return false;
-        }
-        if (mMaxSdkVersion != null ? !mMaxSdkVersion.equals(that.mMaxSdkVersion)
-                : that.mMaxSdkVersion != null) {
-            return false;
-        }
-        if (mMinSdkVersion != null ? !mMinSdkVersion.equals(that.mMinSdkVersion)
-                : that.mMinSdkVersion != null) {
-            return false;
-        }
-        if (!mName.equals(that.mName)) {
-            return false;
-        }
-        if (mRenderscriptNdkModeEnabled != null ? !mRenderscriptNdkModeEnabled
-                .equals(that.mRenderscriptNdkModeEnabled)
-                : that.mRenderscriptNdkModeEnabled != null) {
-            return false;
-        }
-        if (mRenderscriptSupportModeEnabled != null ? !mRenderscriptSupportModeEnabled
-                .equals(that.mRenderscriptSupportModeEnabled) : that.mRenderscriptSupportModeEnabled
-                != null) {
-            return false;
-        }
-        if (mRenderscriptTargetApi != null ? !mRenderscriptTargetApi
-                .equals(that.mRenderscriptTargetApi)
-                : that.mRenderscriptTargetApi != null) {
-            return false;
-        }
-        if (mResourceConfiguration != null ? !mResourceConfiguration
-                .equals(that.mResourceConfiguration)
-                : that.mResourceConfiguration != null) {
-            return false;
-        }
-        if (mSigningConfig != null ? !mSigningConfig.equals(that.mSigningConfig)
-                : that.mSigningConfig != null) {
-            return false;
-        }
-        if (mTargetSdkVersion != null ? !mTargetSdkVersion.equals(that.mTargetSdkVersion)
-                : that.mTargetSdkVersion != null) {
-            return false;
-        }
-        if (mTestApplicationId != null ? !mTestApplicationId.equals(that.mTestApplicationId)
-                : that.mTestApplicationId != 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 (mVersionCode != null ? !mVersionCode.equals(that.mVersionCode)
-                : that.mVersionCode != null) {
-            return false;
-        }
-        if (mVersionName != null ? !mVersionName.equals(that.mVersionName)
-                : that.mVersionName != null) {
-            return false;
-        }
-
-        return true;
+        return Objects.equal(mDimension, that.mDimension) &&
+                Objects.equal(mApplicationId, that.mApplicationId) &&
+                Objects.equal(mMaxSdkVersion, that.mMaxSdkVersion) &&
+                Objects.equal(mMinSdkVersion, that.mMinSdkVersion) &&
+                Objects.equal(mName, that.mName) &&
+                Objects.equal(mRenderscriptNdkModeEnabled, that.mRenderscriptNdkModeEnabled) &&
+                Objects.equal(mRenderscriptSupportModeEnabled,
+                        that.mRenderscriptSupportModeEnabled) &&
+                Objects.equal(mRenderscriptTargetApi, that.mRenderscriptTargetApi) &&
+                Objects.equal(mResourceConfiguration, that.mResourceConfiguration) &&
+                Objects.equal(mSigningConfig, that.mSigningConfig) &&
+                Objects.equal(mTargetSdkVersion, that.mTargetSdkVersion) &&
+                Objects.equal(mTestApplicationId, that.mTestApplicationId) &&
+                Objects.equal(mTestFunctionalTest, that.mTestFunctionalTest) &&
+                Objects.equal(mTestHandleProfiling, that.mTestHandleProfiling) &&
+                Objects.equal(mTestInstrumentationRunner, that.mTestInstrumentationRunner) &&
+                Objects.equal(mTestInstrumentationRunnerArguments,
+                        that.mTestInstrumentationRunnerArguments) &&
+                Objects.equal(mVersionCode, that.mVersionCode) &&
+                Objects.equal(mVersionName, that.mVersionName);
     }
 
     @Override
     public int hashCode() {
-        int result = super.hashCode();
-        result = 31 * result + mName.hashCode();
-        result = 31 * result + (mDimension != null ? mDimension.hashCode() : 0);
-        result = 31 * result + (mMinSdkVersion != null ? mMinSdkVersion.hashCode() : 0);
-        result = 31 * result + (mTargetSdkVersion != null ? mTargetSdkVersion.hashCode() : 0);
-        result = 31 * result + (mMaxSdkVersion != null ? mMaxSdkVersion.hashCode() : 0);
-        result = 31 * result + (mRenderscriptTargetApi != null ? mRenderscriptTargetApi.hashCode()
-                : 0);
-        result = 31 * result + (mRenderscriptSupportModeEnabled != null ? mRenderscriptSupportModeEnabled
-                .hashCode()
-                : 0);
-        result = 31 * result + (mRenderscriptNdkModeEnabled != null ? mRenderscriptNdkModeEnabled.hashCode() : 0);
-        result = 31 * result + (mVersionCode != null ? mVersionCode.hashCode() : 0);
-        result = 31 * result + (mVersionName != null ? mVersionName.hashCode() : 0);
-        result = 31 * result + (mApplicationId != null ? mApplicationId.hashCode() : 0);
-        result = 31 * result + (mTestApplicationId != null ? mTestApplicationId.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;
+        return Objects.hashCode(
+                super.hashCode(),
+                mName,
+                mDimension,
+                mMinSdkVersion,
+                mTargetSdkVersion,
+                mMaxSdkVersion,
+                mRenderscriptTargetApi,
+                mRenderscriptSupportModeEnabled,
+                mRenderscriptNdkModeEnabled,
+                mVersionCode,
+                mVersionName,
+                mApplicationId,
+                mTestApplicationId,
+                mTestInstrumentationRunner,
+                mTestInstrumentationRunnerArguments,
+                mTestHandleProfiling,
+                mTestFunctionalTest,
+                mSigningConfig,
+                mResourceConfiguration);
     }
 
     @Override
@@ -640,6 +644,7 @@
                 .add("applicationId", mApplicationId)
                 .add("testApplicationId", mTestApplicationId)
                 .add("testInstrumentationRunner", mTestInstrumentationRunner)
+                .add("testInstrumentationRunnerArguments", mTestInstrumentationRunnerArguments)
                 .add("testHandleProfiling", mTestHandleProfiling)
                 .add("testFunctionalTest", mTestFunctionalTest)
                 .add("signingConfig", mSigningConfig)
diff --git a/build-system/builder/src/main/java/com/android/builder/core/ErrorReporter.java b/build-system/builder/src/main/java/com/android/builder/core/ErrorReporter.java
new file mode 100644
index 0000000..359920f
--- /dev/null
+++ b/build-system/builder/src/main/java/com/android/builder/core/ErrorReporter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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.core;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.SyncIssue;
+import com.android.ide.common.blame.MessageReceiver;
+
+/**
+ * An error reporter for project evaluation and execution.
+ *
+ * The behavior of the reporter must vary depending on the evaluation mode
+ * ({@link ErrorReporter.EvaluationMode}), indicating whether
+ * the IDE is querying the project or not.
+ */
+public abstract class ErrorReporter implements MessageReceiver {
+
+    public enum EvaluationMode {
+        /** Standard mode, errors should be breaking */
+        STANDARD,
+        /**
+         * IDE mode. Errors should not be breaking and should generate a SyncIssue instead.
+         */
+        IDE,
+        /** Legacy IDE mode (Studio 1.0), where SyncIssue are not understood by the IDE. */
+        IDE_LEGACY
+    }
+
+    @NonNull
+    private final EvaluationMode mMode;
+
+    protected ErrorReporter(@NonNull EvaluationMode mode) {
+        mMode = mode;
+    }
+
+    @NonNull
+    public EvaluationMode getMode() {
+        return mMode;
+    }
+
+    @NonNull
+    public abstract SyncIssue handleSyncError(@NonNull String data, int type, @NonNull String msg);
+}
diff --git a/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java b/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java
index 0beb384..6d6cbff 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java
@@ -48,6 +48,8 @@
     private boolean mMultiDex = false;
     private int mMinSdkVersion = 21;
     private File mEcjOptionFile = null;
+    private Collection<File> mJarJarRuleFiles = null;
+    private File mIncrementalDir = null;
 
     public JackProcessBuilder() {
     }
@@ -133,6 +135,12 @@
     }
 
     @NonNull
+    public JackProcessBuilder setJarJarRuleFiles(@NonNull Collection<File> jarJarRuleFiles) {
+        mJarJarRuleFiles = jarJarRuleFiles;
+        return this;
+    }
+
+    @NonNull
     public JavaProcessInfo build(@NonNull BuildToolInfo buildToolInfo) throws ProcessException {
 
         FullRevision revision = buildToolInfo.getRevision();
@@ -201,6 +209,12 @@
             }
         }
 
+        if (mJarJarRuleFiles != null) {
+            for (File jarjarRuleFile : mJarJarRuleFiles) {
+                builder.addArgs("--config-jarjar", jarjarRuleFile.getAbsolutePath());
+            }
+        }
+
         builder.addArgs("@" + mEcjOptionFile.getAbsolutePath());
 
         return builder.createJavaProcess();
diff --git a/build-system/builder/src/main/java/com/android/builder/core/LibraryRequest.java b/build-system/builder/src/main/java/com/android/builder/core/LibraryRequest.java
new file mode 100644
index 0000000..7e0451b
--- /dev/null
+++ b/build-system/builder/src/main/java/com/android/builder/core/LibraryRequest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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.core;
+
+import com.android.annotations.NonNull;
+
+/**
+ * a request for an optional library.
+ */
+public class LibraryRequest {
+
+    @NonNull
+    private final String mName;
+    private final boolean mRequired;
+
+    public LibraryRequest(@NonNull String name, boolean required) {
+        mName = name;
+        mRequired = required;
+    }
+
+    /**
+     * The name of the library. This is the unique name that will show up in the manifest.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Whether the library is required by the app or just optional.
+     */
+    public boolean isRequired() {
+        return mRequired;
+    }
+}
diff --git a/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java b/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java
index 6bfc479..afa5e43 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java
@@ -26,7 +26,6 @@
 import com.android.builder.dependency.DependencyContainer;
 import com.android.builder.dependency.JarDependency;
 import com.android.builder.dependency.LibraryDependency;
-import com.android.utils.StringHelper;
 import com.android.builder.model.ApiVersion;
 import com.android.builder.model.BuildType;
 import com.android.builder.model.ClassField;
@@ -35,15 +34,14 @@
 import com.android.builder.model.SourceProvider;
 import com.android.ide.common.res2.AssetSet;
 import com.android.ide.common.res2.ResourceSet;
-import com.google.common.collect.ArrayListMultimap;
+import com.android.utils.StringHelper;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 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 java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -398,8 +396,10 @@
             }
 
             if (!mFlavors.isEmpty()) {
+                boolean first = true;
                 for (F flavor : mFlavors) {
-                    sb.append(flavor.getName());
+                    sb.append(first ? flavor.getName() : StringHelper.capitalize(flavor.getName()));
+                    first = false;
                 }
 
                 sb.append('/').append(mBuildType.getName());
@@ -511,114 +511,6 @@
     }
 
     /**
-     * Checks the SourceProviders to ensure that they don't use the same folders.
-     *
-     * This can't be allowed as it can break incremental support, or
-     * generate dup files in resulting APK.
-     *
-     * @throws java.lang.RuntimeException if a dup is found.
-     */
-    public void checkSourceProviders() {
-        List<SourceProvider> providers = Lists.newArrayListWithExpectedSize(
-                4 + mFlavorSourceProviders.size());
-
-        providers.add(mDefaultSourceProvider);
-        if (mBuildTypeSourceProvider != null) {
-            providers.add(mBuildTypeSourceProvider);
-        }
-
-        providers.addAll(mFlavorSourceProviders);
-        if (mVariantSourceProvider != null) {
-            providers.add(mVariantSourceProvider);
-        }
-        if (mMultiFlavorSourceProvider != null) {
-            providers.add(mMultiFlavorSourceProvider);
-        }
-
-        try {
-            checkFileSourceSet(providers, SourceProvider.class.getMethod("getManifestFile"), "manifest");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getJavaDirectories"), "java");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getResourcesDirectories"), "resources");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getAidlDirectories"), "aidl");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getRenderscriptDirectories"), "rs");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getCDirectories"), "c");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getCppDirectories"), "cpp");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getResDirectories"), "res");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getAssetsDirectories"), "assets");
-            checkFileCollectionSourceSet(providers,
-                    SourceProvider.class.getMethod("getJniLibsDirectories"), "jniLibs");
-
-        } catch (NoSuchMethodException ignored) {
-        } catch (InvocationTargetException ignored) {
-        } catch (IllegalAccessException ignored) {
-        }
-    }
-
-    private static void checkFileSourceSet(
-            List<SourceProvider> providers,
-            Method m,
-            @NonNull String name)
-            throws IllegalAccessException, InvocationTargetException {
-        Map<String, File> map = Maps.newHashMap();
-        for (SourceProvider sourceProvider : providers) {
-            File file = (File) m.invoke(sourceProvider);
-
-            if (!map.isEmpty()) {
-                for (Map.Entry<String, File> entry : map.entrySet()) {
-                    if (file.equals(entry.getValue())) {
-                        throw new RuntimeException(String.format(
-                                "SourceSets '%s' and '%s' use the same file for '%s': %s",
-                                sourceProvider.getName(),
-                                entry.getKey(),
-                                name,
-                                file));
-                    }
-                }
-            }
-
-            map.put(sourceProvider.getName(), file);
-        }
-    }
-
-    private static void checkFileCollectionSourceSet(
-            List<SourceProvider> providers,
-            Method m,
-            @NonNull String name)
-            throws IllegalAccessException, InvocationTargetException {
-        Multimap<String, File> map = ArrayListMultimap.create();
-        for (SourceProvider sourceProvider : providers) {
-            //noinspection unchecked
-            Collection<File> list = (Collection<File>) m.invoke(sourceProvider);
-
-            if (!map.isEmpty()) {
-                for (Map.Entry<String, Collection<File>> entry : map.asMap().entrySet()) {
-                    Collection<File> existingFiles = entry.getValue();
-                    for (File f : list) {
-                        if (existingFiles.contains(f)) {
-                            throw new RuntimeException(String.format(
-                                    "SourceSets '%s' and '%s' use the same file/folder for '%s': %s",
-                                    sourceProvider.getName(),
-                                    entry.getKey(),
-                                    name,
-                                    f));
-                        }
-                    }
-                }
-            }
-
-            map.putAll(sourceProvider.getName(), list);
-        }
-    }
-
-    /**
      * Sets the variant-specific source provider.
      * @param sourceProvider the source provider for the product flavor
      *
@@ -678,9 +570,6 @@
 
         resolveIndirectLibraryDependencies(mDirectLibraries, mFlatLibraries);
 
-        for (LibraryDependency libraryDependency : mFlatLibraries) {
-            mLocalJars.addAll(libraryDependency.getLocalDependencies());
-        }
         return this;
     }
 
@@ -947,16 +836,12 @@
         String versionName = mMergedFlavor.getVersionName();
         String versionSuffix = mBuildType.getVersionNameSuffix();
 
-        if (versionSuffix != null && !versionSuffix.isEmpty()) {
-            if (versionName == null) {
-                if (!mType.isForTesting()) {
-                    versionName = getVersionNameFromManifest();
-                } else {
-                    versionName = "";
-                }
-            }
+        if (versionName == null && !mType.isForTesting()) {
+            versionName = getVersionNameFromManifest();
+        }
 
-            versionName = versionName + versionSuffix;
+        if (versionSuffix != null && !versionSuffix.isEmpty()) {
+            versionName = Strings.nullToEmpty(versionName) + versionSuffix;
         }
 
         return versionName;
@@ -1001,6 +886,20 @@
     }
 
     /**
+     * Returns the instrumentationRunner arguments to use to test this variant, or if the
+     * variant is a test, the ones to use to test the tested variant
+     */
+    @NonNull
+    public Map<String, String> getInstrumentationRunnerArguments() {
+        VariantConfiguration config = this;
+        if (mType.isForTesting()) {
+            config = getTestedConfig();
+            checkState(config != null);
+        }
+        return config.mMergedFlavor.getTestInstrumentationRunnerArguments();
+    }
+
+    /**
      * 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
@@ -1217,11 +1116,13 @@
         if (includeDependencies) {
             for (int n = mFlatLibraries.size() - 1 ; n >= 0 ; n--) {
                 LibraryDependency dependency = mFlatLibraries.get(n);
-                File resFolder = dependency.getResFolder();
-                if (resFolder.isDirectory()) {
-                    ResourceSet resourceSet = new ResourceSet(dependency.getFolder().getName());
-                    resourceSet.addSource(resFolder);
-                    resourceSets.add(resourceSet);
+                if (!dependency.isOptional()) {
+                    File resFolder = dependency.getResFolder();
+                    if (resFolder.isDirectory()) {
+                        ResourceSet resourceSet = new ResourceSet(dependency.getFolder().getName());
+                        resourceSet.addSource(resFolder);
+                        resourceSets.add(resourceSet);
+                    }
                 }
             }
         }
@@ -1523,13 +1424,15 @@
         }
 
         for (LibraryDependency libraryDependency : mFlatLibraries) {
-            File libJar = libraryDependency.getJarFile();
-            if (libJar.exists()) {
-                jars.add(libJar);
-            }
-            for (File jarFile : libraryDependency.getLocalJars()) {
-                if (jarFile.isFile()) {
-                    jars.add(jarFile);
+            if (!libraryDependency.isOptional()) {
+                File libJar = libraryDependency.getJarFile();
+                if (libJar.exists()) {
+                    jars.add(libJar);
+                }
+                for (File jarFile : libraryDependency.getLocalJars()) {
+                    if (jarFile.isFile()) {
+                        jars.add(jarFile);
+                    }
                 }
             }
         }
@@ -1560,6 +1463,21 @@
             }
         }
 
+        for (LibraryDependency libraryDependency : mFlatLibraries) {
+            if (libraryDependency.isOptional()) {
+                File libJar = libraryDependency.getJarFile();
+                if (libJar.exists()) {
+                    jars.add(libJar);
+                }
+                for (File jarFile : libraryDependency.getLocalJars()) {
+                    if (jarFile.isFile()) {
+                        jars.add(jarFile);
+                    }
+                }
+            }
+        }
+
+
         return Lists.newArrayList(jars);
     }
 
@@ -1926,4 +1844,12 @@
         // default is false.
         return false;
     }
+
+    public Collection<File> getJarJarRuleFiles() {
+
+        ImmutableList.Builder<File> jarjarRuleFiles = ImmutableList.builder();
+        jarjarRuleFiles.addAll(getMergedFlavor().getJarJarRuleFiles());
+        jarjarRuleFiles.addAll(mBuildType.getJarJarRuleFiles());
+        return jarjarRuleFiles.build();
+    }
 }
diff --git a/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java b/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java
index 0beb612..e9a6129 100644
--- a/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java
+++ b/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java
@@ -185,7 +185,7 @@
     @Override
     @NonNull
     public File getLintJar() {
-        return new File(mBundleFolder, "lint.jar");
+        return new File(getJarsRootFolder(), "lint.jar");
     }
 
     @Override
diff --git a/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java b/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java
index bf77caf..07eaaff 100644
--- a/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java
+++ b/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java
@@ -30,4 +30,15 @@
      */
     @NonNull
     File getSymbolFile();
+
+    /**
+     * Returns whether the library is considered optional, meaning that it may or may not
+     * be present in the final APK.
+     *
+     * If the library is optional, then:
+     * - if the consumer is a library, it'll get skipped from resource merging and won't show up
+     *   in the consumer R.txt
+     * - if the consumer is a separate test project, all the resources gets skipped from merging.
+     */
+    boolean isOptional();
 }
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java b/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
index acd6134..52298f4 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 import com.android.builder.model.BaseConfig;
 import com.android.builder.model.ClassField;
+import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -49,40 +50,83 @@
     @Nullable
     private File mMultiDexKeepFile;
 
+    @NonNull
+    private List<File> mJarJarRuleFiles = Lists.newArrayList();
+
+    /**
+     * Adds a BuildConfig field.
+     */
     public void addBuildConfigField(@NonNull ClassField field) {
         mBuildConfigFields.put(field.getName(), field);
     }
 
-    public void addResValue(@NonNull ClassField field) {
-        mResValues.put(field.getName(), field);
+    /**
+     * Adds a generated resource value.
+     */
+    public void addResValue(@NonNull ClassField field) { mResValues.put(field.getName(), field);
     }
 
+    /**
+     * Adds a generated resource value.
+     */
     public void addResValues(@NonNull Map<String, ClassField> values) {
         mResValues.putAll(values);
     }
 
+    /**
+     * Returns the BuildConfig fields.
+     */
     @Override
     @NonNull
     public Map<String, ClassField> getBuildConfigFields() {
         return mBuildConfigFields;
     }
 
+    /**
+     * Adds BuildConfig fields.
+     */
     public void addBuildConfigFields(@NonNull Map<String, ClassField> fields) {
         mBuildConfigFields.putAll(fields);
     }
 
+    /**
+     * Returns the generated resource values.
+     */
     @NonNull
     @Override
     public Map<String, ClassField> getResValues() {
         return mResValues;
     }
 
+    /**
+     * Returns ProGuard configuration files to be used.
+     *
+     * <p>There are 2 default rules files
+     * <ul>
+     *     <li>proguard-android.txt
+     *     <li>proguard-android-optimize.txt
+     * </ul>
+     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+     * full path to the files. They are identical except for enabling optimizations.
+     *
+     * <p>See similarly named methods to specify the files.
+     */
     @Override
     @NonNull
     public List<File> getProguardFiles() {
         return mProguardFiles;
     }
 
+    /**
+     * ProGuard rule files to be included in the published AAR.
+     *
+     * <p>These proguard rule files will then be used by any application project that consumes the
+     * AAR (if ProGuard is enabled).
+     *
+     * <p>This allows AAR to specify shrinking or obfuscation exclude rules.
+     *
+     * <p>This is only valid for Library project. This is ignored in Application project.
+     */
     @Override
     @NonNull
     public List<File> getConsumerProguardFiles() {
@@ -95,16 +139,34 @@
         return mTestProguardFiles;
     }
 
+    /**
+     * Returns the manifest placeholders.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-Placeholder-support">
+     *     Manifest merger</a>.
+     */
     @NonNull
     @Override
     public Map<String, Object> getManifestPlaceholders() {
         return mManifestPlaceholders;
     }
 
+    /**
+     * Adds manifest placeholders.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-Placeholder-support">
+     *     Manifest merger</a>.
+     */
     public void addManifestPlaceholders(@NonNull Map<String, Object> manifestPlaceholders) {
         mManifestPlaceholders.putAll(manifestPlaceholders);
     }
 
+    /**
+     * Sets a new set of manifest placeholders.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-Placeholder-support">
+     *     Manifest merger</a>.
+     */
     public void setManifestPlaceholders(@NonNull Map<String, Object> manifestPlaceholders) {
         mManifestPlaceholders.clear();
         this.mManifestPlaceholders.putAll(manifestPlaceholders);
@@ -130,6 +192,8 @@
 
         mMultiDexKeepFile = that.getMultiDexKeepFile();
         mMultiDexKeepProguard = that.getMultiDexKeepProguard();
+
+        mJarJarRuleFiles = that.getJarJarRuleFiles();
     }
 
     private void setBuildConfigFields(@NonNull Map<String, ClassField> fields) {
@@ -175,6 +239,16 @@
         mMultiDexKeepProguard = file;
     }
 
+    public void setJarJarRuleFiles(@NonNull List<File> files) {
+        mJarJarRuleFiles = files;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getJarJarRuleFiles() {
+        return mJarJarRuleFiles;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -186,48 +260,30 @@
 
         BaseConfigImpl that = (BaseConfigImpl) o;
 
-        if (!mBuildConfigFields.equals(that.mBuildConfigFields)) {
-            return false;
-        }
-        if (!mConsumerProguardFiles.equals(that.mConsumerProguardFiles)) {
-            return false;
-        }
-        if (!mManifestPlaceholders.equals(that.mManifestPlaceholders)) {
-            return false;
-        }
-        if (mMultiDexEnabled != null ? !mMultiDexEnabled.equals(that.mMultiDexEnabled) :
-                that.mMultiDexEnabled != null) {
-            return false;
-        }
-        if (mMultiDexKeepFile != null ? !mMultiDexKeepFile.equals(that.mMultiDexKeepFile) :
-                that.mMultiDexKeepFile != null) {
-            return false;
-        }
-        if (mMultiDexKeepProguard != null ? !mMultiDexKeepProguard.equals(that.mMultiDexKeepProguard) :
-                that.mMultiDexKeepProguard != null) {
-            return false;
-        }
-        if (!mProguardFiles.equals(that.mProguardFiles)) {
-            return false;
-        }
-        if (!mResValues.equals(that.mResValues)) {
-            return false;
-        }
+        return Objects.equal(mBuildConfigFields, that.mBuildConfigFields) &&
+                Objects.equal(mConsumerProguardFiles, that.mConsumerProguardFiles) &&
+                Objects.equal(mManifestPlaceholders, that.mManifestPlaceholders) &&
+                Objects.equal(mMultiDexEnabled, that.mMultiDexEnabled) &&
+                Objects.equal(mMultiDexKeepFile, that.mMultiDexKeepFile) &&
+                Objects.equal(mMultiDexKeepProguard, that.mMultiDexKeepProguard) &&
+                Objects.equal(mProguardFiles, that.mProguardFiles) &&
+                Objects.equal(mResValues, that.mResValues) &&
+                Objects.equal(mJarJarRuleFiles, that.mJarJarRuleFiles);
 
-        return true;
     }
 
     @Override
     public int hashCode() {
-        int result = mBuildConfigFields.hashCode();
-        result = 31 * result + mResValues.hashCode();
-        result = 31 * result + mProguardFiles.hashCode();
-        result = 31 * result + mConsumerProguardFiles.hashCode();
-        result = 31 * result + mManifestPlaceholders.hashCode();
-        result = 31 * result + (mMultiDexEnabled != null ? mMultiDexEnabled.hashCode() : 0);
-        result = 31 * result + (mMultiDexKeepFile != null ? mMultiDexKeepFile.hashCode() : 0);
-        result = 31 * result + (mMultiDexKeepProguard != null ? mMultiDexKeepProguard.hashCode() : 0);
-        return result;
+        return Objects.hashCode(
+                mBuildConfigFields,
+                mResValues,
+                mProguardFiles,
+                mConsumerProguardFiles,
+                mManifestPlaceholders,
+                mMultiDexEnabled,
+                mMultiDexKeepFile,
+                mMultiDexKeepProguard,
+                mJarJarRuleFiles);
     }
 
     @Override
@@ -241,6 +297,7 @@
                 ", mMultiDexEnabled=" + mMultiDexEnabled +
                 ", mMultiDexKeepFile=" + mMultiDexKeepFile +
                 ", mMultiDexKeepProguard=" + mMultiDexKeepProguard +
+                ", mJarJarRuleFiles=" + mJarJarRuleFiles +
                 '}';
     }
 }
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java b/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java
index a9a7edc..2338c95 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java
@@ -24,6 +24,7 @@
 import com.android.sdklib.ISystemImage;
 import com.android.sdklib.repository.descriptors.IdDisplay;
 import com.android.utils.SparseArray;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import java.io.File;
@@ -139,6 +140,7 @@
         return "android";
     }
 
+    @NonNull
     @Override
     public AndroidVersion getVersion() {
         return new AndroidVersion(mApiLevel, null);
@@ -169,6 +171,7 @@
         return false;
     }
 
+    @NonNull
     @Override
     public File[] getSkins() {
         return new File[0];
@@ -179,9 +182,16 @@
         return null;
     }
 
+    @NonNull
     @Override
-    public IOptionalLibrary[] getOptionalLibraries() {
-        return new IOptionalLibrary[0];
+    public List<OptionalLibrary> getAdditionalLibraries() {
+        return ImmutableList.of();
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getOptionalLibraries() {
+        return ImmutableList.of();
     }
 
     @Override
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java b/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java
index 9c159b1..8adbcc3 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java
@@ -24,6 +24,7 @@
 import com.android.ide.common.process.ProcessOutputHandler;
 import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.repository.FullRevision;
+import com.android.utils.ILogger;
 import com.android.utils.Pair;
 import com.google.common.io.Files;
 
@@ -90,7 +91,8 @@
             @NonNull BuildToolInfo buildToolInfo,
             boolean verbose,
             @NonNull JavaProcessExecutor processExecutor,
-            @NonNull ProcessOutputHandler processOutputHandler)
+            @NonNull ProcessOutputHandler processOutputHandler,
+            @NonNull ILogger logger)
             throws ProcessException, InterruptedException, IOException {
 
         Key itemKey = Key.of(inputFile, buildToolInfo.getRevision());
@@ -102,14 +104,15 @@
         if (pair.getSecond()) {
             try {
                 // haven't process this file yet so do it and record it.
-                List<File> files = AndroidBuilder.convertLibraryToJack(
+                List<File> files = AndroidBuilder.convertLibaryToJackUsingApis(
                         inputFile,
                         outFile,
                         dexOptions,
                         buildToolInfo,
                         verbose,
                         processExecutor,
-                        processOutputHandler);
+                        processOutputHandler,
+                        logger);
                 item.getOutputFiles().addAll(files);
 
                 incrementMisses();
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java b/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
index 3bee2fc..da566c1 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
@@ -101,9 +101,7 @@
 
     private final int mTargetApi;
 
-    private final boolean mDebugBuild;
-
-    private final int mOptimLevel;
+    private final int mOptimizationLevel;
 
     private final boolean mNdkMode;
 
@@ -123,7 +121,7 @@
             @NonNull BuildToolInfo buildToolInfo,
             int targetApi,
             boolean debugBuild,
-            int optimLevel,
+            int optimizationLevel,
             boolean ndkMode,
             boolean supportMode,
             @Nullable Set<String> abiFilters) {
@@ -135,8 +133,7 @@
         mLibOutputDir = libOutputDir;
         mBuildToolInfo = buildToolInfo;
         mTargetApi = targetApi;
-        mDebugBuild = debugBuild;
-        mOptimLevel = optimLevel;
+        mOptimizationLevel = optimizationLevel;
         mNdkMode = ndkMode;
         mSupportMode = supportMode;
         mAbiFilters = abiFilters;
@@ -226,7 +223,7 @@
 //        }
 
         builder.addArgs("-O");
-        builder.addArgs(Integer.toString(mOptimLevel));
+        builder.addArgs(Integer.toString(mOptimizationLevel));
 
         // add all import paths
         builder.addArgs("-I");
@@ -349,7 +346,7 @@
         builder.setExecutable(mBuildToolInfo.getPath(BuildToolInfo.PathId.BCC_COMPAT));
         builder.addEnvironments(env);
 
-        builder.addArgs("-O" + Integer.toString(mOptimLevel));
+        builder.addArgs("-O" + Integer.toString(mOptimizationLevel));
 
         File outFile = new File(objAbiFolder, objName);
         builder.addArgs("-o", outFile.getAbsolutePath());
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java b/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
index 2648b6a..1356d4d 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
@@ -98,7 +98,7 @@
         return processDependencyData(content);
     }
 
-    private static enum ParseMode {
+    private enum ParseMode {
         OUTPUT, MAIN, SECONDARY, DONE
     }
 
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java b/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
index 22917f8..f3db39d 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
@@ -28,12 +28,13 @@
 import com.android.builder.packaging.SealedPackageException;
 import com.android.builder.signing.SignedJarBuilder;
 import com.android.builder.signing.SignedJarBuilder.IZipEntryFilter;
-import com.android.ide.common.packaging.PackagingUtils;
 import com.android.ide.common.signing.CertificateInfo;
+import com.android.utils.FileUtils;
 import com.android.utils.ILogger;
-import com.google.common.collect.Sets;
 import com.google.common.io.Closeables;
+import com.google.common.io.Files;
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -44,11 +45,9 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
@@ -69,10 +68,10 @@
             Pattern.CASE_INSENSITIVE);
 
     /**
-     * A No-op zip filter. It's used to detect conflicts.
+     * Filter to detect duplicate entries
      *
      */
-    private final class NullZipFilter implements IZipEntryFilter {
+    private final class DuplicateZipFilter implements IZipEntryFilter {
         private File mInputFile;
 
         void reset(File inputFile) {
@@ -85,7 +84,13 @@
 
             File duplicate = checkFileForDuplicate(archivePath);
             if (duplicate != null) {
-                throw new DuplicateFileException(archivePath, duplicate, mInputFile);
+                // we have a duplicate but it might be the same source file, in this case,
+                // we just ignore the duplicate, and of course, we don't add it again.
+                File potentialDuplicate = new File(mInputFile, archivePath);
+                if (!duplicate.getAbsolutePath().equals(potentialDuplicate.getAbsolutePath())) {
+                    throw new DuplicateFileException(archivePath, duplicate, mInputFile);
+                }
+                return false;
             } else {
                 mAddedFiles.put(archivePath, mInputFile);
             }
@@ -95,118 +100,20 @@
     }
 
     /**
-     * Custom {@link IZipEntryFilter} to filter out everything that is not a standard java
-     * resources, and also record whether the zip file contains native libraries.
-     * <p/>Used in {@link SignedJarBuilder#writeZip(java.io.InputStream, IZipEntryFilter)} when
-     * we only want the java resources from external jars.
+     * A filter to filter out binary files like .class
      */
-    private final class JavaAndNativeResourceFilter implements IZipEntryFilter {
-        private final List<String> mNativeLibs = new ArrayList<String>();
-        private Set<String> mUsedPickFirsts = null;
-
-        @Nullable
-        private final PackagingOptions mPackagingOptions;
-
+    private static final class NoBinaryZipFilter implements IZipEntryFilter {
         @NonNull
-        private final Set<String> mExcludes;
-        @NonNull
-        private final Set<String> mPickFirsts;
+        private final IZipEntryFilter parentFilter;
 
-        private boolean mNativeLibsConflict = false;
-        private File mInputFile;
-
-        private JavaAndNativeResourceFilter(@Nullable PackagingOptions packagingOptions) {
-            mPackagingOptions = packagingOptions;
-
-            mExcludes = mPackagingOptions != null ? mPackagingOptions.getExcludes() :
-                    Collections.<String>emptySet();
-            mPickFirsts = mPackagingOptions != null ? mPackagingOptions.getPickFirsts() :
-                    Collections.<String>emptySet();
-
+        private NoBinaryZipFilter(@NonNull IZipEntryFilter parentFilter) {
+            this.parentFilter = parentFilter;
         }
 
+
         @Override
         public boolean checkEntry(String archivePath) throws ZipAbortException {
-            // split the path into segments.
-            String[] segments = archivePath.split("/");
-
-            // empty path? skip to next entry.
-            if (segments.length == 0) {
-                return false;
-            }
-
-            //noinspection VariableNotUsedInsideIf
-            if (mPackagingOptions != null) {
-                if (mExcludes.contains(archivePath)) {
-                    return false;
-                }
-
-                if (mPickFirsts.contains(archivePath)) {
-                    if (mUsedPickFirsts == null) {
-                        mUsedPickFirsts = Sets.newHashSetWithExpectedSize(mPickFirsts.size());
-                    }
-
-                    if (mUsedPickFirsts.contains(archivePath)) {
-                        return false;
-                    } else {
-                        mUsedPickFirsts.add(archivePath);
-                    }
-                }
-            }
-
-            // Check each folders to make sure they should be included.
-            // Folders like CVS, .svn, etc.. should already have been excluded from the
-            // jar file, but we need to exclude some other folder (like /META-INF) so
-            // we check anyway.
-            for (int i = 0 ; i < segments.length - 1; i++) {
-                if (!PackagingUtils.checkFolderForPackaging(segments[i])) {
-                    return false;
-                }
-            }
-
-            // get the file name from the path
-            String fileName = segments[segments.length-1];
-
-            boolean check = PackagingUtils.checkFileForPackaging(fileName);
-
-            // only do additional checks if the file passes the default checks.
-            if (check) {
-                mLogger.verbose("=> %s", archivePath);
-
-                File duplicate = checkFileForDuplicate(archivePath);
-                if (duplicate != null) {
-                    throw new DuplicateFileException(archivePath, duplicate, mInputFile);
-                } else {
-                    mAddedFiles.put(archivePath, mInputFile);
-                }
-
-                if (archivePath.endsWith(".so")) {
-                    mNativeLibs.add(archivePath);
-
-                    // only .so located in lib/ will interfere with the installation
-                    if (archivePath.startsWith(SdkConstants.FD_APK_NATIVE_LIBS + "/")) {
-                        mNativeLibsConflict = true;
-                    }
-                } else if (archivePath.endsWith(".jnilib")) {
-                    mNativeLibs.add(archivePath);
-                }
-            }
-
-            return check;
-        }
-
-        List<String> getNativeLibs() {
-            return mNativeLibs;
-        }
-
-        boolean getNativeLibsConflict() {
-            return mNativeLibsConflict;
-        }
-
-        void reset(File inputFile) {
-            mInputFile = inputFile;
-            mNativeLibs.clear();
-            mNativeLibsConflict = false;
+            return parentFilter.checkEntry(archivePath) && !archivePath.endsWith(".class");
         }
     }
 
@@ -215,48 +122,11 @@
     private boolean mJniDebugMode = false;
     private boolean mIsSealed = false;
 
-    private final NullZipFilter mNullFilter = new NullZipFilter();
-    private final JavaAndNativeResourceFilter mFilter;
+    private final DuplicateZipFilter mNoDuplicateFilter = new DuplicateZipFilter();
+    private final NoBinaryZipFilter mNoBinaryZipFilter = new NoBinaryZipFilter(mNoDuplicateFilter);
+    @Nullable private final SignedJarBuilder.IZipEntryFilter mPackagingOptionsFilter;
     private final HashMap<String, File> mAddedFiles = new HashMap<String, File>();
-
-    /**
-     * Status for the addition of a jar file resources into the APK.
-     * This indicates possible issues with native library inside the jar file.
-     */
-    public interface JarStatus {
-        /**
-         * Returns the list of native libraries found in the jar file.
-         */
-        List<String> getNativeLibs();
-
-        /**
-         * Returns whether some of those libraries were located in the location that Android
-         * expects its native libraries.
-         */
-        boolean hasNativeLibsConflicts();
-
-    }
-
-    /** Internal implementation of {@link JarStatus}. */
-    private static final class JarStatusImpl implements JarStatus {
-        public final List<String> mLibs;
-        public final boolean mNativeLibsConflict;
-
-        private JarStatusImpl(List<String> libs, boolean nativeLibsConflict) {
-            mLibs = libs;
-            mNativeLibsConflict = nativeLibsConflict;
-        }
-
-        @Override
-        public List<String> getNativeLibs() {
-            return mLibs;
-        }
-
-        @Override
-        public boolean hasNativeLibsConflicts() {
-            return mNativeLibsConflict;
-        }
-    }
+    private final HashMap<String, File> mMergeFiles = new HashMap<String, File>();
 
     /**
      * Creates a new instance.
@@ -272,6 +142,7 @@
      *
      * @param apkLocation the file to create
      * @param resLocation the file representing the packaged resource file.
+     * @param mergingFolder the folder to store files that are being merged.
      * @param certificateInfo the signing information used to sign the package. Optional the OS path to the debug keystore, if needed or null.
      * @param logger the logger.
      * @throws com.android.builder.packaging.PackagerException
@@ -279,11 +150,12 @@
     public Packager(
             @NonNull String apkLocation,
             @NonNull String resLocation,
+            @NonNull File mergingFolder,
             CertificateInfo certificateInfo,
             @Nullable String createdBy,
             @Nullable PackagingOptions packagingOptions,
+            @Nullable SignedJarBuilder.IZipEntryFilter packagingOptionsFilter,
             ILogger logger) throws PackagerException {
-        mFilter = new JavaAndNativeResourceFilter(packagingOptions);
 
         try {
             File apkFile = new File(apkLocation);
@@ -292,6 +164,14 @@
             File resFile = new File(resLocation);
             checkInputFile(resFile);
 
+            checkMergingFolder(mergingFolder);
+            if (packagingOptions != null) {
+                for (String merge : packagingOptions.getMerges()) {
+                    mMergeFiles.put(merge, new File(mergingFolder, merge));
+                }
+            }
+
+            mPackagingOptionsFilter = packagingOptionsFilter;
             mLogger = logger;
 
             mBuilder = new SignedJarBuilder(
@@ -409,11 +289,11 @@
             mLogger.verbose("%s:", zipFile);
 
             // reset the filter with this input.
-            mNullFilter.reset(zipFile);
+            mNoDuplicateFilter.reset(zipFile);
 
             // ask the builder to add the content of the file.
             fis = new FileInputStream(zipFile);
-            mBuilder.writeZip(fis, mNullFilter);
+            mBuilder.writeZip(fis, mNoDuplicateFilter, null /* ZipEntryExtractor */);
         } catch (DuplicateFileException e) {
             mBuilder.cleanUp();
             throw e;
@@ -430,51 +310,59 @@
     }
 
     /**
-     * Adds the resources from a jar file.
-     * @param jarFile the jar File.
-     * @return a {@link JarStatus} object indicating if native libraries where found in
-     *         the jar file.
-     * @throws PackagerException if an error occurred
-     * @throws SealedPackageException if the APK is already sealed.
-     * @throws DuplicateFileException if a file conflicts with another already added to the APK
-     *                                   at the same location inside the APK archive.
+     * Adds all resources from a merged folder or jar file. There cannot be any duplicates and all
+     * files present must be added unless it is a "binary" file like a .class or .dex (jack
+     * produces the classes.dex in the same location as the obfuscated resources).
+     * @param jarFileOrDirectory a jar file or directory reference.
+     * @throws PackagerException could not add an entry to the package.
+     * @throws DuplicateFileException if an entry with the same name was already present in the
+     * package being built while adding the jarFileOrDirectory content.
      */
-    public JarStatus addResourcesFromJar(File jarFile) throws PackagerException,
-            SealedPackageException, DuplicateFileException {
-        if (mIsSealed) {
-            throw new SealedPackageException("APK is already sealed");
-        }
+    public void addResources(@NonNull File jarFileOrDirectory)
+            throws PackagerException, DuplicateFileException {
 
-        FileInputStream fis = null;
+        mNoDuplicateFilter.reset(jarFileOrDirectory);
+        InputStream fis = null;
         try {
-            mLogger.verbose("%s:", jarFile);
-
-            // reset the filter with this input.
-            mFilter.reset(jarFile);
-
-            // ask the builder to add the content of the file, filtered to only let through
-            // the java resources.
-            fis = new FileInputStream(jarFile);
-            mBuilder.writeZip(fis, mFilter);
-
-            // check if native libraries were found in the external library. This should
-            // constitutes an error or warning depending on if they are in lib/
-            return new JarStatusImpl(mFilter.getNativeLibs(), mFilter.getNativeLibsConflict());
+            if (jarFileOrDirectory.isDirectory()) {
+                addResourcesFromDirectory(jarFileOrDirectory, "");
+            } else {
+                fis = new BufferedInputStream(new FileInputStream(jarFileOrDirectory));
+                mBuilder.writeZip(fis, mNoBinaryZipFilter, null /* ZipEntryExtractor */);
+            }
         } catch (DuplicateFileException e) {
             mBuilder.cleanUp();
             throw e;
         } catch (Exception e) {
             mBuilder.cleanUp();
-            throw new PackagerException(e, "Failed to add %s", jarFile);
+            throw new PackagerException(e, "Failed to add %s", jarFileOrDirectory);
         } finally {
             try {
-                Closeables.close(fis, true /* swallowIOException */);
+                if (fis != null) {
+                    Closeables.close(fis, true /* swallowIOException */);
+                }
             } catch (IOException e) {
                 // ignore.
             }
         }
     }
 
+    private void addResourcesFromDirectory(@NonNull File directory, String path)
+            throws IOException, IZipEntryFilter.ZipAbortException {
+        File[] directoryFiles = directory.listFiles();
+        if (directoryFiles == null) {
+            return;
+        }
+        for (File file : directoryFiles) {
+            String entryName = path.isEmpty() ? file.getName() : path + "/" + file.getName();
+            if (file.isDirectory()) {
+                addResourcesFromDirectory(file, entryName);
+            } else {
+                doAddFile(file, entryName);
+            }
+        }
+    }
+
     /**
      * Adds the native libraries from the top native folder.
      * The content of this folder must be the various ABI folders.
@@ -535,8 +423,12 @@
                                     abi.getName() + "/" + libName;
 
                                 try {
-                                    doAddFile(lib, path);
-                                } catch (IOException e) {
+
+                                    if (mPackagingOptionsFilter == null
+                                            || mPackagingOptionsFilter.checkEntry(path)) {
+                                        doAddFile(lib, path);
+                                    }
+                                } catch (Exception e) {
                                     mBuilder.cleanUp();
                                     throw new PackagerException(e, "Failed to add %s", lib);
                                 }
@@ -559,6 +451,20 @@
             throw new SealedPackageException("APK is already sealed");
         }
 
+        // Add all the merged files that are pending to be packaged.
+        for (Map.Entry<String, File>entry : mMergeFiles.entrySet()) {
+            File inputFile = entry.getValue();
+            String archivePath = entry.getKey();
+            try {
+                if (inputFile.exists()) {
+                    mBuilder.writeFile(inputFile, archivePath);
+                }
+            } catch (IOException e) {
+                mBuilder.cleanUp();
+                throw new PackagerException(e, "Failed to add merged file %s", inputFile);
+            }
+        }
+
         // close and sign the application package.
         try {
             mBuilder.close();
@@ -570,17 +476,26 @@
         }
     }
 
-    private void doAddFile(File file, String archivePath) throws DuplicateFileException,
+    private void doAddFile(File file, String archivePath) throws IZipEntryFilter.ZipAbortException,
             IOException {
-        mLogger.verbose("%1$s => %2$s", file, archivePath);
 
-        File duplicate = checkFileForDuplicate(archivePath);
-        if (duplicate != null) {
-            throw new DuplicateFileException(archivePath, duplicate, file);
+        // If a file has to be merged, write it to a file in the merging folder and add it later.
+        if (mMergeFiles.keySet().contains(archivePath)) {
+            File mergingFile = mMergeFiles.get(archivePath);
+            Files.createParentDirs(mergingFile);
+            FileOutputStream fos = new FileOutputStream(mMergeFiles.get(archivePath), true);
+            try {
+                fos.write(Files.toByteArray(file));
+            } finally {
+                fos.close();
+            }
+        } else {
+            if (!mNoBinaryZipFilter.checkEntry(archivePath)) {
+                return;
+            }
+            mAddedFiles.put(archivePath, file);
+            mBuilder.writeFile(file, archivePath);
         }
-
-        mAddedFiles.put(archivePath, file);
-        mBuilder.writeFile(file, archivePath);
     }
 
     /**
@@ -603,7 +518,7 @@
      * @param file the File to check
      * @throws PackagerException If the check fails
      */
-    private void checkOutputFile(File file) throws PackagerException {
+    private static void checkOutputFile(File file) throws PackagerException {
         if (file.isDirectory()) {
             throw new PackagerException("%s is a directory!", file);
         }
@@ -625,6 +540,32 @@
     }
 
     /**
+     * Checks the merger folder is:
+     * - a directory.
+     * - if the folder exists, that it can be modified.
+     * - if it doesn't exists, that a new folder can be created.
+     * @param file the File to check
+     * @throws PackagerException If the check fails
+     */
+    private static void checkMergingFolder(File file) throws PackagerException {
+        if (file.isFile()) {
+            throw new PackagerException("%s is a file!", file);
+        }
+
+        if (file.exists()) { // will be a directory in this case.
+            if (!file.canWrite()) {
+                throw new PackagerException("Cannot write %s", file);
+            }
+        }
+
+        try {
+            FileUtils.emptyFolder(file);
+        } catch (IOException e) {
+            throw new PackagerException(e);
+        }
+    }
+
+    /**
      * Checks an input {@link File} object.
      * This checks the following:
      * - the file is not an existing directory.
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java b/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java
index 62eb862..0716d46 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java
@@ -87,8 +87,7 @@
     @Override
     public void testRunStarted(String runName, int testCount) {
         if (mLogger != null) {
-            mLogger.info(
-                    String.format("Starting %1$d tests on %2$s", testCount, mDeviceName));
+            mLogger.info("Starting %1$d tests on %2$s", testCount, mDeviceName);
         }
         super.testRunStarted(runName, testCount);
     }
@@ -96,9 +95,8 @@
     @Override
     public void testFailed(TestIdentifier test, String trace) {
         if (mLogger != null) {
-            mLogger.warning(
-                    String.format("\n%1$s > %2$s[%3$s] \033[31mFAILED \033[0m",
-                            test.getClassName(), test.getTestName(), mDeviceName));
+            mLogger.warning("\n%1$s > %2$s[%3$s] \033[31mFAILED \033[0m",
+                    test.getClassName(), test.getTestName(), mDeviceName);
             mLogger.warning(getModifiedTrace(trace));
         }
 
@@ -109,7 +107,11 @@
     
     @Override
     public void testAssumptionFailure(TestIdentifier test, String trace) {
-        testFailed(test, trace);
+        if (mLogger != null) {
+            mLogger.warning("\n%1$s > %2$s[%3$s] \033[33mSKIPPED \033[0m\n%4$s",
+                    test.getClassName(), test.getTestName(), mDeviceName, getModifiedTrace(trace));
+        }
+        super.testAssumptionFailure(test, trace);
     }
 
     @Override
@@ -117,13 +119,11 @@
         if (!mFailedTests.remove(test)) {
             // if wasn't present in the list, then the test succeeded.
             if (mLogger != null) {
-                mLogger.info(
-                        String.format("\n%1$s > %2$s[%3$s] \033[32mSUCCESS \033[0m",
-                                test.getClassName(), test.getTestName(), mDeviceName));
+                mLogger.info("\n%1$s > %2$s[%3$s] \033[32mSUCCESS \033[0m",
+                        test.getClassName(), test.getTestName(), mDeviceName);
             }
 
         }
-
         super.testEnded(test, testMetrics);
     }
 
@@ -135,6 +135,15 @@
         super.testRunFailed(errorMessage);
     }
 
+    @Override
+    public void testIgnored(TestIdentifier test) {
+        if (mLogger != null) {
+            mLogger.warning("\n%1$s > %2$s[%3$s] \033[33mSKIPPED \033[0m",
+                    test.getClassName(), test.getTestName(), mDeviceName);
+        }
+        super.testIgnored(test);
+    }
+
     private String getModifiedTrace(String trace) {
         // split lines
         String[] lines = trace.split("\n");
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java b/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java
index f6b4985..46cbc13 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java
@@ -33,7 +33,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.PrintWriter;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -67,27 +66,21 @@
     @NonNull
     private final List<File> testedApks;
     @NonNull
-    private final File adbExec;
-    @NonNull
-    private final Collection<String> installOptions;
-
-    private final int timeoutInMs;
-    @NonNull
     private final ILogger logger;
 
+    private final int timeoutInMs;
+
     public SimpleTestCallable(
-            @NonNull  DeviceConnector device,
-            @NonNull  String projectName,
-            @NonNull  String flavorName,
-            @NonNull  File testApk,
+            @NonNull DeviceConnector device,
+            @NonNull String projectName,
+            @NonNull String flavorName,
+            @NonNull File testApk,
             @NonNull List<File> testedApks,
-            @NonNull  File adbExec,
-            @NonNull  TestData testData,
-            @NonNull  File resultsDir,
-            @NonNull  File coverageDir,
-                      int timeoutInMs,
-            @NonNull Collection<String> installOptions,
-            @NonNull  ILogger logger) {
+            @NonNull TestData testData,
+            @NonNull File resultsDir,
+            @NonNull File coverageDir,
+            int timeoutInMs,
+            @NonNull ILogger logger) {
         this.projectName = projectName;
         this.device = device;
         this.flavorName = flavorName;
@@ -96,9 +89,7 @@
         this.testApk = testApk;
         this.testedApks = testedApks;
         this.testData = testData;
-        this.adbExec = adbExec;
         this.timeoutInMs = timeoutInMs;
-        this.installOptions = installOptions;
         this.logger = logger;
     }
 
@@ -150,6 +141,11 @@
                     testData.getInstrumentationRunner(),
                     device);
 
+            for (Map.Entry<String, String> argument:
+                    testData.getInstrumentationRunnerArguments().entrySet()) {
+                runner.addInstrumentationArg(argument.getKey(), argument.getValue());
+            }
+
             if (testData.isTestCoverageEnabled()) {
                 runner.addInstrumentationArg("coverage", "true");
                 runner.addInstrumentationArg("coverageFile", coverageFile);
diff --git a/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java b/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java
index db45a6b..ee852ce 100644
--- a/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java
+++ b/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java
@@ -29,7 +29,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
diff --git a/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java b/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java
index d17ac44..83db810 100644
--- a/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java
+++ b/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java
@@ -17,60 +17,175 @@
 package com.android.builder.png;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
 
 import com.android.annotations.NonNull;
+import com.android.ide.common.res2.ResourcePreprocessor;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.ide.common.vectordrawable.VdPreview;
 import com.android.resources.Density;
+import com.android.resources.ResourceFolderType;
+import com.android.utils.ILogger;
 import com.google.common.base.Charsets;
 import com.google.common.collect.Lists;
 import com.google.common.io.Files;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
 
-/**
- * Generates PNG images from VectorDrawable files.
- */
-public class VectorDrawableRenderer {
+import javax.imageio.ImageIO;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
 
+/**
+ * Generates PNG images (and XML copies) from VectorDrawable files.
+ */
+@SuppressWarnings("MethodMayBeStatic")
+public class VectorDrawableRenderer implements ResourcePreprocessor {
     /** Projects with minSdk set to this or higher don't need to generate PNGs. */
     public static final int MIN_SDK_WITH_VECTOR_SUPPORT = 21;
 
+    private final ILogger mLogger;
+    private final File mOutputDir;
+    private final Collection<Density> mDensities;
+
+    public VectorDrawableRenderer(File outputDir, Collection<Density> densities, ILogger logger) {
+        mOutputDir = outputDir;
+        mDensities = densities;
+        mLogger = logger;
+    }
+
     public Collection<File> createPngFiles(
             @NonNull File inputXmlFile,
             @NonNull File outputResDirectory,
             @NonNull Collection<Density> densities) throws IOException {
-        checkArgument(inputXmlFile.exists());
-        checkArgument(outputResDirectory.exists());
-
-        Collection<File> createdFiles = Lists.newArrayList();
-
-        for (Density density : densities) {
-            // Sketch implementation.
-
-            // TODO: add the density to all other qualifiers of the original file.
-            File directory = new File(outputResDirectory, "drawable-" + density.getResourceValue());
-            File pngFile = new File(directory, inputXmlFile.getName().replace(".xml", ".png"));
-
-            Files.createParentDirs(pngFile);
-            Files.write("PNG for " + density.getResourceValue(), pngFile, Charsets.UTF_8);
-            createdFiles.add(pngFile);
-        }
-
-        File v21Dir = new File(outputResDirectory, "drawable-v21");
-        File v21Copy = new File(v21Dir, inputXmlFile.getName());
-        Files.createParentDirs(v21Copy);
-        Files.copy(inputXmlFile, v21Copy);
-        createdFiles.add(v21Copy);
-
-        // TODO: make all the drawable-hdpi-v21 aliases.
-
-        return createdFiles;
+        throw new RuntimeException("Replaced by the new way to generate PNGs");
     }
 
-    public boolean isVectorDrawable(File resourceFile) {
-        // TODO: parse the root element of the file to check.
-        return resourceFile.getPath().endsWith(".xml")
-                && resourceFile.getParentFile().getName().equals("drawable");
+    @Override
+    public boolean needsPreprocessing(File resourceFile) {
+        return isXml(resourceFile)
+                && isInDrawable(resourceFile)
+                && getEffectiveVersion(resourceFile) < MIN_SDK_WITH_VECTOR_SUPPORT
+                && isRootVector(resourceFile);
+    }
+
+    @Override
+    public Collection<File> getFilesToBeGenerated(File inputXmlFile) {
+        FolderConfiguration originalConfiguration = getFolderConfiguration(inputXmlFile);
+
+        // Create all the PNG files and duplicate the XML into folders with the version qualifier.
+        Collection<File> filesToBeGenerated = Lists.newArrayList();
+        for (Density density : mDensities) {
+            FolderConfiguration newConfiguration = FolderConfiguration.copyOf(originalConfiguration);
+            newConfiguration.setDensityQualifier(new DensityQualifier(density));
+
+            File directory = new File(
+                    mOutputDir,
+                    newConfiguration.getFolderName(ResourceFolderType.DRAWABLE));
+            File pngFile = new File(
+                    directory,
+                    inputXmlFile.getName().replace(".xml", ".png"));
+
+            filesToBeGenerated.add(pngFile);
+
+            newConfiguration.setVersionQualifier(new VersionQualifier(MIN_SDK_WITH_VECTOR_SUPPORT));
+            File destination = new File(
+                    mOutputDir,
+                    newConfiguration.getFolderName(ResourceFolderType.DRAWABLE));
+            File xmlCopy = new File(destination, inputXmlFile.getName());
+            filesToBeGenerated.add(xmlCopy);
+        }
+
+        return filesToBeGenerated;
+    }
+
+    @Override
+    public void generateFile(File toBeGenerated, File original) throws IOException {
+        Files.createParentDirs(toBeGenerated);
+
+        if (isXml(toBeGenerated)) {
+            Files.copy(original, toBeGenerated);
+        } else {
+            mLogger.info(
+                    "Generating PNG: [%s] from [%s]",
+                    toBeGenerated.getAbsolutePath(),
+                    original.getAbsolutePath());
+
+            FolderConfiguration folderConfiguration = getFolderConfiguration(toBeGenerated);
+            checkState(folderConfiguration.getDensityQualifier() != null);
+            Density density = folderConfiguration.getDensityQualifier().getValue();
+
+            String xmlContent = Files.toString(original, Charsets.UTF_8);
+            float scaleFactor = density.getDpiValue() / (float) Density.MEDIUM.getDpiValue();
+            if (scaleFactor <= 0) {
+                scaleFactor = 1.0f;
+            }
+
+            final VdPreview.TargetSize imageSize = VdPreview.TargetSize.createSizeFromScale(scaleFactor);
+            BufferedImage image = VdPreview.getPreviewFromVectorXml(imageSize, xmlContent, null);
+            checkState(image != null, "Generating the image failed.");
+            ImageIO.write(image, "png", toBeGenerated);
+        }
+    }
+
+    @NonNull
+    private FolderConfiguration getFolderConfiguration(@NonNull File inputXmlFile) {
+        String parentName = inputXmlFile.getParentFile().getName();
+        FolderConfiguration originalConfiguration =
+                FolderConfiguration.getConfigForFolder(parentName);
+        checkArgument(
+                originalConfiguration != null,
+                "Invalid resource folder name [%s].",
+                parentName);
+        return originalConfiguration;
+    }
+
+    private boolean isInDrawable(@NonNull File inputXmlFile) {
+        ResourceFolderType folderType =
+                ResourceFolderType.getFolderType(inputXmlFile.getParentFile().getName());
+
+        return folderType == ResourceFolderType.DRAWABLE;
+    }
+
+    /**
+     * Parse the root element of the file, return true if it is a vector.
+     * TODO: Combine this with the drawing code, such that we don't parse the file twice.
+     */
+    private boolean isRootVector(File resourceFile) {
+        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+        boolean result = false;
+        try {
+            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+            Document doc = dBuilder.parse(resourceFile);
+            Element root = doc.getDocumentElement();
+            if (root != null && root.getNodeName().equalsIgnoreCase("vector")) {
+                result = true;
+            }
+        } catch (Exception e) {
+            mLogger.error(e, "Exception in parsing the XML resource" + e.getMessage());
+        }
+
+        return result;
+    }
+
+    private boolean isXml(File resourceFile) {
+        return resourceFile.getPath().endsWith(".xml");
+    }
+
+    private int getEffectiveVersion(File resourceFile) {
+        FolderConfiguration configuration = getFolderConfiguration(resourceFile);
+        if (configuration.getVersionQualifier() == null) {
+            configuration.createDefault();
+        }
+        //noinspection ConstantConditions - handled above.
+        return configuration.getVersionQualifier().getVersion();
     }
 }
diff --git a/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java b/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java
index 3517c85..5a2fe7f 100644
--- a/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java
+++ b/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java
@@ -85,7 +85,7 @@
 
         IAndroidTarget target = mSdkManager.getTargetFromHashString(targetHash);
         if (target == null) {
-            throw new IllegalStateException("failed to find target " + targetHash + " : " + mSdkLocation);
+            throw new IllegalStateException("failed to find target with hash string '" + targetHash + "' in: " + mSdkLocation);
         }
 
         BuildToolInfo buildToolInfo = mSdkManager.getBuildTool(buildToolRevision);
diff --git a/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java b/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java
index d8a3dde..b41c9ff 100644
--- a/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java
@@ -148,6 +148,18 @@
         boolean checkEntry(String archivePath) throws ZipAbortException;
     }
 
+
+    /**
+     * Classes which implement this interface provides a method to check whether a file should
+     * be merged and extracted from the zip.
+     */
+    public interface ZipEntryExtractor {
+
+        boolean checkEntry(String archivePath);
+
+        void extract(String archivePath, InputStream zis) throws IOException;
+    }
+
     /**
      * Creates a {@link SignedJarBuilder} with a given output stream, and signing information.
      * <p/>If either <code>key</code> or <code>certificate</code> is <code>null</code> then
@@ -209,6 +221,17 @@
 
     /**
      * Copies the content of a Jar/Zip archive into the receiver archive.
+     * @param input the {@link InputStream} for the Jar/Zip to copy.
+     * @throws IOException
+     * @throws ZipAbortException if the {@link IZipEntryFilter} filter indicated that the write
+     *                           must be aborted.
+     */
+    public void writeZip(InputStream input) throws IOException, ZipAbortException {
+        writeZip(input, null, null);
+    }
+
+    /**
+     * Copies the content of a Jar/Zip archive into the receiver archive.
      * <p/>An optional {@link IZipEntryFilter} allows to selectively choose which files
      * to copy over.
      * @param input the {@link InputStream} for the Jar/Zip to copy.
@@ -217,7 +240,7 @@
      * @throws ZipAbortException if the {@link IZipEntryFilter} filter indicated that the write
      *                           must be aborted.
      */
-    public void writeZip(InputStream input, IZipEntryFilter filter)
+    public void writeZip(InputStream input, IZipEntryFilter filter, ZipEntryExtractor extractor)
             throws IOException, ZipAbortException {
         ZipInputStream zis = new ZipInputStream(input);
 
@@ -256,6 +279,12 @@
                     }
                 }
 
+                // if we have a filter, we check the entry to see if it's a file that should be extracted.
+                if (extractor != null && extractor.checkEntry(name)) {
+                    extractor.extract(name, zis);
+                    continue;
+                }
+
                 // if we have a filter, we check the entry against it
                 if (filter != null && !filter.checkEntry(name)) {
                     continue;
diff --git a/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java b/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java
index ab99df7..b1371d3 100644
--- a/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java
+++ b/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java
@@ -23,10 +23,16 @@
 import com.android.ddmlib.AndroidDebugBridge;
 import com.android.ddmlib.IDevice;
 import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * DeviceProvider for locally connected devices. Basically returns the list of devices that
@@ -87,18 +93,29 @@
             throw new DeviceException("No connected devices!");
         }
 
-        final String androidSerial = System.getenv("ANDROID_SERIAL");
-        final Boolean isValidSerial = androidSerial != null && !androidSerial.isEmpty();
+        final String androidSerialsEnv = System.getenv("ANDROID_SERIAL");
+        final boolean isValidSerial = androidSerialsEnv != null && !androidSerialsEnv.isEmpty();
+
+        final Set<String> serials;
+        if (isValidSerial) {
+            serials = Sets.newHashSet(Splitter.on(',').split(androidSerialsEnv));
+        } else {
+            serials = Collections.emptySet();
+        }
+
         final List<IDevice> filteredDevices = Lists.newArrayListWithCapacity(devices.length);
         for (IDevice iDevice : devices) {
-            if (!isValidSerial || iDevice.getSerialNumber().equals(androidSerial)) {
+            if (!isValidSerial || serials.contains(iDevice.getSerialNumber())) {
+                serials.remove(iDevice.getSerialNumber());
                 filteredDevices.add(iDevice);
             }
         }
 
-        if (filteredDevices.isEmpty()) {
+        if (!serials.isEmpty()) {
             throw new DeviceException(String.format(
-                    "Connected device with serial %s not found!", androidSerial));
+                    "Connected device with serial%s '%s' not found!",
+                    serials.size() == 1 ? "" : "s",
+                    Joiner.on("', '").join(serials)));
         }
 
         for (IDevice device : filteredDevices) {
@@ -117,7 +134,7 @@
             if (isValidSerial) {
                 throw new DeviceException(String.format(
                         "Connected device with serial $1%s is not online.",
-                        androidSerial));
+                        androidSerialsEnv));
             } else {
                 throw new DeviceException("No online devices found.");
             }
diff --git a/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java b/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
index 4486a30..8e048b7 100755
--- a/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
+++ b/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
@@ -21,7 +21,6 @@
 import com.android.builder.internal.InstallUtils;
 import com.android.builder.internal.testing.CustomTestRunListener;
 import com.android.builder.internal.testing.SimpleTestCallable;
-import com.android.builder.testing.api.DeviceConfig;
 import com.android.builder.testing.api.DeviceConfigProviderImpl;
 import com.android.builder.testing.api.DeviceConnector;
 import com.android.builder.testing.api.DeviceException;
@@ -46,17 +45,14 @@
  */
 public class SimpleTestRunner implements TestRunner {
 
-    @NonNull
-    private final File mAdbExec;
     @Nullable
     private final File mSplitSelectExec;
     @NonNull
     private final ProcessExecutor mProcessExecutor;
 
-    public SimpleTestRunner(@NonNull File adbExec,
+    public SimpleTestRunner(
             @Nullable File splitSelectExec,
             @NonNull ProcessExecutor processExecutor) {
-        mAdbExec = adbExec;
         mSplitSelectExec = splitSelectExec;
         mProcessExecutor = processExecutor;
     }
@@ -114,9 +110,10 @@
                     }
 
                     compatibleDevices++;
-                    executor.execute(new SimpleTestCallable(device, projectName, variantName,
-                            testApk, testedApks, mAdbExec, testData,
-                            resultsDir, coverageDir, timeoutInMs, installOptions, logger));
+                    executor.execute(
+                            new SimpleTestCallable(
+                                    device, projectName, variantName, testApk, testedApks, testData,
+                                    resultsDir, coverageDir, timeoutInMs, logger));
                 }
             } else {
                 unauthorizedDevices++;
diff --git a/build-system/builder/src/main/java/com/android/builder/testing/TestData.java b/build-system/builder/src/main/java/com/android/builder/testing/TestData.java
index 065cfc4..64fdaef 100644
--- a/build-system/builder/src/main/java/com/android/builder/testing/TestData.java
+++ b/build-system/builder/src/main/java/com/android/builder/testing/TestData.java
@@ -27,6 +27,7 @@
 
 import java.io.File;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Data representing the test app and the tested application/library.
@@ -52,6 +53,9 @@
     @NonNull
     String getInstrumentationRunner();
 
+    @NonNull
+    Map<String, String> getInstrumentationRunnerArguments();
+
     /**
      * Returns whether the tested app is enabled for code coverage
      */
diff --git a/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java b/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java
index 1f68790..e4b8435 100644
--- a/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java
+++ b/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java
@@ -291,7 +291,7 @@
 
         List<String> command = processInfo.getArgs();
 
-        assertTrue("res1,res2,xhdpi,nodpi,anydpi".equals(command.get(command.indexOf("-c") + 1)));
+        assertTrue("res1,res2,xhdpi,nodpi".equals(command.get(command.indexOf("-c") + 1)));
         assertTrue(command.indexOf("--preferred-density") == -1);
     }
 
diff --git a/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java b/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java
index 53974de..98794cc 100644
--- a/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java
+++ b/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java
@@ -36,17 +36,21 @@
 import com.android.sdklib.BuildToolInfo;
 import com.android.sdklib.repository.FullRevision;
 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 junit.framework.TestCase;
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
 
 public class PreDexCacheTest extends TestCase {
 
@@ -92,19 +96,19 @@
                 }
 
                 // read the source content
-                List<String> lines = Files.readLines(input, Charsets.UTF_8);
+                JarFile jarFile = new JarFile(input);
+                JarEntry jarEntry = jarFile.getJarEntry("content.class");
+                assert jarEntry != null;
+                InputStream contentStream = jarFile.getInputStream(jarEntry);
+                byte[] content  = new byte[256];
+                int read = contentStream.read(content);
+                contentStream.close();
+                jarFile.close();
 
-                // modify the lines
-                List<String> dexedLines = Lists.newArrayListWithCapacity(lines.size());
-                for (String line : lines) {
-                    dexedLines.add(DEX_DATA + line + DEX_DATA);
-                }
-
-                // combine the lines
-                String content = Joiner.on('\n').join(dexedLines);
+                String line = new String(content, 0, read, Charsets.UTF_8);
 
                 // write it
-                Files.write(content, new File(output), Charsets.UTF_8);
+                Files.write(DEX_DATA + line + DEX_DATA, new File(output), Charsets.UTF_8);
             } catch (Exception e) {
                 //noinspection ThrowableInstanceNeverThrown
                 processException = new ProcessException(null, e);
@@ -412,7 +416,15 @@
         File input = File.createTempFile("predex", ".jar");
         input.deleteOnExit();
 
-        Files.write(content, input, Charsets.UTF_8);
+        JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(input));
+        try {
+            jarOutputStream.putNextEntry(new ZipEntry("content.class"));
+            jarOutputStream.write(content.getBytes(Charsets.UTF_8));
+            jarOutputStream.closeEntry();
+        } finally {
+            jarOutputStream.close();
+        }
+
         return input;
     }
 
diff --git a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java
index c95ebc7..ddbe625 100644
--- a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java
+++ b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java
@@ -16,7 +16,7 @@
 
 package com.android.builder.png;
 
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
 
 import com.android.SdkConstants;
@@ -33,7 +33,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.io.Files;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 
 import java.awt.image.BufferedImage;
 import java.io.File;
@@ -62,12 +62,18 @@
     public static final byte[] SIGNATURE = new byte[]{
             (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
 
+    /**
+     * Returns the lastest build tools that's at least the passed version.
+     * @param fullRevision the minimum required build tools version.
+     * @return the latest build tools.
+     * @throws RuntimeException if the latest build tools is older than fullRevision.
+     */
     static File getAapt(FullRevision fullRevision) {
         ILogger logger = new StdLogger(StdLogger.Level.VERBOSE);
         SdkManager sdkManager = SdkManager.createManager(getSdkDir().getAbsolutePath(), logger);
         assert sdkManager != null;
-        BuildToolInfo buildToolInfo = sdkManager.getBuildTool(fullRevision);
-        if (buildToolInfo == null) {
+        BuildToolInfo buildToolInfo = sdkManager.getLatestBuildTool();
+        if (buildToolInfo == null || buildToolInfo.getRevision().compareTo(fullRevision) < 0) {
             throw new RuntimeException("Test requires build-tools " + fullRevision.toShortString());
         }
         return new File(buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));
diff --git a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java
index 607a516..ab5a3f1 100644
--- a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java
+++ b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java
@@ -26,7 +26,6 @@
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -43,7 +42,6 @@
 /**
  * Asynchronous version of the aapt cruncher test.
  */
-@Ignore  // TODO: Re-enable when build tools 22 are available.
 @RunWith(Parameterized.class)
 public class NinePatchAsyncAaptProcessTest {
 
@@ -83,7 +81,7 @@
     @NonNull
     private static PngCruncher getCruncher() {
         ILogger logger = new StdLogger(StdLogger.Level.VERBOSE);
-        File aapt = NinePatchAaptProcessorTestUtils.getAapt(FullRevision.parseRevision("22.1.3"));
+        File aapt = NinePatchAaptProcessorTestUtils.getAapt(FullRevision.parseRevision("22.0.1"));
         return QueuedCruncher.Builder.INSTANCE.newCruncher(aapt.getAbsolutePath(), logger);
     }
 
diff --git a/build-system/changelog.txt b/build-system/changelog.txt
index f73b880..10ec3ec 100644
--- a/build-system/changelog.txt
+++ b/build-system/changelog.txt
@@ -1,12 +1,50 @@
 1.3.0
 - By default, "LICENSE" and "LICENSE.txt" are excluded when creating an APK.
   This can be changed from the DSL:
-  android {
-    packagingOptions.excludes = []
-  }
+
+    android {
+      packagingOptions.excludes = []
+    }
+
 - New sourceSets task for inspecting the set of all available source sets.
 - Unit tests recognize multi-flavor and per-variant source folders (e.g.
   testDemoDebug). Android tests recognized multi-flavor source folders.
+- Unit testing improvements
+  * Run javac on main and test sources, even if useJack is true.
+  * Correctly recognize per-build-type dependencies.
+- It's now possible to specify instrumentation test runner arguments in
+  build.gradle (in defaultConfig or per flavor):
+
+    android {
+      defaultConfig {
+        testInstrumentationRunnerArguments size: "medium"
+      }
+
+      productFlavors {
+        foo {
+          testInstrumentationRunnerArguments foo: "bar"
+        }
+      }
+    }
+
+  or from the command line:
+
+    ./gradlew cC \
+       -Pandroid.testInstrumentationRunnerArguments.size=medium \
+       -Pandroid.testInstrumentationRunnerArguments.class=TestA,TestB
+
+- Arbitrary additional AAPT parameters can be set in build.gradle:
+    android {
+      aaptOptions {
+        additionalParameters "--custom_option", "value"
+      }
+    }
+- Resource names are validated before they are merged.
+- When building aar, do not provide automatic @{applicationId} placeholder
+  in manifest merger. Use a different placeholder like @{libApplicationId}
+  and provide a value for it if applicationIds should be baked in the library.
+- Introduce support for incremental compilation support with Jill and Jack. Change is purely
+  internal and does not require DSL change nor can it be disabled.
 
 1.2.0
 - Unit testing improvements
@@ -21,7 +59,7 @@
   * DSL: new code block for configuring the test tasks:
     android {
       testOptions {
-        unitTest.all {
+        unitTests.all {
           jvmArgs '-XX:MaxPermSize=256m' // Or any other gradle option.
         }
       }
@@ -59,6 +97,8 @@
   * New configurations for adding test-only dependencies, e.g.
       testCompile 'junit:junit:4.11'
       testMyFlavorCompile 'some:library:1.0'
+  * New option, android.testOptions.unitTests.returnDefaultValues to control
+    the behaviour of the "mockable" android.jar.
 - Task names that used to contain 'Test', e.g. 'assembleDebugTest' now use
   'AndroidTest', e.g. 'assembleDebugAndroidTest'. This is to distinguish them
   from the unit test tasks, e.g. 'assembleDebugUnitTest'.
@@ -79,6 +119,7 @@
             useNewCruncher false
         }
     }
+- Improved DSL reference. See http://developer.android.com/tools/building/plugin-for-gradle.html
 
 1.0.0
 - Final 1.0.0 version
diff --git a/build-system/docs/src/fromGradle/docs/css/base.css b/build-system/docs/src/fromGradle/docs/css/base.css
index ec53702..9a208b4 100644
--- a/build-system/docs/src/fromGradle/docs/css/base.css
+++ b/build-system/docs/src/fromGradle/docs/css/base.css
@@ -363,6 +363,10 @@
     margin-right: 10px;
 }
 
+div.chapter > p {
+    margin: 2em 0 2em 0;
+}
+
 .text-container h1, .text-container h2, .text-container h3, .text-container h4, .text-container h5,
 div.book h1, div.book h2, div.book h3, div.book h4, div.book h5,
 div.chapter h1, div.chapter h2, div.chapter h3, div.chapter h4, div.chapter h5 {
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml
index 35c0e95..b182893 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml
@@ -47,6 +47,7 @@
             <tr><td>jacoco</td></tr>
             <tr><td>splits</td></tr>
             <tr><td>adbOptions</td></tr>
+            <tr><td>useLibrary</td></tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml
index f9dc302..293a81f 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml
@@ -8,7 +8,6 @@
             <tr><td>ignoreAssets</td></tr>
             <tr><td>noCompress</td></tr>
             <tr><td>failOnMissingConfigEntry</td></tr>
-            <tr><td>useNewCruncher</td></tr>
         </table>
     </section>
     <section>
@@ -17,6 +16,7 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
+            <tr><td>noCompress</td></tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml
index 126df1f..14afb53 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml
@@ -5,16 +5,22 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
-            <tr><td>debuggable</td></tr>
-            <tr><td>jniDebuggable</td></tr>
-            <tr><td>renderscriptDebuggable</td></tr>
-            <tr><td>renderscriptOptimLevel</td></tr>
             <tr><td>applicationIdSuffix</td></tr>
-            <tr><td>versionNameSuffix</td></tr>
-            <tr><td>signingConfig</td></tr>
-            <tr><td>zipAlignEnabled</td></tr>
+            <tr><td>debuggable</td></tr>
+            <tr><td>embedMicroApp</td></tr>
+            <tr><td>jniDebuggable</td></tr>
             <tr><td>minifyEnabled</td></tr>
             <tr><td>multiDexEnabled</td></tr>
+            <tr><td>name</td></tr>
+            <tr><td>pseudoLocalesEnabled</td></tr>
+            <tr><td>renderscriptDebuggable</td></tr>
+            <tr><td>renderscriptOptimLevel</td></tr>
+            <tr><td>shrinkResources</td></tr>
+            <tr><td>signingConfig</td></tr>
+            <tr><td>testCoverageEnabled</td></tr>
+            <tr><td>useJack</td></tr>
+            <tr><td>versionNameSuffix</td></tr>
+            <tr><td>zipAlignEnabled</td></tr>
         </table>
     </section>
     <section>
@@ -23,9 +29,14 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
+            <tr><td>buildConfigField</td></tr>
+            <tr><td>resValue</td></tr>
             <tr><td>proguardFile</td></tr>
             <tr><td>proguardFiles</td></tr>
+            <tr><td>resValue</td></tr>
             <tr><td>setProguardFiles</td></tr>
+            <tr><td>shrinkResources</td></tr>
+            <tr><td>useJack</td></tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml
index 1e9ce24..da862a7 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml
@@ -5,7 +5,6 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
-            <tr><td>strict</td></tr>
             <tr><td>compatibleScreens</td></tr>
         </table>
     </section>
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.GroupableProductFlavor.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.GroupableProductFlavor.xml
deleted file mode 100644
index 36a19bb..0000000
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.GroupableProductFlavor.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<section>
-    <section>
-        <title>Properties</title>
-        <table>
-            <thead>
-                <tr><td>Name</td></tr>
-            </thead>
-            <tr><td>flavorDimension</td></tr>
-        </table>
-    </section>
-    <section>
-        <title>Methods</title>
-        <table>
-            <thead>
-                <tr><td>Name</td></tr>
-            </thead>
-        </table>
-    </section>
-</section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml
index 1319e0b..72abcb7 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml
@@ -5,16 +5,21 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
+            <tr><td>applicationId</td></tr>
             <tr><td>dimension</td></tr>
+            <tr><td>flavorDimension</td></tr>
+            <tr><td>multiDexEnabled</td></tr>
+            <tr><td>signingConfig</td></tr>
+            <tr><td>testApplicationId</td></tr>
+            <tr><td>testFunctionalTest</td></tr>
+            <tr><td>testHandleProfiling</td></tr>
+            <tr><td>testInstrumentationRunner</td></tr>
+            <tr><td>testInstrumentationRunnerArguments</td></tr>
+            <tr><td>useJack</td></tr>
+            <tr><td>versionCode</td></tr>
             <tr><td>versionCode</td></tr>
             <tr><td>versionName</td></tr>
-            <tr><td>applicationId</td></tr>
-            <tr><td>testApplicationId</td></tr>
-            <tr><td>testInstrumentationRunner</td></tr>
-            <tr><td>signingConfig</td></tr>
-            <tr><td>testHandleProfiling</td></tr>
-            <tr><td>testFunctionalTest</td></tr>
-            <tr><td>multiDexEnabled</td></tr>
+            <tr><td>versionName</td></tr>
         </table>
     </section>
     <section>
@@ -23,11 +28,18 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
-            <tr><td>targetSdkVersion</td></tr>
+            <tr><td>maxSdkVersion</td></tr>
             <tr><td>minSdkVersion</td></tr>
             <tr><td>proguardFile</td></tr>
             <tr><td>proguardFiles</td></tr>
+            <tr><td>resConfig</td></tr>
+            <tr><td>resConfigs</td></tr>
+            <tr><td>resValue</td></tr>
             <tr><td>setProguardFiles</td></tr>
+            <tr><td>targetSdkVersion</td></tr>
+            <tr><td>testInstrumentationRunnerArgument</td></tr>
+            <tr><td>testInstrumentationRunnerArguments</td></tr>
+            <tr><td>useJack</td></tr>
         </table>
     </section>
 </section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml
index bb55ea8..5ec5789 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml
@@ -1,3 +1,19 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
 <section>
     <section>
         <title>Properties</title>
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml
index bb55ea8..43f7a19 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml
@@ -1,3 +1,19 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
 <section>
     <section>
         <title>Properties</title>
@@ -5,6 +21,10 @@
             <thead>
                 <tr><td>Name</td></tr>
             </thead>
+            <tr><td>proguardFiles</td></tr>
+            <tr><td>consumerProguardFiles</td></tr>
+            <tr><td>manifestPlaceholders</td></tr>
+            <tr><td>multiDexEnabled</td></tr>
         </table>
     </section>
     <section>
diff --git a/build-system/docs/src/fromGradle/docs/dsl/dsl.xml b/build-system/docs/src/fromGradle/docs/dsl/dsl.xml
index d78b148..edb4014 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/dsl.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/dsl.xml
@@ -23,19 +23,19 @@
         <title>Android configuration structure</title>
         <table>
             <title>Android configuration blocks</title>
-            <tr><td>defaultConfig</td></tr>
-            <tr><td>sourceSets</td></tr>
-            <tr><td>buildTypes</td></tr>
-            <tr><td>signingConfigs</td></tr>
-            <tr><td>productFlavors</td></tr>
-            <tr><td>testOptions</td></tr>
             <tr><td>aaptOptions</td></tr>
-            <tr><td>lintOptions</td></tr>
-            <tr><td>dexOptions</td></tr>
+            <tr><td>buildTypes</td></tr>
             <tr><td>compileOptions</td></tr>
-            <tr><td>packagingOptions</td></tr>
+            <tr><td>defaultConfig</td></tr>
+            <tr><td>dexOptions</td></tr>
             <tr><td>jacoco</td></tr>
+            <tr><td>lintOptions</td></tr>
+            <tr><td>packagingOptions</td></tr>
+            <tr><td>productFlavors</td></tr>
+            <tr><td>signingConfigs</td></tr>
+            <tr><td>sourceSets</td></tr>
             <tr><td>splits</td></tr>
+            <tr><td>testOptions</td></tr>
         </table>
     </section>
 
@@ -44,25 +44,26 @@
         <para>Listed below are some of the central types which are used in the Android plugin:</para>
         <table>
             <title>DSL types</title>
-            <tr><td>com.android.build.gradle.BaseExtension</td></tr>
-            <tr><td>com.android.build.gradle.TestedExtension</td></tr>
             <tr><td>com.android.build.gradle.AppExtension</td></tr>
+            <tr><td>com.android.build.gradle.BaseExtension</td></tr>
             <tr><td>com.android.build.gradle.LibraryExtension</td></tr>
             <tr><td>com.android.build.gradle.TestExtension</td></tr>
+            <tr><td>com.android.build.gradle.api.AndroidSourceDirectorySet</td></tr>
+            <tr><td>com.android.build.gradle.api.AndroidSourceFile</td></tr>
             <tr><td>com.android.build.gradle.api.AndroidSourceSet</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.ProductFlavor</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.GroupableProductFlavor</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.BuildType</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.SigningConfig</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.TestOptions</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.AaptOptions</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.LintOptions</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.DexOptions</td></tr>
             <tr><td>com.android.build.gradle.internal.CompileOptions</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.PackagingOptions</td></tr>
-            <tr><td>com.android.build.gradle.internal.coverage.JacocoExtension</td></tr>
-            <tr><td>com.android.build.gradle.internal.dsl.Splits</td></tr>
             <tr><td>com.android.build.gradle.internal.api.VariantFilter</td></tr>
+            <tr><td>com.android.build.gradle.internal.coverage.JacocoExtension</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.AaptOptions</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.BuildType</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.DexOptions</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.LintOptions</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.PackagingOptions</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.ProductFlavor</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.SigningConfig</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.Splits</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.TestOptions.UnitTestOptions</td></tr>
+            <tr><td>com.android.build.gradle.internal.dsl.TestOptions</td></tr>
         </table>
     </section>
 </book>
diff --git a/build-system/google-services/build.gradle b/build-system/google-services/build.gradle
new file mode 100644
index 0000000..e5afe75
--- /dev/null
+++ b/build-system/google-services/build.gradle
@@ -0,0 +1,48 @@
+apply plugin: 'groovy'
+apply plugin: 'clone-artifacts'
+apply plugin: 'idea'
+apply plugin: 'jacoco'
+
+dependencies {
+    compile gradleApi()
+    compile project(':base:gradle')
+
+    testCompile 'junit:junit:4.12'
+}
+
+ext {
+
+}
+
+group = 'com.google.gms'
+archivesBaseName = 'google-services'
+version = rootProject.ext.buildVersion
+
+project.ext.pomName = 'Gradle Plug-in for Google Services'
+project.ext.pomDesc = 'Gradle plug-in for Google Services'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+
+groovydoc {
+    exclude     "**/internal/**"
+    includePrivate false
+
+    docTitle "Gradle Plugin for Google Services"
+    header ""
+    footer "Copyright (C) 2015 The Android Open Source Project"
+    overview ""
+}
+
+task javadocJar(type: Jar, dependsOn:groovydoc) {
+    classifier  'javadoc'
+    from        groovydoc.destinationDir
+}
+
+// Only package JavaDoc if using --init-script=buildSrc/base/release.gradle
+if (project.has("release")) {
+  artifacts {
+    archives javadocJar
+  }
+}
+
diff --git a/build-system/google-services/google-services.iml b/build-system/google-services/google-services.iml
new file mode 100644
index 0000000..772aed8
--- /dev/null
+++ b/build-system/google-services/google-services.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/groovy" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/groovy" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="library" name="groovy" level="project" />
+    <orderEntry type="module" module-name="gradle" exported="" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesPlugin.groovy b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesPlugin.groovy
new file mode 100644
index 0000000..00b8c79
--- /dev/null
+++ b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesPlugin.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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.google.gms.googleservices
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+class GoogleServicesPlugin implements Plugin<Project> {
+
+    public final static String JSON_FILE_NAME = 'google-services.json'
+
+    @Override
+    void apply(Project project) {
+        // setup this plugin no matter the order.
+        if (!checkForKnownPlugins(project)) {
+            project.plugins.whenPluginAdded {
+                checkForKnownPlugins(project)
+            }
+        }
+    }
+
+    private void setupPlugin(Project project, boolean isLibrary) {
+        if (isLibrary) {
+            project.android.libraryVariants.all { variant ->
+                handleVariant(project, variant)
+            }
+        } else {
+            project.android.applicationVariants.all { variant ->
+                handleVariant(project, variant)
+            }
+        }
+    }
+
+    private static void handleVariant(Project project, def variant) {
+        File quickstartFile = project.file(JSON_FILE_NAME)
+        File outputDir = project.file("$project.buildDir/generated/res/google-services/$variant.dirName")
+
+        GoogleServicesTask task = project.tasks.create("process${variant.name.capitalize()}GoogleServices", GoogleServicesTask)
+
+        task.quickstartFile = quickstartFile
+        task.intermediateDir = outputDir
+        task.packageName = variant.applicationId
+
+        variant.registerResGeneratingTask(task, outputDir)
+    }
+
+    private boolean checkForKnownPlugins(Project project) {
+        if (project.plugins.hasPlugin("android") ||
+                project.plugins.hasPlugin("com.android.application")) {
+            // this is a bit fragile but since this is internal usage this is ok
+            // (another plugin could declare itself to be 'android')
+            setupPlugin(project, false)
+            return true
+        } else if (project.plugins.hasPlugin("android-library") ||
+                project.plugins.hasPlugin("com.android.library")) {
+            // this is a bit fragile but since this is internal usage this is ok
+            // (another plugin could declare itself to be 'android-library')
+            setupPlugin(project, true)
+            return true
+        }
+        return false
+    }
+}
diff --git a/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesTask.java b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesTask.java
new file mode 100644
index 0000000..aadd5bb
--- /dev/null
+++ b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesTask.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2015 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.google.gms.googleservices;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ */
+public class GoogleServicesTask extends DefaultTask {
+
+    private static final String STATUS_DISABLED = "1";
+    private static final String STATUS_ENABLED = "2";
+
+    /**
+     * The input is not technically optional but we want to control the error message.
+     * Without @Optional, Gradle will complain itself the file is missing.
+     */
+    @InputFile @Optional
+    public File quickstartFile;
+
+    @OutputDirectory
+    public File intermediateDir;
+
+    @Input
+    public String packageName;
+
+    @TaskAction
+    public void action() throws IOException {
+        if (!quickstartFile.isFile()) {
+            getLogger().warn("File " + quickstartFile.getName() + " is missing from module root folder." +
+                    " The Google Quickstart Plugin cannot function without it.");
+
+            // Skip the rest of the actions because it would not make sense if `quickstartFile` is missing.
+            return;
+        }
+
+        // delete content of outputdir.
+        deleteFolder(intermediateDir);
+        if (!intermediateDir.mkdirs()) {
+            throw new GradleException("Failed to create folder: " + intermediateDir);
+        }
+
+        JsonElement root = new JsonParser().parse(Files.newReader(quickstartFile, Charsets.UTF_8));
+
+        if (!root.isJsonObject()) {
+            throw new GradleException("Malformed root json");
+        }
+
+        JsonObject rootObject = root.getAsJsonObject();
+
+        Map<String, String> resValues = new TreeMap<String, String>();
+
+        handleProjectNumber(rootObject, resValues);
+
+        JsonObject clientObject = getClientForPackageName(rootObject);
+
+        if (clientObject != null) {
+            handleAnalytics(clientObject, resValues);
+            handleAdsService(clientObject, resValues);
+        } else {
+            getLogger().warn("No matching client found for package name '" + packageName + "'");
+        }
+
+        // write the values file.
+        File values = new File(intermediateDir, "values");
+        if (!values.exists() && !values.mkdirs()) {
+            throw new GradleException("Failed to create folder: " + values);
+        }
+
+        Files.write(getValuesContent(resValues), new File(values, "values.xml"), Charsets.UTF_8);
+    }
+
+    /**
+     * Handle project_info/project_number for @string/gcm_defaultSenderId, and fill the res map with the read value.
+     * @param rootObject the root Json object.
+     * @throws IOException
+     */
+    private void handleProjectNumber(JsonObject rootObject, Map<String, String> resValues)
+            throws IOException {
+        JsonObject projectInfo = rootObject.getAsJsonObject("project_info");
+        if (projectInfo == null) {
+            throw new GradleException("Missing project_info object");
+        }
+
+        JsonPrimitive projectNumber = projectInfo.getAsJsonPrimitive("project_number");
+        if (projectNumber == null) {
+            throw new GradleException("Missing project_info/project_number object");
+        }
+
+        resValues.put("gcm_defaultSenderId", projectNumber.getAsString());
+    }
+
+    /**
+     * Handle a client object for analytics (@xml/global_tracker)
+     * @param clientObject the client Json object.
+     * @throws IOException
+     */
+    private void handleAnalytics(JsonObject clientObject, Map<String, String> resValues)
+            throws IOException {
+        JsonObject analyticsService = getServiceByName(clientObject, "analytics_service");
+        if (analyticsService == null) return;
+
+        JsonObject analyticsProp = analyticsService.getAsJsonObject("analytics_property");
+        if (analyticsProp == null) return;
+
+        JsonPrimitive trackingId = analyticsProp.getAsJsonPrimitive("tracking_id");
+        if (trackingId == null) return;
+
+        resValues.put("ga_trackingId", trackingId.getAsString());
+
+        File xml = new File(intermediateDir, "xml");
+        if (!xml.exists() && !xml.mkdirs()) {
+            throw new GradleException("Failed to create folder: " + xml);
+        }
+
+        Files.write(getGlobalTrackerContent(
+                trackingId.getAsString()),
+                new File(xml, "global_tracker.xml"),
+                Charsets.UTF_8);
+    }
+
+    /**
+     * Handle a client object for analytics (@xml/global_tracker)
+     * @param clientObject the client Json object.
+     * @throws IOException
+     */
+    private void handleAdsService(JsonObject clientObject, Map<String, String> resValues)
+            throws IOException {
+        JsonObject adsService = getServiceByName(clientObject, "ads_service");
+        if (adsService == null) return;
+
+        findStringByName(adsService, "test_banner_ad_unit_id", resValues);
+        findStringByName(adsService, "test_interstitial_ad_unit_id", resValues);
+    }
+
+    private static void findStringByName(JsonObject jsonObject, String stringName,
+            Map<String, String> resValues) {
+        JsonPrimitive id = jsonObject.getAsJsonPrimitive(stringName);
+        if (id != null) {
+            resValues.put(stringName, id.getAsString());
+        }
+    }
+
+    /**
+     * find an item in the "client" array that match the package name of the app
+     * @param jsonObject the root json object.
+     * @return a JsonObject representing the client entry or null if no match is found.
+     */
+    private JsonObject getClientForPackageName(JsonObject jsonObject) {
+        JsonArray array = jsonObject.getAsJsonArray("client");
+        if (array != null) {
+            final int count = array.size();
+            for (int i = 0 ; i < count ; i++) {
+                JsonElement clientElement = array.get(i);
+                if (clientElement == null || !clientElement.isJsonObject()) {
+                    continue;
+                }
+
+                JsonObject clientObject = clientElement.getAsJsonObject();
+
+                JsonObject clientInfo = clientObject.getAsJsonObject("client_info");
+                if (clientInfo == null) continue;
+
+                JsonObject androidClientInfo = clientInfo.getAsJsonObject("android_client_info");
+                if (androidClientInfo == null) continue;
+
+                JsonPrimitive clientPackageName = androidClientInfo.getAsJsonPrimitive("package_name");
+                if (clientPackageName == null) continue;
+
+                if (packageName.equals(clientPackageName.getAsString())) {
+                    return clientObject;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Finds a service by name in the client object. Returns null if the service is not found
+     * or if the service is disabled.
+     *
+     * @param clientObject the json object that represents the client.
+     * @param serviceName the service name
+     * @return the service if found.
+     */
+    private JsonObject getServiceByName(JsonObject clientObject, String serviceName) {
+        JsonObject services = clientObject.getAsJsonObject("services");
+        if (services == null) return null;
+
+        JsonObject service = services.getAsJsonObject(serviceName);
+        if (service == null) return null;
+
+        JsonPrimitive status = service.getAsJsonPrimitive("status");
+        if (status == null) return null;
+
+        String statusStr = status.getAsString();
+
+        if (STATUS_DISABLED.equals(statusStr)) return null;
+        if (!STATUS_ENABLED.equals(statusStr)) {
+            getLogger().warn(String.format("Status with value '%1$s' for service '%2$s' is unknown",
+                    statusStr,
+                    serviceName));
+            return null;
+        }
+
+        return service;
+    }
+
+
+    private static String getGlobalTrackerContent(String ga_trackingId) {
+        return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+                "<resources>\n" +
+                "    <string name=\"ga_trackingId\">" + ga_trackingId + "</string>\n" +
+                "</resources>\n";
+    }
+
+    private static String getValuesContent(Map<String, String> entries) {
+        StringBuilder sb = new StringBuilder(256);
+
+        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+                "<resources>\n");
+
+        for (Map.Entry<String, String> entry : entries.entrySet()) {
+            sb.append("    <string name=\"").append(entry.getKey()).append("\">")
+                    .append(entry.getValue()).append("</string>\n");
+        }
+
+        sb.append("</resources>\n");
+
+        return sb.toString();
+    }
+
+    private static void deleteFolder(final File folder) {
+        if (!folder.exists()) {
+            return;
+        }
+        File[] files = folder.listFiles();
+        if (files != null) {
+            for (final File file : files) {
+                if (file.isDirectory()) {
+                    deleteFolder(file);
+                } else {
+                    if (!file.delete()) {
+                        throw new GradleException("Failed to delete: " + file);
+                    }
+                }
+            }
+        }
+        if (!folder.delete()) {
+            throw new GradleException("Failed to delete: " + folder);
+        }
+    }
+}
diff --git a/build-system/google-services/src/main/resources/META-INF/gradle-plugins/com.google.gms.google-services.properties b/build-system/google-services/src/main/resources/META-INF/gradle-plugins/com.google.gms.google-services.properties
new file mode 100644
index 0000000..592bed6
--- /dev/null
+++ b/build-system/google-services/src/main/resources/META-INF/gradle-plugins/com.google.gms.google-services.properties
@@ -0,0 +1 @@
+implementation-class=com.google.gms.googleservices.GoogleServicesPlugin
diff --git a/build-system/gradle-core/build.gradle b/build-system/gradle-core/build.gradle
index d0815d9..2505e6d 100644
--- a/build-system/gradle-core/build.gradle
+++ b/build-system/gradle-core/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'groovy'
+apply plugin: 'jacoco'
 apply plugin: 'clone-artifacts'
 
 configurations {
@@ -37,6 +29,7 @@
     testCompile 'junit:junit:4.12'
     testCompile 'org.mockito:mockito-all:1.9.5'
     testCompile project(':base:project-test-lib')
+    testCompile project(':base:testutils')
 }
 
 tasks.compileJava.dependsOn ":setupGradleInIde"
@@ -69,6 +62,8 @@
     header ""
     footer "Copyright (C) 2012 The Android Open Source Project"
     overview ""
+
+    groovyClasspath = configurations.provided
 }
 
 task javadocJar(type: Jar, dependsOn:groovydoc) {
diff --git a/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java b/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java
index 4f410a8..9ea3e7e 100644
--- a/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java
+++ b/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java
@@ -30,7 +30,7 @@
 
     public PackageTestResults(String name, AllTestResults model) {
         super(model);
-        this.name = name.length() == 0 ? DEFAULT_PACKAGE : name;
+        this.name = name.isEmpty() ? DEFAULT_PACKAGE : name;
     }
 
     @Override
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
new file mode 100644
index 0000000..ec7373f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 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;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.model.SigningConfig;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.repository.FullRevision;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * User configuration settings for all android plugins.
+ */
+public interface AndroidConfig {
+
+    /** Build tool version */
+    String getBuildToolsVersion();
+
+    /** Compile SDK version */
+    String getCompileSdkVersion();
+
+    /** Build tool revisions */
+    FullRevision getBuildToolsRevision();
+
+    /** Name of the variant to publish */
+    String getDefaultPublishConfig();
+
+    /** Whether to also publish non-default variants */
+    boolean getPublishNonDefault();
+
+    /** Filter to determine which variants to build */
+    Action<VariantFilter> getVariantFilter();
+
+    /** Adb options */
+    AdbOptions getAdbOptions();
+
+    /** A prefix to be used when creating new resources. Used by Studio */
+    String getResourcePrefix();
+
+    /** List of flavor dimensions */
+    List<String> getFlavorDimensionList();
+
+    /** Whether to generate pure splits or multi apk */
+    boolean getGeneratePureSplits();
+
+    /** Preprocessing Options */
+    PreprocessingOptions getPreprocessingOptions();
+
+    @Deprecated
+    boolean getEnforceUniquePackageName();
+
+    /** Default config, shared by all flavors. */
+    CoreProductFlavor getDefaultConfig();
+
+    /** Options for aapt, tool for packaging resources. */
+    AaptOptions getAaptOptions();
+
+    /** Compile options */
+    CompileOptions getCompileOptions();
+
+    /** Dex options. */
+    DexOptions getDexOptions();
+
+    /** JaCoCo options. */
+    JacocoExtension getJacoco();
+
+    /** Lint options. */
+    LintOptions getLintOptions();
+
+    /** Packaging options. */
+    PackagingOptions getPackagingOptions();
+
+    /** APK splits */
+    Splits getSplits();
+
+    /** Options for running tests. */
+    TestOptions getTestOptions();
+
+    /** List of device providers */
+    @NonNull
+    List<DeviceProvider> getDeviceProviders();
+
+    /** List of remote CI servers */
+    @NonNull
+    List<TestServer> getTestServers();
+
+    /** All product flavors used by this project. */
+    Collection<? extends CoreProductFlavor> getProductFlavors();
+
+    /** Build types used by this project. */
+    Collection<? extends CoreBuildType> getBuildTypes();
+
+    /** Signing configs used by this project. */
+    Collection<? extends SigningConfig> getSigningConfigs();
+
+    /** Source sets for all variants */
+    NamedDomainObjectContainer<AndroidSourceSet> getSourceSets();
+
+    /** Whether to package build config class file */
+    Boolean getPackageBuildConfig();
+
+    Collection<LibraryRequest> getLibraryRequests();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidGradleOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidGradleOptions.java
new file mode 100644
index 0000000..7d71dd3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidGradleOptions.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 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;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidProject;
+import com.google.common.collect.Maps;
+
+import org.gradle.api.Project;
+
+import java.util.Map;
+
+/**
+ * Determines if various options, triggered from the command line or environment, are set.
+ */
+public class AndroidGradleOptions {
+
+    private static final String PROPERTY_TEST_RUNNER_ARGS =
+            "android.testInstrumentationRunnerArguments.";
+
+    // TODO: Drop the "com." prefix, for consistency.
+    private static final String PROPERTY_BENCHMARK_NAME = "com.android.benchmark.name";
+    private static final String PROPERTY_BENCHMARK_MODE = "com.android.benchmark.mode";
+
+    @NonNull
+    public static Map<String, String> getExtraInstrumentationTestRunnerArgs(@NonNull Project project) {
+        Map<String, String> argsMap = Maps.newHashMap();
+        for (Map.Entry<String, ?> entry : project.getProperties().entrySet()) {
+            if (entry.getKey().startsWith(PROPERTY_TEST_RUNNER_ARGS)) {
+                String argName = entry.getKey().substring(PROPERTY_TEST_RUNNER_ARGS.length());
+                String argValue = entry.getValue().toString();
+
+                argsMap.put(argName, argValue);
+            }
+        }
+
+        return argsMap;
+    }
+
+    @Nullable
+    public static String getBenchmarkName(@NonNull Project project) {
+        return getString(project, PROPERTY_BENCHMARK_NAME);
+    }
+
+    @Nullable
+    public static String getBenchmarkMode(@NonNull Project project) {
+        return getString(project, PROPERTY_BENCHMARK_MODE);
+    }
+
+    public static boolean invokedFromIde(@NonNull Project project) {
+        return getBoolean(project, AndroidProject.PROPERTY_INVOKED_FROM_IDE);
+    }
+
+    public static boolean buildModelOnly(@NonNull Project project) {
+        return getBoolean(project, AndroidProject.PROPERTY_BUILD_MODEL_ONLY);
+    }
+
+    public static boolean buildModelOnlyAdvanced(@NonNull Project project) {
+        return getBoolean(project, AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED);
+    }
+
+    @Nullable
+    public static String getApkLocation(@NonNull Project project) {
+        return getString(project, AndroidProject.PROPERTY_APK_LOCATION);
+    }
+
+    @Nullable
+    public static SigningOptions getSigningOptions(@NonNull Project project) {
+        String signingStoreFile =
+                getString(project, AndroidProject.PROPERTY_SIGNING_STORE_FILE);
+        String signingStorePassword =
+                getString(project, AndroidProject.PROPERTY_SIGNING_STORE_PASSWORD);
+        String signingKeyAlias =
+                getString(project, AndroidProject.PROPERTY_SIGNING_KEY_ALIAS);
+        String signingKeyPassword =
+                getString(project, AndroidProject.PROPERTY_SIGNING_KEY_PASSWORD);
+
+        if (signingStoreFile != null
+                && signingStorePassword != null
+                && signingKeyAlias != null
+                && signingKeyPassword != null) {
+            String signingStoreType =
+                    getString(project, AndroidProject.PROPERTY_SIGNING_STORE_TYPE);
+
+            return new SigningOptions(
+                    signingStoreFile,
+                    signingStorePassword,
+                    signingKeyAlias,
+                    signingKeyPassword,
+                    signingStoreType);
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static String getString(@NonNull Project project, String propertyName) {
+        return (String) project.getProperties().get(propertyName);
+    }
+
+    private static boolean getBoolean(
+            @NonNull Project project,
+            @NonNull String propertyName) {
+        if (project.hasProperty(propertyName)) {
+            Object value = project.getProperties().get(propertyName);
+            if (value instanceof String) {
+                return Boolean.parseBoolean((String) value);
+            }
+        }
+
+        return false;
+    }
+
+    public static class SigningOptions {
+        @NonNull public final String storeFile;
+        @NonNull public final String storePassword;
+        @NonNull public final String keyAlias;
+        @NonNull public final String keyPassword;
+        @Nullable public final String storeType;
+
+        public SigningOptions(
+                @NonNull String storeFile,
+                @NonNull String storePassword,
+                @NonNull String keyAlias,
+                @NonNull String keyPassword,
+                @Nullable String storeType) {
+            this.storeFile = storeFile;
+            this.storeType = storeType;
+            this.storePassword = storePassword;
+            this.keyAlias = keyAlias;
+            this.keyPassword = keyPassword;
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AppExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AppExtension.groovy
deleted file mode 100644
index ce05288..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AppExtension.groovy
+++ /dev/null
@@ -1,68 +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.build.gradle
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.api.ApplicationVariant
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * 'android' extension for 'com.android.application' project.
- */
-@CompileStatic
-public class AppExtension extends TestedExtension {
-
-    private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList =
-        new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class)
-
-    AppExtension(
-            @NonNull ProjectInternal project,
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull SdkHandler sdkHandler,
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
-            @NonNull ExtraModelInfo extraModelInfo,
-            boolean isLibrary) {
-        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
-                signingConfigs, extraModelInfo, isLibrary)
-    }
-
-    /**
-     * Returns the list of Application variants. Since the collections is built after evaluation,
-     * it should be used with Gradle's <code>all</code> iterator to process future items.
-     */
-    public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
-        return applicationVariantList
-    }
-
-    @Override
-    void addVariant(BaseVariant variant) {
-        applicationVariantList.add((ApplicationVariant) variant)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/BaseExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
deleted file mode 100644
index 1a3d81e..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
+++ /dev/null
@@ -1,623 +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.build.gradle
-
-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.internal.CompileOptions
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.SourceSetSourceProviderWrapper
-import com.android.build.gradle.internal.coverage.JacocoExtension
-import com.android.build.gradle.internal.dsl.AaptOptions
-import com.android.build.gradle.internal.dsl.AdbOptions
-import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.DexOptions
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.LintOptions
-import com.android.build.gradle.internal.dsl.PackagingOptions
-import com.android.build.gradle.internal.dsl.ProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.dsl.Splits
-import com.android.build.gradle.internal.dsl.TestOptions
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-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
-import com.google.common.collect.Lists
-import groovy.transform.CompileStatic
-import org.gradle.api.Action
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.logging.Logger
-import org.gradle.api.logging.Logging
-import org.gradle.api.tasks.SourceSet
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * Base 'android' extension for all android plugins.
- *
- * <p>This is never used directly. Instead,
- *<ul>
- * <li>Plugin 'com.android.application' uses {@link AppExtension}</li>
- * <li>Plugin 'com.android.library' uses {@link LibraryExtension}</li>
- * </ul>
- */
-public abstract class BaseExtension {
-
-    private String target
-    private FullRevision buildToolsRevision
-
-    /** Default config, shared by all flavors. */
-    final ProductFlavor defaultConfig
-
-    /** Options for aapt, tool for packaging resources. */
-    final AaptOptions aaptOptions
-
-    /** Lint options. */
-    final LintOptions lintOptions
-
-    /** Dex options. */
-    final DexOptions dexOptions
-
-    /** Options for running tests. */
-    final TestOptions testOptions
-
-    /** Compile options */
-    final CompileOptions compileOptions
-
-    /** Packaging options. */
-    final PackagingOptions packagingOptions
-
-    /** JaCoCo options. */
-    final JacocoExtension jacoco
-
-    /** APK splits */
-    final Splits splits
-
-    /** All product flavors used by this project. */
-    final NamedDomainObjectContainer<GroupableProductFlavor> productFlavors
-
-    /** Build types used by this project. */
-    final NamedDomainObjectContainer<BuildType> buildTypes
-
-    /** Signing configs used by this project. */
-    final NamedDomainObjectContainer<SigningConfig> signingConfigs
-
-    private ExtraModelInfo extraModelInfo
-
-    protected Project project
-
-    /** Adb options */
-    final AdbOptions adbOptions;
-
-    /** A prefix to be used when creating new resources. Used by Studio */
-    String resourcePrefix
-
-    List<String> flavorDimensionList
-
-    private String defaultPublishConfig = "release"
-    private boolean publishNonDefault = false
-
-    private Closure<Void> variantFilter
-
-    private final List<DeviceProvider> deviceProviderList = Lists.newArrayList();
-    private final List<TestServer> testServerList = Lists.newArrayList();
-
-    private final AndroidBuilder androidBuilder
-
-    private final SdkHandler sdkHandler
-
-    protected Logger logger
-
-    private boolean isWritable = true;
-
-    /**
-     * The source sets container.
-     */
-    final NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer
-
-    BaseExtension(
-            @NonNull ProjectInternal project,
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull SdkHandler sdkHandler,
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
-            @NonNull ExtraModelInfo extraModelInfo,
-            boolean isLibrary) {
-        this.androidBuilder = androidBuilder
-        this.sdkHandler = sdkHandler
-        this.buildTypes = buildTypes
-        this.productFlavors = productFlavors
-        this.signingConfigs = signingConfigs
-        this.extraModelInfo = extraModelInfo
-        this.project = project
-
-        logger = Logging.getLogger(this.class)
-
-        defaultConfig = instantiator.newInstance(ProductFlavor, BuilderConstants.MAIN,
-                project, instantiator, project.getLogger())
-
-        aaptOptions = instantiator.newInstance(AaptOptions)
-        dexOptions = instantiator.newInstance(DexOptions)
-        lintOptions = instantiator.newInstance(LintOptions)
-        testOptions = instantiator.newInstance(TestOptions)
-        compileOptions = instantiator.newInstance(CompileOptions)
-        packagingOptions = instantiator.newInstance(PackagingOptions)
-        jacoco = instantiator.newInstance(JacocoExtension)
-        adbOptions = instantiator.newInstance(AdbOptions)
-        splits = instantiator.newInstance(Splits, instantiator)
-
-        sourceSetsContainer = project.container(AndroidSourceSet,
-                new AndroidSourceSetFactory(instantiator, project, isLibrary))
-
-        sourceSetsContainer.whenObjectAdded { AndroidSourceSet sourceSet ->
-            ConfigurationContainer configurations = project.getConfigurations()
-
-            createConfiguration(
-                    configurations,
-                    sourceSet.getCompileConfigurationName(),
-                    "Classpath for compiling the ${sourceSet.name} sources.")
-
-            String packageConfigDescription
-            if (isLibrary) {
-                packageConfigDescription = "Classpath only used when publishing '${sourceSet.name}'."
-            } else {
-                packageConfigDescription = "Classpath packaged with the compiled '${sourceSet.name}' classes."
-            }
-            createConfiguration(
-                    configurations,
-                    sourceSet.getPackageConfigurationName(),
-                    packageConfigDescription)
-
-            createConfiguration(
-                    configurations,
-                    sourceSet.getProvidedConfigurationName(),
-                    "Classpath for only compiling the ${sourceSet.name} sources.")
-
-            createConfiguration(
-                    configurations,
-                    sourceSet.getWearAppConfigurationName(),
-                    "Link to a wear app to embed for object '${sourceSet.name}'.")
-
-            sourceSet.setRoot(String.format("src/%s", sourceSet.getName()))
-        }
-
-        sourceSetsContainer.create(defaultConfig.name)
-    }
-
-    /**
-     * Disallow further modification on the extension.
-     */
-    public void disableWrite() {
-        isWritable = false
-    }
-
-    protected checkWritability() {
-        if (!isWritable) {
-            throw new GradleException(
-                    "Android tasks have already been created.\n" +
-                            "This happens when calling android.applicationVariants,\n" +
-                            "android.libraryVariants or android.testVariants.\n" +
-                            "Once these methods are called, it is not possible to\n" +
-                            "continue configuring the model.")
-        }
-    }
-
-    protected void createConfiguration(
-            @NonNull ConfigurationContainer configurations,
-            @NonNull String configurationName,
-            @NonNull String configurationDescription) {
-        logger.info("Creating configuration ${configurationName}.")
-
-        Configuration configuration = configurations.findByName(configurationName)
-        if (configuration == null) {
-            configuration = configurations.create(configurationName)
-        }
-        configuration.setVisible(false);
-        configuration.setDescription(configurationDescription)
-    }
-
-    /**
-     * Sets the compile SDK version, based on full SDK version string, e.g.
-     * <code>android-21</code> for Lollipop.
-     */
-    void compileSdkVersion(String version) {
-        checkWritability()
-        this.target = version
-    }
-
-    /**
-     * Sets the compile SDK version, based on API level, e.g. 21 for Lollipop.
-     */
-    void compileSdkVersion(int apiLevel) {
-        compileSdkVersion("android-" + apiLevel)
-    }
-
-    void setCompileSdkVersion(int apiLevel) {
-        compileSdkVersion(apiLevel)
-    }
-
-    void setCompileSdkVersion(String target) {
-        compileSdkVersion(target)
-    }
-
-    void buildToolsVersion(String version) {
-        checkWritability()
-        buildToolsRevision = FullRevision.parseRevision(version)
-    }
-
-    /**
-     * <strong>Required.</strong> Version of the build tools to use.
-     *
-     * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
-     * back may give a slightly different string.
-     */
-    String getBuildToolsVersion() {
-        return buildToolsRevision.toString()
-    }
-
-    void setBuildToolsVersion(String version) {
-        buildToolsVersion(version)
-    }
-
-    /**
-     * Configures the build types.
-     */
-    void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
-        checkWritability()
-        action.execute(buildTypes)
-    }
-
-    /**
-     * Configures the product flavors.
-     */
-    void productFlavors(Action<? super NamedDomainObjectContainer<GroupableProductFlavor>> action) {
-        checkWritability()
-        action.execute(productFlavors)
-    }
-
-    /**
-     * Configures the signing configs.
-     */
-    void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {
-        checkWritability()
-        action.execute(signingConfigs)
-    }
-
-    public void flavorDimensions(String... dimensions) {
-        checkWritability()
-        flavorDimensionList = Arrays.asList(dimensions)
-    }
-
-    /**
-     * Configures the source sets. Note that the Android plugin uses its own implementation of
-     * source sets, {@link AndroidSourceSet}.
-     */
-    void sourceSets(Action<NamedDomainObjectContainer<AndroidSourceSet>> action) {
-        checkWritability()
-        action.execute(sourceSetsContainer)
-    }
-
-    /**
-     * All source sets. Note that the Android plugin uses its own implementation of
-     * source sets, {@link AndroidSourceSet}.
-     */
-    NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
-        sourceSetsContainer
-    }
-
-    /**
-     * The default configuration, inherited by all build flavors (if any are defined).
-     */
-    void defaultConfig(Action<ProductFlavor> action) {
-        checkWritability()
-        action.execute(defaultConfig)
-    }
-
-    /**
-     * Configures aapt options.
-     */
-    void aaptOptions(Action<AaptOptions> action) {
-        checkWritability()
-        action.execute(aaptOptions)
-    }
-
-    /**
-     * Configures dex options.
-     * @param action
-     */
-    void dexOptions(Action<DexOptions> action) {
-        checkWritability()
-        action.execute(dexOptions)
-    }
-
-    /**
-     * Configure lint options.
-     */
-    void lintOptions(Action<LintOptions> action) {
-        checkWritability()
-        action.execute(lintOptions)
-    }
-
-    /** Configures the test options. */
-    void testOptions(Action<TestOptions> action) {
-        checkWritability()
-        action.execute(testOptions)
-    }
-
-    /**
-     * Configures compile options.
-     */
-    void compileOptions(Action<CompileOptions> action) {
-        checkWritability()
-        action.execute(compileOptions)
-    }
-
-    /**
-     * Configures packaging options.
-     */
-    void packagingOptions(Action<PackagingOptions> action) {
-        checkWritability()
-        action.execute(packagingOptions)
-    }
-
-    /**
-     * Configures JaCoCo options.
-     */
-    void jacoco(Action<JacocoExtension> action) {
-        checkWritability()
-        action.execute(jacoco)
-    }
-
-    /**
-     * Configures adb options.
-     */
-    void adbOptions(Action<AdbOptions> action) {
-        checkWritability()
-        action.execute(adbOptions)
-    }
-
-    /**
-     * Configures APK splits.
-     */
-    void splits(Action<Splits> action) {
-        checkWritability()
-        action.execute(splits)
-    }
-
-    void deviceProvider(DeviceProvider deviceProvider) {
-        checkWritability()
-        deviceProviderList.add(deviceProvider)
-    }
-
-    @NonNull
-    List<DeviceProvider> getDeviceProviders() {
-        return deviceProviderList
-    }
-
-    void testServer(TestServer testServer) {
-        checkWritability()
-        testServerList.add(testServer)
-    }
-
-    @NonNull
-    List<TestServer> getTestServers() {
-        return testServerList
-    }
-
-    public void defaultPublishConfig(String value) {
-        setDefaultPublishConfig(value)
-    }
-
-    public void publishNonDefault(boolean value) {
-        publishNonDefault = value
-    }
-
-    /**
-     * Name of the configuration used to build the default artifact of this project.
-     *
-     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
-     * Referencing a Library</a>
-     */
-    public String getDefaultPublishConfig() {
-        return defaultPublishConfig
-    }
-
-    public void setDefaultPublishConfig(String value) {
-        defaultPublishConfig = value
-    }
-
-    /**
-     * Whether to publish artifacts for all configurations, not just the default one.
-     *
-     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
-     * Referencing a Library</a>
-     */
-    public boolean getPublishNonDefault() {
-        return publishNonDefault
-    }
-
-    /**
-     * Sets a variant filter to control which variant are excluded. The closure is passed a single
-     * object of type {@link com.android.build.gradle.internal.api.VariantFilter}
-     * @param filter the filter as a closure
-     */
-    void variantFilter(Closure<Void> filter) {
-        setVariantFilter(filter)
-    }
-
-    /**
-     * Sets a variant filter to control which variant are excluded. The closure is passed a single
-     * object of type {@link com.android.build.gradle.internal.api.VariantFilter}
-     * @param filter the filter as a closure
-     */
-    void setVariantFilter(Closure<Void> filter) {
-        variantFilter = filter
-    }
-
-    /**
-     * A variant filter to control which variant are excluded. The filter is a closure which
-     * is passed a single object of type {@link com.android.build.gradle.internal.api.VariantFilter}
-     */
-    public Closure<Void> getVariantFilter() {
-        return variantFilter;
-    }
-
-    void resourcePrefix(String prefix) {
-        resourcePrefix = prefix
-    }
-
-    abstract void addVariant(BaseVariant variant)
-
-    public void registerArtifactType(@NonNull String name,
-                                     boolean isTest,
-                                     int artifactType) {
-        extraModelInfo.registerArtifactType(name, isTest, artifactType)
-    }
-
-    public void registerBuildTypeSourceProvider(
-            @NonNull String name,
-            @NonNull BuildType buildType,
-            @NonNull SourceProvider sourceProvider) {
-        extraModelInfo.registerBuildTypeSourceProvider(name, buildType, sourceProvider)
-    }
-
-    public void registerProductFlavorSourceProvider(
-            @NonNull String name,
-            @NonNull ProductFlavor productFlavor,
-            @NonNull SourceProvider sourceProvider) {
-        extraModelInfo.registerProductFlavorSourceProvider(name, productFlavor, sourceProvider)
-    }
-
-    @CompileStatic
-    public void registerJavaArtifact(
-            @NonNull String name,
-            @NonNull BaseVariant variant,
-            @NonNull String assembleTaskName,
-            @NonNull String javaCompileTaskName,
-            @NonNull Collection<File> generatedSourceFolders,
-            @NonNull Iterable<String> ideSetupTaskNames,
-            @NonNull Configuration configuration,
-            @NonNull File classesFolder,
-            @NonNull File javaResourceFolder,
-            @Nullable SourceProvider sourceProvider) {
-        extraModelInfo.registerJavaArtifact(name, variant, assembleTaskName,
-                javaCompileTaskName, generatedSourceFolders, ideSetupTaskNames,
-                configuration, classesFolder, javaResourceFolder, sourceProvider)
-    }
-
-    public void registerMultiFlavorSourceProvider(
-            @NonNull String name,
-            @NonNull String flavorName,
-            @NonNull SourceProvider sourceProvider) {
-        extraModelInfo.registerMultiFlavorSourceProvider(name, flavorName, sourceProvider)
-    }
-
-    @NonNull
-    public SourceProvider wrapJavaSourceSet(@NonNull SourceSet sourceSet) {
-        return new SourceSetSourceProviderWrapper(sourceSet)
-    }
-
-    /**
-     * <strong>Required.</strong> Compile SDK version.
-     *
-     * <p>Setter can be called with a string like "android-21" or a number.
-     *
-     * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
-     * back may give a slightly different string.
-     */
-    public String getCompileSdkVersion() {
-        return target
-    }
-
-    public FullRevision getBuildToolsRevision() {
-        return buildToolsRevision
-    }
-
-    public File getSdkDirectory() {
-        return sdkHandler.getSdkFolder()
-    }
-
-    public File getNdkDirectory() {
-        return sdkHandler.getNdkFolder()
-    }
-
-    public List<File> getBootClasspath() {
-        return androidBuilder.getBootClasspath()
-    }
-
-    public File getAdbExe() {
-        return sdkHandler.getSdkInfo().adb
-    }
-
-    public File getDefaultProguardFile(String name) {
-        File sdkDir = sdkHandler.getAndCheckSdkFolder()
-        return new File(sdkDir,
-                SdkConstants.FD_TOOLS + File.separatorChar
-                        + SdkConstants.FD_PROGUARD + File.separatorChar
-                        + name);
-    }
-
-    // ---------------
-    // TEMP for compatibility
-
-    /**
-     * Whether to generate PNGs from vector drawables. This doesn't work yet, so it's disabled by
-     * default.
-     */
-    boolean preprocessResources = false;
-
-    // by default, we do not generate pure splits
-    boolean generatePureSplits = false;
-
-    void generatePureSplits(boolean flag) {
-        if (flag) {
-            logger.warn("Pure splits are not supported by PlayStore yet.")
-        }
-        this.generatePureSplits = flag;
-    }
-
-    private boolean enforceUniquePackageName = true
-
-    public void enforceUniquePackageName(boolean value) {
-        if (!value) {
-            LoggingUtil.displayDeprecationWarning(logger, project, "Support for libraries with same package name is deprecated and will be removed in a future release.")
-        }
-        enforceUniquePackageName = value
-    }
-
-    public void setEnforceUniquePackageName(boolean value) {
-        enforceUniquePackageName(value)
-    }
-
-    public getEnforceUniquePackageName() {
-        return enforceUniquePackageName
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
deleted file mode 100644
index 44d090b..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
+++ /dev/null
@@ -1,91 +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.build.gradle
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.api.LibraryVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * 'android' extension for 'com.android.library' project.
- */
-@CompileStatic
-public class LibraryExtension extends TestedExtension {
-
-    private final DefaultDomainObjectSet<LibraryVariant> libraryVariantList =
-        new DefaultDomainObjectSet<LibraryVariant>(LibraryVariant.class)
-
-    LibraryExtension(
-            @NonNull ProjectInternal project,
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull SdkHandler sdkHandler,
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
-            @NonNull ExtraModelInfo extraModelInfo,
-            boolean isLibrary) {
-        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
-                signingConfigs, extraModelInfo, isLibrary)
-    }
-
-    /**
-     * Returns the list of library variants. Since the collections is built after evaluation,
-     * it should be used with Gradle's <code>all</code> iterator to process future items.
-     */
-    public DefaultDomainObjectSet<LibraryVariant> getLibraryVariants() {
-        return libraryVariantList
-    }
-
-    @Override
-    void addVariant(BaseVariant variant) {
-        libraryVariantList.add((LibraryVariant) variant)
-    }
-
-    // ---------------
-    // TEMP for compatibility
-    // STOPSHIP Remove in 1.0
-
-    private boolean packageBuildConfig = true
-
-    public void packageBuildConfig(boolean value) {
-        if (!value) {
-            LoggingUtil.displayDeprecationWarning(logger, project, "Support for not packaging BuildConfig is deprecated and will be removed in 1.0")
-        }
-
-        packageBuildConfig = value
-    }
-
-    public void setPackageBuildConfig(boolean value) {
-        packageBuildConfig(value)
-    }
-
-    boolean getPackageBuildConfig() {
-        return packageBuildConfig
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy
index 4f834bd..9871cf7 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy
@@ -49,6 +49,7 @@
         mergeReportsTask.group = JavaBasePlugin.VERIFICATION_GROUP
         mergeReportsTask.description = "Merges all the Android test reports from the sub projects."
         mergeReportsTask.reportType = ReportType.MULTI_PROJECT
+        mergeReportsTask.setVariantName("")
 
         mergeReportsTask.conventionMapping.resultsDir = {
             String location = extension.resultsDir != null ?
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestAndroidConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestAndroidConfig.java
new file mode 100644
index 0000000..3ccd01c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestAndroidConfig.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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;
+
+/**
+ * User configuration settings for 'com.android.test' project.
+ */
+public interface TestAndroidConfig extends AndroidConfig {
+
+    /**
+     * Returns the Gradle path of the project that this test project tests.
+     */
+    String getTargetProjectPath();
+
+    /**
+     * Returns the variant of the tested project.
+     *
+     * Default is 'debug'
+     */
+    String getTargetVariant();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestExtension.groovy
deleted file mode 100644
index 729b471..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestExtension.groovy
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2015 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
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.api.ApplicationVariant
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * 'android' extension for 'com.android.test' project.
- */
-@CompileStatic
-public class TestExtension extends BaseExtension {
-
-    private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList =
-        new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class)
-
-    private String targetProjectPath = null
-    private String targetVariant = "debug"
-
-    TestExtension(
-            @NonNull ProjectInternal project,
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull SdkHandler sdkHandler,
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
-            @NonNull ExtraModelInfo extraModelInfo,
-            boolean isLibrary) {
-        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
-                signingConfigs, extraModelInfo, isLibrary)
-    }
-
-    /**
-     * Returns the list of Application variants. Since the collections is built after evaluation,
-     * it should be used with Gradle's <code>all</code> iterator to process future items.
-     */
-    public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
-        return applicationVariantList
-    }
-
-    @Override
-    void addVariant(BaseVariant variant) {
-        applicationVariantList.add((ApplicationVariant) variant)
-    }
-
-    /**
-     * Returns the Gradle path of the project that this test project tests.
-     */
-    String getTargetProjectPath() {
-        return targetProjectPath
-    }
-
-    void setTargetProjectPath(String targetProjectPath) {
-        checkWritability()
-        this.targetProjectPath = targetProjectPath
-    }
-
-    void targetProjectPath(String targetProjectPath) {
-        setTargetProjectPath(targetProjectPath)
-    }
-
-    /**
-     * Returns the variant of the tested project.
-     *
-     * Default is 'debug'
-     */
-    String getTargetVariant() {
-        return targetVariant
-    }
-
-    void setTargetVariant(String targetVariant) {
-        checkWritability()
-        this.targetVariant = targetVariant
-    }
-
-    void targetVariant(String targetVariant) {
-        setTargetVariant(targetVariant)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedAndroidConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedAndroidConfig.java
new file mode 100644
index 0000000..000c956
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedAndroidConfig.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.TestVariant;
+import com.android.build.gradle.api.UnitTestVariant;
+
+import org.gradle.api.DomainObjectSet;
+
+/**
+ * User configuration settings for android plugin with test component.
+ */
+public interface TestedAndroidConfig extends AndroidConfig {
+
+    /**
+     * Return the name of the BuildType for testing.
+     */
+    @NonNull
+    String getTestBuildType();
+
+    /**
+     * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+     * it should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    @NonNull
+    DomainObjectSet<TestVariant> getTestVariants();
+
+    /**
+     * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+     * it should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    @NonNull
+    DomainObjectSet<UnitTestVariant> getUnitTestVariants();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedExtension.groovy
deleted file mode 100644
index 5892e3d..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedExtension.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 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
-import com.android.annotations.NonNull
-import com.android.build.gradle.api.TestVariant
-import com.android.build.gradle.api.UnitTestVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.DomainObjectSet
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.core.VariantType.UNIT_TEST
-/**
- * base 'android' extension for plugins that have a test component.
- */
-@CompileStatic
-public abstract class TestedExtension extends BaseExtension {
-
-    private final DomainObjectSet<TestVariant> testVariantList =
-            new DefaultDomainObjectSet<TestVariant>(TestVariant.class)
-
-    private final DomainObjectSet<UnitTestVariant> unitTestVariantList =
-            new DefaultDomainObjectSet<UnitTestVariant>(UnitTestVariant.class)
-
-    String testBuildType = "debug"
-
-    TestedExtension(
-            @NonNull ProjectInternal project,
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull SdkHandler sdkHandler,
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
-            @NonNull ExtraModelInfo extraModelInfo,
-            boolean isLibrary) {
-        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
-                signingConfigs, extraModelInfo, isLibrary)
-
-        sourceSetsContainer.create(ANDROID_TEST.prefix)
-        sourceSetsContainer.create(UNIT_TEST.prefix)
-    }
-
-    /**
-     * Returns the list of (Android) test variants. Since the collections is built after evaluation,
-     * it should be used with Gradle's <code>all</code> iterator to process future items.
-     */
-    @NonNull
-    public DomainObjectSet<TestVariant> getTestVariants() {
-        return testVariantList
-    }
-
-    void addTestVariant(TestVariant testVariant) {
-        testVariantList.add(testVariant)
-    }
-
-    /**
-     * Returns the list of (Android) test variants. Since the collections is built after evaluation,
-     * it should be used with Gradle's <code>all</code> iterator to process future items.
-     */
-    @NonNull
-    public DomainObjectSet<UnitTestVariant> getUnitTestVariants() {
-        return unitTestVariantList
-    }
-
-    void addUnitTestVariant(UnitTestVariant testVariant) {
-        unitTestVariantList.add(testVariant)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java
index b6bc97a..76f161a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java
@@ -35,7 +35,7 @@
     File getSrcFile();
 
     /**
-     * Sets the location of the file.
+     * Sets the location of the file. Returns this object.
      *
      * @param srcPath The source directory. This is evaluated as for
      *                {@link org.gradle.api.Project#file(Object)}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.groovy
deleted file mode 100644
index 0e00ccc..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.groovy
+++ /dev/null
@@ -1,252 +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.build.gradle.api
-import com.android.annotations.NonNull
-/**
- * A {@code AndroidSourceSet} represents a logical group of Java, aidl, renderscript source
- * as well as Android and non-Android resources.
- */
-public interface AndroidSourceSet {
-
-    /**
-     * Returns the name of this source set.
-     *
-     * @return The name. Never returns null.
-     */
-    @NonNull
-    String getName();
-
-    /**
-     * Returns the Java resources which are to be copied into the javaResources output directory.
-     *
-     * @return the java resources. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getResources();
-
-    /**
-     * Configures the Java resources for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
-     * contains the java resources.
-     *
-     * @param configureClosure The closure to use to configure the javaResources.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet resources(Closure configureClosure);
-
-    /**
-     * Returns the Java source which is to be compiled by the Java compiler into the class output
-     * directory.
-     *
-     * @return the Java source. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getJava();
-
-    /**
-     * Configures the Java source for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
-     * contains the Java source.
-     *
-     * @param configureClosure The closure to use to configure the Java source.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet java(Closure configureClosure);
-
-    /**
-     * Returns the name of the compile configuration for this source set.
-     * @return The configuration name
-     */
-    @NonNull
-    String getCompileConfigurationName();
-
-    /**
-     * Returns the name of the runtime configuration for this source set.
-     * @return The runtime configuration name
-     */
-    @NonNull
-    String getPackageConfigurationName();
-
-    /**
-     * Returns the name of the compiled-only configuration for this source set.
-     * @return The provided configuration name
-     */
-    @NonNull
-    String getProvidedConfigurationName();
-
-    /**
-     * Returns the name of the wearApp configuration for this source set.
-     * @return The configuration name
-     */
-    @NonNull
-    String getWearAppConfigurationName();
-
-    /**
-     * The Android Manifest file for this source set.
-     *
-     * @return the manifest. Never returns null.
-     */
-    @NonNull
-    AndroidSourceFile getManifest();
-
-    /**
-     * Configures the location of the Android Manifest for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceFile} which contains the
-     * manifest.
-     *
-     * @param configureClosure The closure to use to configure the Android Manifest.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet manifest(Closure configureClosure);
-
-    /**
-     * The Android Resources directory for this source set.
-     *
-     * @return the resources. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getRes();
-
-    /**
-     * Configures the location of the Android Resources for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
-     * which contains the resources.
-     *
-     * @param configureClosure The closure to use to configure the Resources.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet res(Closure configureClosure);
-
-    /**
-     * The Android Assets directory for this source set.
-     *
-     * @return the assets. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getAssets();
-
-    /**
-     * Configures the location of the Android Assets for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
-     * which contains the assets.
-     *
-     * @param configureClosure The closure to use to configure the Assets.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet assets(Closure configureClosure);
-
-    /**
-     * The Android AIDL source directory for this source set.
-     *
-     * @return the source. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getAidl();
-
-    /**
-     * Configures the location of the Android AIDL source for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
-     * which contains the AIDL source.
-     *
-     * @param configureClosure The closure to use to configure the AIDL source.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet aidl(Closure configureClosure);
-
-    /**
-     * The Android Renderscript source directory for this source set.
-     *
-     * @return the source. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getRenderscript();
-
-    /**
-     * Configures the location of the Android Renderscript source for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
-     * which contains the Renderscript source.
-     *
-     * @param configureClosure The closure to use to configure the Renderscript source.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet renderscript(Closure configureClosure);
-
-    /**
-     * The Android JNI source directory for this source set.
-     *
-     * @return the source. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getJni();
-
-    /**
-     * Configures the location of the Android JNI source for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
-     * which contains the JNI source.
-     *
-     * @param configureClosure The closure to use to configure the JNI source.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet jni(Closure configureClosure);
-
-    /**
-     * The Android JNI libs directory for this source set.
-     *
-     * @return the libs. Never returns null.
-     */
-    @NonNull
-    AndroidSourceDirectorySet getJniLibs();
-
-    /**
-     * Configures the location of the Android JNI libs for this set.
-     *
-     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
-     * which contains the JNI libs.
-     *
-     * @param configureClosure The closure to use to configure the JNI libs.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet jniLibs(Closure configureClosure);
-
-    /**
-     * Sets the root of the source sets to a given path.
-     *
-     * All entries of the source set are located under this root directory.
-     *
-     * @param path the root directory.
-     * @return this
-     */
-    @NonNull
-    AndroidSourceSet setRoot(String path);
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.java
new file mode 100644
index 0000000..26079e3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.java
@@ -0,0 +1,255 @@
+/*
+ * 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.api;
+import com.android.annotations.NonNull;
+
+import groovy.lang.Closure;
+
+/**
+ * An AndroidSourceSet represents a logical group of Java, aidl and RenderScript sources
+ * as well as Android and non-Android (Java-style) resources.
+ */
+public interface AndroidSourceSet {
+
+    /**
+     * Returns the name of this source set.
+     *
+     * @return The name. Never returns null.
+     */
+    @NonNull
+    String getName();
+
+    /**
+     * Returns the Java resources which are to be copied into the javaResources output directory.
+     *
+     * @return the java resources. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getResources();
+
+    /**
+     * Configures the Java resources for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
+     * contains the java resources.
+     *
+     * @param configureClosure The closure to use to configure the javaResources.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet resources(Closure configureClosure);
+
+    /**
+     * Returns the Java source which is to be compiled by the Java compiler into the class output
+     * directory.
+     *
+     * @return the Java source. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getJava();
+
+    /**
+     * Configures the Java source for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
+     * contains the Java source.
+     *
+     * @param configureClosure The closure to use to configure the Java source.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet java(Closure configureClosure);
+
+    /**
+     * Returns the name of the compile configuration for this source set.
+     * @return The configuration name
+     */
+    @NonNull
+    String getCompileConfigurationName();
+
+    /**
+     * Returns the name of the runtime configuration for this source set.
+     * @return The runtime configuration name
+     */
+    @NonNull
+    String getPackageConfigurationName();
+
+    /**
+     * Returns the name of the compiled-only configuration for this source set.
+     * @return The provided configuration name
+     */
+    @NonNull
+    String getProvidedConfigurationName();
+
+    /**
+     * Returns the name of the wearApp configuration for this source set.
+     * @return The configuration name
+     */
+    @NonNull
+    String getWearAppConfigurationName();
+
+    /**
+     * The Android Manifest file for this source set.
+     *
+     * @return the manifest. Never returns null.
+     */
+    @NonNull
+    AndroidSourceFile getManifest();
+
+    /**
+     * Configures the location of the Android Manifest for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceFile} which contains the
+     * manifest.
+     *
+     * @param configureClosure The closure to use to configure the Android Manifest.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet manifest(Closure configureClosure);
+
+    /**
+     * The Android Resources directory for this source set.
+     *
+     * @return the resources. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getRes();
+
+    /**
+     * Configures the location of the Android Resources for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+     * which contains the resources.
+     *
+     * @param configureClosure The closure to use to configure the Resources.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet res(Closure configureClosure);
+
+    /**
+     * The Android Assets directory for this source set.
+     *
+     * @return the assets. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getAssets();
+
+    /**
+     * Configures the location of the Android Assets for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+     * which contains the assets.
+     *
+     * @param configureClosure The closure to use to configure the Assets.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet assets(Closure configureClosure);
+
+    /**
+     * The Android AIDL source directory for this source set.
+     *
+     * @return the source. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getAidl();
+
+    /**
+     * Configures the location of the Android AIDL source for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+     * which contains the AIDL source.
+     *
+     * @param configureClosure The closure to use to configure the AIDL source.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet aidl(Closure configureClosure);
+
+    /**
+     * The Android RenderScript source directory for this source set.
+     *
+     * @return the source. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getRenderscript();
+
+    /**
+     * Configures the location of the Android RenderScript source for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+     * which contains the Renderscript source.
+     *
+     * @param configureClosure The closure to use to configure the Renderscript source.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet renderscript(Closure configureClosure);
+
+    /**
+     * The Android JNI source directory for this source set.
+     *
+     * @return the source. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getJni();
+
+    /**
+     * Configures the location of the Android JNI source for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+     * which contains the JNI source.
+     *
+     * @param configureClosure The closure to use to configure the JNI source.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet jni(Closure configureClosure);
+
+    /**
+     * The Android JNI libs directory for this source set.
+     *
+     * @return the libs. Never returns null.
+     */
+    @NonNull
+    AndroidSourceDirectorySet getJniLibs();
+
+    /**
+     * Configures the location of the Android JNI libs for this set.
+     *
+     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+     * which contains the JNI libs.
+     *
+     * @param configureClosure The closure to use to configure the JNI libs.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet jniLibs(Closure configureClosure);
+
+    /**
+     * Sets the root of the source sets to a given path.
+     *
+     * All entries of the source set are located under this root directory.
+     *
+     * @param path the root directory.
+     * @return this
+     */
+    @NonNull
+    AndroidSourceSet setRoot(String path);
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
index d61fa28..786c395 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
@@ -29,7 +29,7 @@
 import com.android.builder.model.SourceProvider;
 
 import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.AbstractCopyTask;
 import org.gradle.api.tasks.compile.AbstractCompile;
 import org.gradle.api.tasks.compile.JavaCompile;
 
@@ -108,7 +108,7 @@
      * This is always non-null but could be empty.
      */
     @NonNull
-    List<GroupableProductFlavor> getProductFlavors();
+    List<ProductFlavor> getProductFlavors();
 
     /**
      * Returns a list of sorted SourceProvider in order of ascending order, meaning, the earlier
@@ -205,7 +205,7 @@
      * Returns the Java resource processing task.
      */
     @NonNull
-    Copy getProcessJavaResources();
+    AbstractCopyTask getProcessJavaResources();
 
     /**
      * Returns the assemble task for all this variant's output
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/GroupableProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/GroupableProductFlavor.java
deleted file mode 100644
index f8c9b82..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/GroupableProductFlavor.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2014 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.api;
-
-import com.android.annotations.Nullable;
-import com.android.builder.model.ProductFlavor;
-
-/**
- * A product flavor that is associated with a flavor dimension.
- */
-@Deprecated
-public interface GroupableProductFlavor extends ProductFlavor {
-
-    // TODO: Remove interface now ProductFlavor has dimension.
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java
index a28a6e6..c5aa6ef 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java
@@ -50,5 +50,5 @@
      * Returns the list of flavors, or an empty list.
      */
     @NonNull
-    List<GroupableProductFlavor> getFlavors();
+    List<ProductFlavor> getFlavors();
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.groovy
deleted file mode 100644
index 6f43460..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.groovy
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2015 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.build.gradle.BaseExtension
-import com.android.build.gradle.internal.profile.SpanRecorders
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.variant.ApplicationVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.Recorder
-import com.android.builder.profile.ThreadRecorder
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-/**
- * TaskManager for creating tasks in an Android application project.
- */
-class ApplicationTaskManager extends TaskManager {
-    public ApplicationTaskManager (
-            Project project,
-            AndroidBuilder androidBuilder,
-            BaseExtension extension,
-            SdkHandler sdkHandler,
-            DependencyManager dependencyManager,
-            ToolingModelBuilderRegistry toolingRegistry) {
-        super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry)
-    }
-
-    @Override
-    public void  createTasksForVariantData(
-            @NonNull TaskFactory tasks,
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
-        assert variantData instanceof ApplicationVariantData;
-        ApplicationVariantData appVariantData = (ApplicationVariantData) variantData;
-
-        VariantScope variantScope = variantData.getScope()
-
-        createAnchorTasks(tasks, variantScope);
-        createCheckManifestTask(tasks, variantScope);
-
-        handleMicroApp(tasks, variantScope);
-
-        // Add a task to process the manifest(s)
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK) {
-            createMergeAppManifestsTask(tasks, variantScope)
-        }
-
-        // Add a task to create the res values
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK) {
-            createGenerateResValuesTask(tasks, variantScope);
-        }
-
-        // Add a task to compile renderscript files.
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK) {
-            createRenderscriptTask(tasks, variantScope);
-        }
-
-        // Add a task to merge the resource folders
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK) {
-            createMergeResourcesTask(tasks, variantScope);
-        }
-
-        // Add a task to merge the asset folders
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK) {
-            createMergeAssetsTask(tasks, variantScope);
-        }
-
-        // Add a task to create the BuildConfig class
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK) {
-            createBuildConfigTask(tasks, variantScope);
-        }
-
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_PREPROCESS_RESOURCES_TASK) {
-            createPreprocessResourcesTask(tasks, variantScope)
-        }
-
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_PROCESS_RES_TASK) {
-            // Add a task to process the Android Resources and generate source files
-            createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/);
-
-            // Add a task to process the java resources
-            createProcessJavaResTask(tasks, variantScope);
-        }
-
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_AIDL_TASK) {
-            createAidlTask(tasks, variantScope);
-        }
-
-        // Add a compile task
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK) {
-            if (variantData.getVariantConfiguration().getUseJack()) {
-                createJackTask(appVariantData, null /*testedVariant*/);
-            } else {
-                createJavaCompileTask(tasks, variantScope);
-                createJarTask(tasks, variantScope);
-                createPostCompilationTasks(tasks, variantScope);
-            }
-        }
-
-        // Add NDK tasks
-        if (isNdkTaskNeeded) {
-            SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_NDK_TASK) {
-                createNdkTasks(variantData);
-            }
-        }
-        variantScope.setNdkBuildable(getNdkBuildable(variantData))
-        variantScope.setNdkOutputDirectories(getNdkOutputDirectories(variantData))
-
-        if (variantData.getSplitHandlingPolicy() ==
-                BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
-            if (extension.getBuildToolsRevision().getMajor() < 21) {
-                throw new RuntimeException("Pure splits can only be used with buildtools 21 and later")
-            }
-            SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_SPLIT_TASK) {
-                createSplitResourcesTasks(variantScope);
-                createSplitAbiTasks(variantScope);
-            }
-        }
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_PACKAGING_TASK) {
-            createPackagingTask(tasks, variantScope, true /*publishApk*/);
-        }
-
-        // create the lint tasks.
-        SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_LINT_TASK) {
-            createLintTasks(tasks, variantScope);
-        }
-    }
-
-    /**
-     * Configure variantData to generate embedded wear application.
-     */
-    private void handleMicroApp(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        if (variantData.getVariantConfiguration().getBuildType().isEmbedMicroApp()) {
-            // get all possible configurations for the variant. We'll take the highest priority
-            // of them that have a file.
-            List<String> wearConfigNames = variantData.getWearConfigNames();
-
-            for (String configName : wearConfigNames) {
-                Configuration config = project.getConfigurations().findByName(
-                        configName);
-                // this shouldn't happen, but better safe.
-                if (config == null) {
-                    continue;
-                }
-
-                Set<File> file = config.getFiles();
-
-                int count = file.size();
-                if (count == 1) {
-                    createGenerateMicroApkDataTask(tasks, scope, config);
-                    // found one, bail out.
-                    return;
-                } else if (count > 1) {
-                    throw new RuntimeException(String.format(
-                            "Configuration '%s' resolves to more than one apk.", configName));
-                }
-            }
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
new file mode 100644
index 0000000..649c43e
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2015 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.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.ApplicationVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * TaskManager for creating tasks in an Android application project.
+ */
+public class ApplicationTaskManager extends TaskManager {
+
+    public ApplicationTaskManager(
+            Project project,
+            AndroidBuilder androidBuilder,
+            AndroidConfig extension,
+            SdkHandler sdkHandler,
+            DependencyManager dependencyManager,
+            ToolingModelBuilderRegistry toolingRegistry) {
+        super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry);
+    }
+
+    @Override
+    public void createTasksForVariantData(
+            @NonNull final TaskFactory tasks,
+            @NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+        assert variantData instanceof ApplicationVariantData;
+        final ApplicationVariantData appVariantData = (ApplicationVariantData) variantData;
+
+        final VariantScope variantScope = variantData.getScope();
+
+        createAnchorTasks(tasks, variantScope);
+        createCheckManifestTask(tasks, variantScope);
+
+        handleMicroApp(tasks, variantScope);
+
+        // Add a task to process the manifest(s)
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createMergeAppManifestsTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to create the res values
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createGenerateResValuesTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to compile renderscript files.
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createRenderscriptTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to merge the resource folders
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createMergeResourcesTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to merge the asset folders
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createMergeAssetsTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to create the BuildConfig class
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createBuildConfigTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_PROCESS_RES_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        // Add a task to process the Android Resources and generate source files
+                        createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/);
+
+                        // Add a task to process the java resources
+                        createProcessJavaResTasks(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_AIDL_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createAidlTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a compile task
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+
+                        if (variantData.getVariantConfiguration().getUseJack()) {
+                            createJackTask(tasks, variantScope);
+                        } else {
+                            setJavaCompilerTask(javacTask, tasks, variantScope);
+                            createJarTask(tasks, variantScope);
+                            createPostCompilationTasks(tasks, variantScope);
+                        }
+                        return null;
+                    }
+                });
+
+        // Add NDK tasks
+        if (isNdkTaskNeeded) {
+            ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_NDK_TASK,
+                    new Recorder.Block<Void>() {
+                        @Override
+                        public Void call() {
+                            createNdkTasks(variantScope);
+                            return null;
+                        }
+                    });
+        } else {
+            if (variantData.compileTask != null) {
+                variantData.compileTask.dependsOn(getNdkBuildable(variantData));
+            } else {
+                variantScope.getCompileTask().dependsOn(tasks, getNdkBuildable(variantData));
+            }
+        }
+        variantScope.setNdkBuildable(getNdkBuildable(variantData));
+
+        if (variantData.getSplitHandlingPolicy().equals(
+                BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY)) {
+            if (getExtension().getBuildToolsRevision().getMajor() < 21) {
+                throw new RuntimeException("Pure splits can only be used with buildtools 21 and later");
+            }
+
+            ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_SPLIT_TASK,
+                    new Recorder.Block<Void>() {
+                        @Override
+                        public Void call() {
+                            createSplitResourcesTasks(variantScope);
+                            createSplitAbiTasks(variantScope);
+                            return null;
+                        }
+                    });
+        }
+
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_PACKAGING_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createPackagingTask(tasks, variantScope, true /*publishApk*/);
+                        return null;
+                    }
+                });
+
+        // create the lint tasks.
+        ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_LINT_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createLintTasks(tasks, variantScope);
+                        return null;
+                    }
+                });
+    }
+
+    /**
+     * Configure variantData to generate embedded wear application.
+     */
+    private void handleMicroApp(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        if (variantData.getVariantConfiguration().getBuildType().isEmbedMicroApp()) {
+            // get all possible configurations for the variant. We'll take the highest priority
+            // of them that have a file.
+            List<String> wearConfigNames = variantData.getWearConfigNames();
+
+            for (String configName : wearConfigNames) {
+                Configuration config = project.getConfigurations().findByName(
+                        configName);
+                // this shouldn't happen, but better safe.
+                if (config == null) {
+                    continue;
+                }
+
+                Set<File> file = config.getFiles();
+
+                int count = file.size();
+                if (count == 1) {
+                    createGenerateMicroApkDataTask(tasks, scope, config);
+                    // found one, bail out.
+                    return;
+                } else if (count > 1) {
+                    throw new RuntimeException(String.format(
+                            "Configuration '%s' resolves to more than one apk.", configName));
+                }
+            }
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy
deleted file mode 100644
index 106392f..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy
+++ /dev/null
@@ -1,43 +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.build.gradle.internal
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.api.DefaultAndroidSourceSet
-import com.android.build.gradle.internal.dsl.BuildType
-import org.gradle.api.Project
-import org.gradle.api.Task
-/**
- * Class containing a BuildType and associated data (Sourceset for instance).
- */
-class BuildTypeData extends VariantDimensionData {
-    final BuildType buildType
-    final Task assembleTask
-
-    BuildTypeData(
-            @NonNull  BuildType buildType,
-            @NonNull  Project project,
-            @NonNull  DefaultAndroidSourceSet sourceSet,
-            @Nullable DefaultAndroidSourceSet unitTestSourceSet) {
-        super(sourceSet, null, unitTestSourceSet, project)
-
-        this.buildType = buildType
-
-        assembleTask = project.tasks.create("assemble${buildType.name.capitalize()}")
-        assembleTask.description = "Assembles all ${buildType.name.capitalize()} builds."
-        assembleTask.group = "Build"
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.java
new file mode 100644
index 0000000..fa0279f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.java
@@ -0,0 +1,57 @@
+/*
+ * 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.internal;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.utils.StringHelper;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.plugins.BasePlugin;
+
+/**
+ * Class containing a BuildType and associated data (Sourceset for instance).
+ */
+public class BuildTypeData extends VariantDimensionData {
+    private final CoreBuildType buildType;
+    private final Task assembleTask;
+
+    BuildTypeData(
+            @NonNull  CoreBuildType buildType,
+            @NonNull  Project project,
+            @NonNull  DefaultAndroidSourceSet sourceSet,
+            @Nullable DefaultAndroidSourceSet unitTestSourceSet) {
+        super(sourceSet, null, unitTestSourceSet, project);
+
+        this.buildType = buildType;
+
+        String sourceSetName = StringHelper.capitalize(buildType.getName());
+
+        assembleTask = project.getTasks().create("assemble" + sourceSetName);
+        assembleTask.setDescription("Assembles all " + sourceSetName + " builds.");
+        assembleTask.setGroup(BasePlugin.BUILD_GROUP);
+    }
+
+    public CoreBuildType getBuildType() {
+        return buildType;
+    }
+
+    public Task getAssembleTask() {
+        return assembleTask;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
deleted file mode 100644
index a9b7fae..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
+++ /dev/null
@@ -1,75 +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
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.google.common.base.Charsets
-import groovy.transform.CompileStatic
-import org.gradle.api.JavaVersion
-
-/**
- * Compilation options.
- */
-@CompileStatic
-class CompileOptions {
-    @Nullable
-    private JavaVersion sourceCompatibility
-
-    @Nullable
-    private JavaVersion targetCompatibility
-
-    String encoding = Charsets.UTF_8.name()
-
-    /**
-     * Default Java version that will be used if the source and target compatibility levels will
-     * not be set explicitly.
-     */
-    JavaVersion defaultJavaVersion = JavaVersion.VERSION_1_6
-
-    boolean ndkCygwinMode = false
-
-    void setSourceCompatibility(@NonNull JavaVersion sourceCompatibility) {
-        this.sourceCompatibility = sourceCompatibility
-    }
-
-    /**
-     * Language level of the source code.
-     *
-     * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
-     * Gradle Java plugin</a> uses.
-     */
-    @NonNull
-    JavaVersion getSourceCompatibility() {
-        sourceCompatibility?: defaultJavaVersion
-    }
-
-    void setTargetCompatibility(@NonNull JavaVersion targetCompatibility) {
-        this.targetCompatibility = targetCompatibility
-    }
-
-    /**
-     * Version of the generated Java bytecode.
-     *
-     * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
-     * Gradle Java plugin</a> uses.
-     */
-    @NonNull
-    JavaVersion getTargetCompatibility() {
-        targetCompatibility?: defaultJavaVersion
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.java
new file mode 100644
index 0000000..e8e2403
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.java
@@ -0,0 +1,129 @@
+/*
+ * 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.annotations.Nullable;
+import com.google.common.base.Charsets;
+import org.gradle.api.JavaVersion;
+
+import java.util.Locale;
+
+/**
+ * Compilation options.
+ */
+public class CompileOptions {
+    @Nullable
+    private JavaVersion sourceCompatibility;
+
+    @Nullable
+    private JavaVersion targetCompatibility;
+
+    private String encoding = Charsets.UTF_8.name();
+
+    /**
+     * Default Java version that will be used if the source and target compatibility levels will
+     * not be set explicitly.
+     */
+    private JavaVersion defaultJavaVersion = JavaVersion.VERSION_1_6;
+
+    private boolean ndkCygwinMode = false;
+
+    /**
+     * Language level of the source code.
+     *
+     * <p>Formats supported are :
+     *      "1.6"
+     *      1.6
+     *      JavaVersion.Version_1_6
+     *      "Version_1_6"
+     */
+    public void setSourceCompatibility(@NonNull Object sourceCompatibility) {
+        this.sourceCompatibility = convert(sourceCompatibility);
+    }
+
+    /**
+     * Language level of the source code.
+     *
+     * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
+     * Gradle Java plugin</a> uses.
+     */
+    @NonNull
+    public JavaVersion getSourceCompatibility() {
+        return sourceCompatibility != null ? sourceCompatibility : defaultJavaVersion;
+    }
+
+    /**
+     * Language level of the target code.
+     *
+     * <p>Formats supported are :
+     *      "1.6"
+     *      1.6
+     *      JavaVersion.Version_1_6
+     *      "Version_1_6"
+     */
+    public void setTargetCompatibility(@NonNull Object targetCompatibility) {
+        this.targetCompatibility = convert(targetCompatibility);
+    }
+
+    /**
+     * Version of the generated Java bytecode.
+     *
+     * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
+     * Gradle Java plugin</a> uses.
+     */
+    @NonNull
+    public JavaVersion getTargetCompatibility() {
+        return targetCompatibility != null ? targetCompatibility : defaultJavaVersion;
+    }
+
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    public String getEncoding() {
+        return encoding;
+    }
+
+    public void setDefaultJavaVersion(JavaVersion defaultJavaVersion) {
+        this.defaultJavaVersion = defaultJavaVersion;
+    }
+
+    public JavaVersion getDefaultJavaVersion() {
+        return defaultJavaVersion;
+    }
+
+
+    private static final String VERSION_PREFIX = "VERSION_";
+    /**
+     * Convert all possible supported way of specifying a Java version to {@link JavaVersion}
+     * @param version the user provided java version.
+     * @return {@link JavaVersion}
+     * @throws RuntimeException if it cannot be converted.
+     */
+    @NonNull
+    private static JavaVersion convert(@NonNull Object version) {
+        // for backward version reasons, we support setting strings like 'Version_1_6'
+        if (version instanceof String) {
+            final String versionString = (String) version;
+            if (versionString.toUpperCase(Locale.ENGLISH).startsWith(VERSION_PREFIX)) {
+                version = versionString.substring(VERSION_PREFIX.length()).replace('_', '.');
+            }
+        }
+        return JavaVersion.toVersion(version);
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java
index 834a880..c8c6df9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java
@@ -16,6 +16,13 @@
 
 package com.android.build.gradle.internal;
 
+import static com.android.SdkConstants.DOT_JAR;
+import static com.android.SdkConstants.EXT_ANDROID_PACKAGE;
+import static com.android.SdkConstants.EXT_JAR;
+import static com.android.builder.core.BuilderConstants.EXT_LIB_ARCHIVE;
+import static com.android.builder.core.ErrorReporter.EvaluationMode.STANDARD;
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.build.gradle.internal.dependency.JarInfo;
@@ -34,12 +41,14 @@
 import com.android.builder.model.MavenCoordinates;
 import com.android.builder.model.SyncIssue;
 import com.android.utils.ILogger;
+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.collect.Sets;
 
+import org.gradle.api.CircularReferenceException;
 import org.gradle.api.Project;
 import org.gradle.api.Task;
 import org.gradle.api.UnknownProjectException;
@@ -61,15 +70,9 @@
 import org.gradle.api.specs.Specs;
 import org.gradle.util.GUtil;
 
-import static com.android.SdkConstants.DOT_JAR;
-import static com.android.SdkConstants.EXT_ANDROID_PACKAGE;
-import static com.android.SdkConstants.EXT_JAR;
-import static com.android.build.gradle.internal.ExtraModelInfo.ModelQueryMode.STANDARD;
-import static com.android.builder.core.BuilderConstants.EXT_LIB_ARCHIVE;
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
-
 import java.io.File;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -128,10 +131,11 @@
 
     public void resolveDependencies(
             @NonNull VariantDependencies variantDeps,
-            @Nullable VariantDependencies testedVariantDeps) {
+            @Nullable VariantDependencies testedVariantDeps,
+            @Nullable String testedProjectPath) {
         Multimap<LibraryDependency, VariantDependencies> reverseMap = ArrayListMultimap.create();
 
-        resolveDependencyForConfig(variantDeps, testedVariantDeps, reverseMap);
+        resolveDependencyForConfig(variantDeps, testedVariantDeps, testedProjectPath, reverseMap);
         processLibraries(variantDeps.getLibraries(), reverseMap);
     }
 
@@ -214,6 +218,7 @@
             prepareLibraryTask.setDescription("Prepare " + library.getName());
             prepareLibraryTask.setBundle(library.getBundle());
             prepareLibraryTask.setExplodedDir(library.getBundleFolder());
+            prepareLibraryTask.setVariantName("");
 
             prepareTaskMap.put(key, prepareLibraryTask);
         }
@@ -224,6 +229,7 @@
     private void resolveDependencyForConfig(
             @NonNull VariantDependencies variantDeps,
             @Nullable VariantDependencies testedVariantDeps,
+            @Nullable String testedProjectPath,
             @NonNull Multimap<LibraryDependency, VariantDependencies> reverseMap) {
 
         Configuration compileClasspath = variantDeps.getCompileConfiguration();
@@ -276,6 +282,8 @@
                         artifacts,
                         reverseMap,
                         currentUnresolvedDependencies,
+                        testedProjectPath,
+                        Collections.<String>emptyList(),
                         0);
             } else if (dependencyResult instanceof UnresolvedDependencyResult) {
                 ComponentSelector attempted = ((UnresolvedDependencyResult) dependencyResult).getAttempted();
@@ -304,6 +312,8 @@
                         artifacts,
                         reverseMap,
                         currentUnresolvedDependencies,
+                        testedProjectPath,
+                        Collections.<String>emptyList(),
                         0);
             } else if (dependencyResult instanceof UnresolvedDependencyResult) {
                 ComponentSelector attempted = ((UnresolvedDependencyResult) dependencyResult)
@@ -315,22 +325,35 @@
         }
 
         // now look through both results.
-        // 1. All Android libraries must be in both lists.
-        // since we reuse the same instance of LibInfo for identical modules
+        // 1. Handle the compile and package list of Libraries.
+        // For Libraries:
+        // Only library projects can support provided aar.
+        // However, package(publish)-only are still not supported (they don't make sense).
+        // For now, provided only dependencies will be kept normally in the compile-graph.
+        // However we'll want to not include them in the resource merging.
+        // For Applications:
+        // All Android libraries must be in both lists.
+        // ---
+        // Since we reuse the same instance of LibInfo for identical modules
         // we can simply run through each list and look for libs that are in only one.
         // While the list of library is actually a graph, it's fine to look only at the
-        // top level ones since they transitive ones are in the same scope as the direct libraries.
+        // top level ones since the transitive ones are in the same scope as the direct libraries.
         List<LibInfo> copyOfPackagedLibs = Lists.newArrayList(packagedAndroidLibraries);
+        boolean isLibrary = extraModelInfo.isLibrary();
 
         for (LibInfo lib : compiledAndroidLibraries) {
             if (!copyOfPackagedLibs.contains(lib)) {
-                //noinspection ConstantConditions
-                variantDeps.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
-                        lib.getResolvedCoordinates().toString(),
-                        SyncIssue.TYPE_NON_JAR_PROVIDED_DEP,
-                        String.format(
-                                "Project %s: provided dependencies can only be jars. %s is an Android Library.",
-                                project.getName(), lib.getResolvedCoordinates())));
+                if (isLibrary || lib.isOptional()) {
+                    lib.setIsOptional(true);
+                } else {
+                    //noinspection ConstantConditions
+                    variantDeps.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
+                            lib.getResolvedCoordinates().toString(),
+                            SyncIssue.TYPE_NON_JAR_PROVIDED_DEP,
+                            String.format(
+                                    "Project %s: provided dependencies can only be jars. %s is an Android Library.",
+                                    project.getName(), lib.getResolvedCoordinates())));
+                }
             } else {
                 copyOfPackagedLibs.remove(lib);
             }
@@ -359,7 +382,7 @@
         gatherJarDependencies(jarInfoSet, packagedJars, false /*compiled*/, true /*packaged*/);
         // at this step, we know that libraries have been checked and libraries can only
         // be in both compiled and packaged scope.
-        gatherJarDependenciesFromLibraries(jarInfoSet, compiledAndroidLibraries, true, true);
+        gatherJarDependenciesFromLibraries(jarInfoSet, compiledAndroidLibraries);
 
         // the final list of JarDependency, created from the list of JarInfo.
         List<JarDependency> jars = Lists.newArrayListWithCapacity(jarInfoSet.size());
@@ -376,9 +399,11 @@
 
             for (JarDependency jar : jarDependencies) {
                 if (jar.isPackaged()) {
-                    MavenCoordinates coord = jar.getResolvedCoordinates();
+                    MavenCoordinates coordinates = jar.getResolvedCoordinates();
                     //noinspection ConstantConditions
-                    testedDeps.put(computeVersionLessCoordinateKey(coord), coord.getVersion());
+                    testedDeps.put(
+                            computeVersionLessCoordinateKey(coordinates),
+                            coordinates.getVersion());
                 }
             }
 
@@ -387,28 +412,32 @@
             // to the final immutable instance
             for (JarInfo jar : jarInfoSet) {
                 if (jar.isPackaged()) {
-                    MavenCoordinates coord = jar.getResolvedCoordinates();
+                    MavenCoordinates coordinates = jar.getResolvedCoordinates();
 
-                    String testedVersion = testedDeps.get(computeVersionLessCoordinateKey(coord));
+                    String testedVersion = testedDeps.get(
+                            computeVersionLessCoordinateKey(coordinates));
                     if (testedVersion != null) {
                         // same artifact, skip packaging of the dependency in the test app,
                         // whether the version is a match or not.
 
                         // if the dependency is present in both tested and test artifact,
                         // verify that they are the same version
-                        if (!testedVersion.equals(coord.getVersion())) {
-                            String artifactInfo =  coord.getGroupId() + ":" + coord.getArtifactId();
+                        if (!testedVersion.equals(coordinates.getVersion())) {
+                            String artifactInfo =  coordinates.getGroupId() + ":" + coordinates.getArtifactId();
                             variantDeps.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
                                     artifactInfo,
                                     SyncIssue.TYPE_MISMATCH_DEP,
                                     String.format(
                                             "Conflict with dependency '%s'. Resolved versions for app (%s) and test app (%s) differ.",
-                                            artifactInfo, testedVersion, coord.getVersion())));
+                                            artifactInfo,
+                                            testedVersion,
+                                            coordinates.getVersion())));
 
                         } else {
                             logger.info(String.format(
                                     "Removed '%s' from packaging of %s: Already in tested package.",
-                                    coord, variantDeps.getName()));
+                                    coordinates,
+                                    variantDeps.getName()));
                         }
                     } else {
                         // new artifact, convert it.
@@ -498,7 +527,7 @@
             }
         }
 
-        if (extraModelInfo.getModelQueryMode() != STANDARD &&
+        if (extraModelInfo.getMode() != STANDARD &&
                 compileClasspath.getResolvedConfiguration().hasError()) {
             for (String dependency : currentUnresolvedDependencies) {
                 extraModelInfo.handleSyncError(
@@ -581,7 +610,8 @@
                     libInfo.getProjectVariant(),
                     libInfo.getProject(),
                     libInfo.getRequestedCoordinates(),
-                    libInfo.getResolvedCoordinates());
+                    libInfo.getResolvedCoordinates(),
+                    libInfo.isOptional());
 
             // add it to the map
             convertedMap.put(libInfo, convertedLib);
@@ -620,18 +650,14 @@
 
     private static void gatherJarDependenciesFromLibraries(
             Set<JarInfo> outJarInfos,
-            Collection<LibInfo> inLibraryDependencies,
-            boolean compiled,
-            boolean packaged) {
+            Collection<LibInfo> inLibraryDependencies) {
         for (LibInfo libInfo : inLibraryDependencies) {
-            gatherJarDependencies(outJarInfos, libInfo.getJarDependencies(), compiled, packaged);
+            gatherJarDependencies(outJarInfos, libInfo.getJarDependencies(),
+                    true, !libInfo.isOptional());
 
-            //noinspection unchecked
             gatherJarDependenciesFromLibraries(
                     outJarInfos,
-                    (Collection<LibInfo>) (Collection<?>) libInfo.getDependencies(),
-                    compiled,
-                    packaged);
+                    libInfo.getLibInfoDependencies());
         }
     }
 
@@ -658,7 +684,7 @@
                     List<ResolvedArtifact>> artifacts) {
 
         Set<ResolvedArtifact> allArtifacts;
-        if (extraModelInfo.getModelQueryMode() != STANDARD) {
+        if (extraModelInfo.getMode() != STANDARD) {
             allArtifacts = configuration.getResolvedConfiguration().getLenientConfiguration().getArtifacts(
                     Specs.satisfyAll());
         } else {
@@ -689,15 +715,17 @@
     }
 
     private void addDependency(
-            ResolvedComponentResult resolvedComponentResult,
-            VariantDependencies configDependencies,
-            Collection<LibInfo> outLibraries,
-            List<JarInfo> outJars,
-            Map<ModuleVersionIdentifier, List<LibInfo>> alreadyFoundLibraries,
-            Map<ModuleVersionIdentifier, List<JarInfo>> alreadyFoundJars,
-            Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts,
-            Multimap<LibraryDependency, VariantDependencies> reverseMap,
-            Set<String> currentUnresolvedDependencies,
+            @NonNull ResolvedComponentResult resolvedComponentResult,
+            @NonNull VariantDependencies configDependencies,
+            @NonNull Collection<LibInfo> outLibraries,
+            @NonNull List<JarInfo> outJars,
+            @NonNull Map<ModuleVersionIdentifier, List<LibInfo>> alreadyFoundLibraries,
+            @NonNull Map<ModuleVersionIdentifier, List<JarInfo>> alreadyFoundJars,
+            @NonNull Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts,
+            @NonNull Multimap<LibraryDependency, VariantDependencies> reverseMap,
+            @NonNull Set<String> currentUnresolvedDependencies,
+            @Nullable String testedProjectPath,
+            @NonNull List<String> projectChain,
             int indent) {
 
         ModuleVersionIdentifier moduleVersion = resolvedComponentResult.getModuleVersion();
@@ -742,8 +770,34 @@
             Set<? extends DependencyResult> dependencies = resolvedComponentResult.getDependencies();
             for (DependencyResult dependencyResult : dependencies) {
                 if (dependencyResult instanceof ResolvedDependencyResult) {
+                    ResolvedComponentResult selected =
+                            ((ResolvedDependencyResult) dependencyResult).getSelected();
+
+                    List<String> newProjectChain = projectChain;
+
+                    ComponentIdentifier identifier = selected.getId();
+                    if (identifier instanceof ProjectComponentIdentifier) {
+                        String projectPath =
+                                ((ProjectComponentIdentifier) identifier).getProjectPath();
+
+                        int index = projectChain.indexOf(projectPath);
+                        if (index != -1) {
+                            projectChain.add(projectPath);
+                            String path = Joiner
+                                    .on(" -> ")
+                                    .join(projectChain.subList(index, projectChain.size()));
+
+                            throw new CircularReferenceException(
+                                    "Circular reference between projects: " + path);
+                        }
+
+                        newProjectChain = Lists.newArrayList();
+                        newProjectChain.addAll(projectChain);
+                        newProjectChain.add(projectPath);
+                    }
+
                     addDependency(
-                            ((ResolvedDependencyResult) dependencyResult).getSelected(),
+                            selected,
                             configDependencies,
                             nestedLibraries,
                             nestedJars,
@@ -752,7 +806,9 @@
                             artifacts,
                             reverseMap,
                             currentUnresolvedDependencies,
-                            indent+1);
+                            testedProjectPath,
+                            newProjectChain,
+                            indent + 1);
                 } else if (dependencyResult instanceof UnresolvedDependencyResult) {
                     ComponentSelector attempted = ((UnresolvedDependencyResult) dependencyResult).getAttempted();
                     if (attempted != null) {
@@ -817,11 +873,23 @@
                         }
                         // check this jar does not have a dependency on an library, as this would not work.
                         if (!nestedLibraries.isEmpty()) {
-                            configDependencies.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
-                                    new MavenCoordinatesImpl(artifact).toString(),
-                                    SyncIssue.TYPE_JAR_DEPEND_ON_AAR,
-                                    String.format(
-                                    "Module version %s depends on libraries but is a jar", moduleVersion)));
+                            if (testedProjectPath != null && testedProjectPath.equals(gradlePath)) {
+                                // TODO: make sure this is a direct dependency and not a transitive one.
+                                // add nested libs as optional somehow...
+                                for (LibInfo lib : nestedLibraries) {
+                                    lib.setIsOptional(true);
+                                }
+                                outLibraries.addAll(nestedLibraries);
+
+                            } else {
+                                configDependencies.getChecker()
+                                        .addSyncIssue(extraModelInfo.handleSyncError(
+                                                new MavenCoordinatesImpl(artifact).toString(),
+                                                SyncIssue.TYPE_JAR_DEPEND_ON_AAR,
+                                                String.format(
+                                                        "Module '%s' depends on one or more Android Libraries but is a jar",
+                                                        moduleVersion)));
+                            }
                         }
 
                         if (jarsForThisModule == null) {
@@ -1012,16 +1080,16 @@
     }
 
     /**
-     * Compute a version-less key represening the given coordinate.
-     * @param coord the coordinate
+     * Compute a version-less key representing the given coordinates.
+     * @param coordinates the coordinate
      * @return the key.
      */
     @NonNull
-    private static String computeVersionLessCoordinateKey(@NonNull MavenCoordinates coord) {
-        StringBuilder sb = new StringBuilder(coord.getGroupId());
-        sb.append(':').append(coord.getArtifactId());
-        if (coord.getClassifier() != null) {
-            sb.append(':').append(coord.getClassifier());
+    private static String computeVersionLessCoordinateKey(@NonNull MavenCoordinates coordinates) {
+        StringBuilder sb = new StringBuilder(coordinates.getGroupId());
+        sb.append(':').append(coordinates.getArtifactId());
+        if (coordinates.getClassifier() != null) {
+            sb.append(':').append(coordinates.getClassifier());
         }
         return sb.toString();
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExecutionConfigurationUtil.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExecutionConfigurationUtil.java
new file mode 100644
index 0000000..e9ddda3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExecutionConfigurationUtil.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.ide.common.internal.ExecutorSingleton;
+
+import org.gradle.api.Project;
+
+public class ExecutionConfigurationUtil {
+
+    private static final String THREAD_POOL_SIZE_PROPERTY = "com.android.build.threadPoolSize";
+
+    public static void setThreadPoolSize(Project project) {
+        if (!project.hasProperty(THREAD_POOL_SIZE_PROPERTY)) {
+            return;
+        }
+
+        String threadPoolSizeProperty = project.property(THREAD_POOL_SIZE_PROPERTY).toString();
+
+        try {
+            ExecutorSingleton.setThreadPoolSize(Integer.parseInt(threadPoolSizeProperty));
+        } catch (NumberFormatException e) {
+            project.getLogger().error("com.android.threadPoolSize should be an integer.");
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java
index 1a7c470..af28ddf 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java
@@ -19,27 +19,34 @@
 import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY;
 import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED;
 import static com.android.builder.model.AndroidProject.PROPERTY_INVOKED_FROM_IDE;
-import static com.android.ide.common.blame.output.GradleMessageRewriter.ErrorFormatMode;
+import static com.android.ide.common.blame.parser.JsonEncodedGradleMessageParser.STDOUT_ERROR_TAG;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.build.gradle.AndroidGradleOptions;
 import com.android.build.gradle.api.BaseVariant;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
 import com.android.build.gradle.internal.model.ArtifactMetaDataImpl;
 import com.android.build.gradle.internal.model.JavaArtifactImpl;
 import com.android.build.gradle.internal.model.SyncIssueImpl;
 import com.android.build.gradle.internal.model.SyncIssueKey;
 import com.android.build.gradle.internal.variant.DefaultSourceProviderContainer;
+import com.android.builder.core.ErrorReporter;
 import com.android.builder.model.AndroidArtifact;
 import com.android.builder.model.ArtifactMetaData;
 import com.android.builder.model.JavaArtifact;
 import com.android.builder.model.SourceProvider;
 import com.android.builder.model.SourceProviderContainer;
 import com.android.builder.model.SyncIssue;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.MessageJsonSerializer;
+import com.android.ide.common.blame.SourceFilePosition;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 
 import org.gradle.api.GradleException;
 import org.gradle.api.Project;
@@ -52,16 +59,13 @@
 /**
  * For storing additional model information.
  */
-public class ExtraModelInfo {
-
-    public enum ModelQueryMode {
-        STANDARD, IDE, IDE_ADVANCED
-    }
+public class ExtraModelInfo extends ErrorReporter {
 
     @NonNull
     private final Project project;
+    private final boolean isLibrary;
 
-    private final ModelQueryMode modelQueryMode;
+    @NonNull
     private final ErrorFormatMode errorFormatMode;
 
     private final Map<SyncIssueKey, SyncIssue> syncIssues = Maps.newHashMap();
@@ -74,49 +78,63 @@
     private final ListMultimap<String, SourceProviderContainer> extraProductFlavorSourceProviders = ArrayListMultimap.create();
     private final ListMultimap<String, SourceProviderContainer> extraMultiFlavorSourceProviders = ArrayListMultimap.create();
 
-    public ExtraModelInfo(@NonNull Project project) {
+    @Nullable
+    private final Gson mGson;
+
+    public ExtraModelInfo(@NonNull Project project, boolean isLibrary) {
+        super(computeModelQueryMode(project));
         this.project = project;
-        modelQueryMode = computeModelQueryMode(project);
+        this.isLibrary = isLibrary;
         errorFormatMode = computeErrorFormatMode(project);
+        if (errorFormatMode == ErrorFormatMode.MACHINE_PARSABLE) {
+            GsonBuilder gsonBuilder = new GsonBuilder();
+            MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
+            mGson = gsonBuilder.create();
+        } else {
+            mGson = null;
+        }
+    }
+
+    public boolean isLibrary() {
+        return isLibrary;
     }
 
     public Map<SyncIssueKey, SyncIssue> getSyncIssues() {
         return syncIssues;
     }
 
-    public ModelQueryMode getModelQueryMode() {
-        return modelQueryMode;
-    }
-
-    public ErrorFormatMode getErrorFormatMode() {
-        return errorFormatMode;
-    }
-
+    @Override
+    @NonNull
     public SyncIssue handleSyncError(@NonNull String data, int type, @NonNull String msg) {
-        switch (modelQueryMode) {
+        SyncIssue issue;
+        switch (getMode()) {
             case STANDARD:
-                if (isDependencyIssue(type)) {
-                    // if it's a dependency issue we don't throw right away. we'll
-                    // throw during build instead.
-                    // but we do log.
-                    project.getLogger().warn("WARNING: " + msg);
-                    return new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
+                if (!isDependencyIssue(type)) {
+                    throw new GradleException(msg);
                 }
-                throw new GradleException(msg);
-            case IDE:
+                // if it's a dependency issue we don't throw right away. we'll
+                // throw during build instead.
+                // but we do log.
+                project.getLogger().warn("WARNING: " + msg);
+                issue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
+                break;
+            case IDE_LEGACY:
                 // compat mode for the only issue supported before the addition of SyncIssue
                 // in the model.
                 if (type != SyncIssue.TYPE_UNRESOLVED_DEPENDENCY) {
                     throw new GradleException(msg);
                 }
                 // intended fall-through
-            case IDE_ADVANCED:
+            case IDE:
                 // new IDE, able to support SyncIssue.
-                SyncIssue syncIssue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
-                syncIssues.put(SyncIssueKey.from(syncIssue), syncIssue);
+                issue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
+                syncIssues.put(SyncIssueKey.from(issue), issue);
+                break;
+            default:
+                throw new RuntimeException("Unknown SyncIssue type");
         }
 
-        return null;
+        return issue;
     }
 
     private static boolean isDependencyIssue(int type) {
@@ -136,7 +154,50 @@
 
     }
 
-    public Collection<ArtifactMetaData> getExtraArtifacts() {
+    @Override
+    public void receiveMessage(@NonNull Message message) {
+        StringBuilder errorStringBuilder = new StringBuilder();
+        if (errorFormatMode == ErrorFormatMode.HUMAN_READABLE) {
+            for (SourceFilePosition pos : message.getSourceFilePositions()) {
+                errorStringBuilder.append(pos.toString());
+                errorStringBuilder.append(' ');
+            }
+            if (errorStringBuilder.length() > 0) {
+                errorStringBuilder.append(": ");
+            }
+            errorStringBuilder.append(message.getText()).append("\n");
+
+        } else {
+            //noinspection ConstantConditions mGson != null when errorFormatMode == MACHINE_PARSABLE
+            errorStringBuilder.append(STDOUT_ERROR_TAG)
+                    .append(mGson.toJson(message)).append("\n");
+        }
+
+        String messageString = errorStringBuilder.toString();
+
+        switch (message.getKind()) {
+            case ERROR:
+                project.getLogger().error(messageString);
+                break;
+            case WARNING:
+                project.getLogger().warn(messageString);
+                break;
+            case INFO:
+                project.getLogger().info(messageString);
+                break;
+            case STATISTICS:
+                project.getLogger().trace(messageString);
+                break;
+            case UNKNOWN:
+                project.getLogger().debug(messageString);
+                break;
+            case SIMPLE:
+                project.getLogger().info(messageString);
+                break;
+        }
+    }
+
+        public Collection<ArtifactMetaData> getExtraArtifacts() {
         return extraArtifactMap.values();
     }
 
@@ -171,7 +232,7 @@
     }
 
     public void registerBuildTypeSourceProvider(@NonNull String name,
-            @NonNull BuildType buildType,
+            @NonNull CoreBuildType buildType,
             @NonNull SourceProvider sourceProvider) {
         if (extraArtifactMap.get(name) == null) {
             throw new IllegalArgumentException(String.format(
@@ -185,7 +246,7 @@
     }
 
     public void registerProductFlavorSourceProvider(@NonNull String name,
-            @NonNull ProductFlavor productFlavor,
+            @NonNull CoreProductFlavor productFlavor,
             @NonNull SourceProvider sourceProvider) {
         if (extraArtifactMap.get(name) == null) {
             throw new IllegalArgumentException(String.format(
@@ -235,7 +296,7 @@
 
         JavaArtifact artifact = new JavaArtifactImpl(
                 name, assembleTaskName, javaCompileTaskName, ideSetupTaskNames,
-                generatedSourceFolders, classesFolder, javaResourcesFolder,
+                generatedSourceFolders, classesFolder, javaResourcesFolder, null,
                 new ConfigurationDependencies(configuration), sourceProvider, null);
 
         extraJavaArtifacts.put(variant.getName(), artifact);
@@ -246,36 +307,27 @@
      * means we will attempt to resolve dependencies even if some are broken/unsupported to avoid
      * failing the import in the IDE.
      */
-    private static ModelQueryMode computeModelQueryMode(@NonNull Project project) {
-        if (isPropertyTrue(project, PROPERTY_BUILD_MODEL_ONLY_ADVANCED)) {
-            return ModelQueryMode.IDE_ADVANCED;
+    private static EvaluationMode computeModelQueryMode(@NonNull Project project) {
+        if (AndroidGradleOptions.buildModelOnlyAdvanced(project)) {
+            return EvaluationMode.IDE;
         }
 
-        if (isPropertyTrue(project, PROPERTY_BUILD_MODEL_ONLY)) {
-            return ModelQueryMode.IDE;
+        if (AndroidGradleOptions.buildModelOnly(project)) {
+            return EvaluationMode.IDE_LEGACY;
         }
 
-        return ModelQueryMode.STANDARD;
+        return EvaluationMode.STANDARD;
     }
 
     private static ErrorFormatMode computeErrorFormatMode(@NonNull Project project) {
-        if (isPropertyTrue(project, PROPERTY_INVOKED_FROM_IDE)) {
+        if (AndroidGradleOptions.invokedFromIde(project)) {
             return ErrorFormatMode.MACHINE_PARSABLE;
         } else {
             return ErrorFormatMode.HUMAN_READABLE;
         }
     }
 
-    private static boolean isPropertyTrue(
-            @NonNull Project project,
-            @NonNull String propertyName) {
-        if (project.hasProperty(propertyName)) {
-            Object value = project.getProperties().get(propertyName);
-            if (value instanceof String) {
-                return Boolean.parseBoolean((String) value);
-            }
-        }
-
-        return false;
+    public enum ErrorFormatMode {
+        MACHINE_PARSABLE, HUMAN_READABLE
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.groovy
deleted file mode 100644
index 34664b9..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.groovy
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (C) 2015 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.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.profile.SpanRecorders
-import com.android.build.gradle.internal.scope.AndroidTask
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.MergeFileTask
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.build.gradle.internal.variant.LibVariantOutputData
-import com.android.build.gradle.internal.variant.LibraryVariantData
-import com.android.build.gradle.internal.variant.VariantHelper
-import com.android.build.gradle.tasks.ExtractAnnotations
-import com.android.build.gradle.tasks.MergeResources
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultBuildType
-import com.android.builder.core.VariantType
-import com.android.builder.dependency.LibraryBundle
-import com.android.builder.dependency.LibraryDependency
-import com.android.builder.dependency.ManifestDependency
-import com.android.builder.model.AndroidLibrary
-import com.android.builder.model.MavenCoordinates
-import com.android.builder.profile.ExecutionType
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.internal.ConventionMapping
-import org.gradle.api.plugins.BasePlugin
-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
-import org.gradle.tooling.BuildException
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP
-import static com.android.SdkConstants.LIBS_FOLDER
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static com.android.builder.model.AndroidProject.FD_OUTPUTS
-/**
- * TaskManager for creating tasks in an Android library project.
- */
-class LibraryTaskManager extends TaskManager {
-
-    private static final String ANNOTATIONS = "annotations"
-
-    private Task assembleDefault;
-
-    public LibraryTaskManager (
-            Project project,
-            AndroidBuilder androidBuilder,
-            BaseExtension extension,
-            SdkHandler sdkHandler,
-            DependencyManager dependencyManager,
-            ToolingModelBuilderRegistry toolingRegistry) {
-        super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry)
-    }
-
-    @Override
-    public void createTasksForVariantData(
-            @NonNull TaskFactory tasks,
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
-        LibraryVariantData libVariantData = variantData as LibraryVariantData
-        GradleVariantConfiguration variantConfig = variantData.variantConfiguration
-        DefaultBuildType buildType = variantConfig.buildType
-
-        VariantScope variantScope = variantData.getScope()
-
-        String fullName = variantConfig.fullName
-        String dirName = variantConfig.dirName
-
-        createAnchorTasks(tasks, variantScope)
-
-        createCheckManifestTask(tasks, variantScope)
-
-        // Add a task to create the res values
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK) {
-            createGenerateResValuesTask(tasks, variantScope)
-        }
-
-        // Add a task to process the manifest(s)
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK) {
-            createMergeLibManifestsTask(tasks, variantScope)
-        }
-
-        // Add a task to compile renderscript files.
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK) {
-            createRenderscriptTask(tasks, variantScope)
-        }
-
-        AndroidTask<MergeResources> packageRes = SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK) {
-            // Create a merge task to only merge the resources from this library and not
-            // the dependencies. This is what gets packaged in the aar.
-            AndroidTask<MergeResources> mergeResourceTask = basicCreateMergeResourcesTask(
-                    tasks,
-                    variantScope,
-                    "package",
-                    new File(variantScope.getGlobalScope().getIntermediatesDir(),
-                            "$DIR_BUNDLES/$variantScope.variantConfiguration.dirName/res"),
-                    false /*includeDependencies*/,
-                    false /*process9Patch*/);
-
-            if (!variantData.variantDependency.androidDependencies.isEmpty()) {
-                // Add a task to merge the resource folders, including the libraries, in order to
-                // generate the R.txt file with all the symbols, including the ones from
-                // the dependencies.
-                createMergeResourcesTask(tasks, variantScope)
-            }
-
-            mergeResourceTask.configure(tasks) { MergeResources task ->
-                task.conventionMapping.publicFile = {
-                    new File(variantScope.globalScope.intermediatesDir,
-                            "$DIR_BUNDLES/${dirName}/${SdkConstants.FN_PUBLIC_TXT}")
-                }
-            }
-
-            return mergeResourceTask;
-        }
-
-        // Add a task to merge the assets folders
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK) {
-            createMergeAssetsTask(tasks, variantScope)
-        }
-
-        // Add a task to create the BuildConfig class
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK) {
-            createBuildConfigTask(tasks, variantScope)
-        }
-
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK) {
-            createPreprocessResourcesTask(tasks, variantScope)
-        }
-
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROCESS_RES_TASK) {
-            // Add a task to generate resource source files, directing the location
-            // of the r.txt file to be directly in the bundle.
-            createProcessResTask(tasks, variantScope,
-                    new File("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"),
-                    false /*generateResourcePackage*/,
-            )
-
-            // process java resources
-            createProcessJavaResTask(tasks, variantScope)
-        }
-
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_AIDL_TASK) {
-            createAidlTask(tasks, variantScope)
-        }
-
-        // Add a compile task
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_COMPILE_TASK) {
-            createJavaCompileTask(tasks, variantScope);
-        }
-
-        // package the prebuilt native libs into the bundle folder
-        Sync packageJniLibs = project.tasks.create(
-                "package${fullName.capitalize()}JniLibs",
-                Sync)
-
-        // Add dependencies on NDK tasks if NDK plugin is applied.
-        if (isNdkTaskNeeded) {
-            // Add NDK tasks
-            SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_NDK_TASK) {
-                createNdkTasks(variantData);
-                packageJniLibs.dependsOn variantData.ndkCompileTask
-                packageJniLibs.from(variantData.ndkCompileTask.soFolder).include("**/*.so")
-            }
-        }
-
-        Sync packageRenderscript = SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGING_TASK) {
-            // package from 2 sources.
-            packageJniLibs.from(variantConfig.jniLibsList).include("**/*.so")
-            packageJniLibs.into(project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/jni"))
-
-            // package the renderscript header files files into the bundle folder
-            Sync packageRenderscript = project.tasks.create(
-                    "package${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/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$SdkConstants.FD_RENDERSCRIPT"))
-            return packageRenderscript;
-        }
-
-        // merge consumer proguard files from different build types and flavors
-        MergeFileTask mergeProGuardFileTask = SpanRecorders.record(
-                ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_PROGUARD_FILE_TASK) {
-            MergeFileTask mergeProGuardFileTask = project.tasks.create(
-                    "merge${fullName.capitalize()}ProguardFiles",
-                    MergeFileTask)
-            mergeProGuardFileTask.inputFiles =
-                project.files(variantConfig.getConsumerProguardFiles()).files
-            mergeProGuardFileTask.outputFile = project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$LibraryBundle.FN_PROGUARD_TXT")
-            return mergeProGuardFileTask
-        }
-
-        // copy lint.jar into the bundle folder
-        Copy lintCopy = project.tasks.create(
-                "copy${fullName.capitalize()}Lint",
-                Copy)
-        lintCopy.dependsOn LINT_COMPILE
-        lintCopy.from("$project.buildDir/${FD_INTERMEDIATES}/lint/lint.jar")
-        lintCopy.into("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/$dirName")
-
-        Zip bundle = project.tasks.create(
-                "bundle${fullName.capitalize()}",
-                Zip)
-
-        libVariantData.generateAnnotationsTask = variantData.variantDependency.annotationsPresent ? createExtractAnnotations(
-                fullName, project, variantData) : null
-        if (libVariantData.generateAnnotationsTask != null) {
-            bundle.dependsOn(libVariantData.generateAnnotationsTask)
-        }
-
-        final boolean instrumented = variantConfig.buildType.isTestCoverageEnabled()
-
-
-        // data holding dependencies and input for the dex. This gets updated as new
-        // post-compilation steps are inserted between the compilation and dx.
-        PostCompilationData pcData = new PostCompilationData()
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK) {
-            pcData.classGeneratingTask = [variantScope.javaCompileTask.name]
-            pcData.libraryGeneratingTask = Collections.singletonList(
-                    variantData.variantDependency.packageConfiguration.buildDependencies)
-            pcData.inputFiles = {
-                return variantData.javaCompileTask.outputs.files.files
-            }
-            pcData.inputDir = {
-                return variantScope.javaOutputDir
-            }
-            pcData.inputLibraries = {
-                return Collections.emptyList()
-            }
-
-            // if needed, instrument the code
-            if (instrumented) {
-                pcData = createJacocoTask(tasks, variantScope, pcData);
-            }
-        }
-
-        if (buildType.isMinifyEnabled()) {
-            // run proguard on output of compile task
-            SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROGUARD_TASK) {
-                File outFile = maybeCreateProguardTasks(tasks, variantScope, pcData);
-                pcData.inputFiles = { [outFile] }
-                pcData.inputDir = null
-                pcData.inputLibraries = { [] }
-            }
-        } else {
-            // package the local jar in libs/
-            SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR) {
-                Sync packageLocalJar = project.tasks.create(
-                        "package${fullName.capitalize()}LocalJar",
-                        Sync)
-                packageLocalJar.from(
-                        DependencyManager.getPackagedLocalJarFileList(
-                                variantData.variantDependency).toArray())
-                packageLocalJar.into(project.file(
-                        "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$LIBS_FOLDER"))
-
-                // add the input libraries. This is only going to be the agent jar if applicable
-                // due to how inputLibraries is initialized.
-                // TODO: clean this.
-                packageLocalJar.from(pcData.inputLibraries)
-                TaskManager.optionalDependsOn(packageLocalJar, pcData.libraryGeneratingTask)
-                pcData.libraryGeneratingTask = Collections.singletonList(packageLocalJar)
-
-                // jar the classes.
-                Jar jar = project.tasks.create("package${fullName.capitalize()}Jar", Jar);
-                jar.dependsOn variantScope.processJavaResourcesTask.name
-
-                // add the class files (whether they are instrumented or not.
-                jar.from(pcData.inputDir)
-                TaskManager.optionalDependsOn(jar, pcData.classGeneratingTask)
-                pcData.classGeneratingTask = Collections.singletonList(jar)
-
-                jar.from(variantScope.getJavaResourcesDestinationDir())
-
-                jar.destinationDir = project.file(
-                        "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${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")
-                if (!((LibraryExtension) extension).packageBuildConfig) {
-                    jar.exclude(packageName + "/Manifest.class")
-                    jar.exclude(packageName + "/Manifest\$*.class")
-                    jar.exclude(packageName + "/BuildConfig.class")
-                }
-
-                if (libVariantData.generateAnnotationsTask != null) {
-                    // In case extract annotations strips out private typedef annotation classes
-                    jar.dependsOn libVariantData.generateAnnotationsTask
-                }
-            }
-        }
-
-        bundle.dependsOn packageRes.name, packageRenderscript, lintCopy, packageJniLibs, mergeProGuardFileTask
-        TaskManager.optionalDependsOn(bundle, pcData.classGeneratingTask)
-        TaskManager.optionalDependsOn(bundle, pcData.libraryGeneratingTask)
-
-        bundle.setDescription("Assembles a bundle containing the library in ${fullName.capitalize()}.");
-        bundle.destinationDir = project.file("$project.buildDir/${FD_OUTPUTS}/aar")
-        bundle.setArchiveName("${project.name}-${variantConfig.baseName}.${BuilderConstants.EXT_LIB_ARCHIVE}")
-        bundle.extension = BuilderConstants.EXT_LIB_ARCHIVE
-        bundle.from(project.file("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"))
-        bundle.from(project.file("$project.buildDir/${FD_INTERMEDIATES}/$ANNOTATIONS/${dirName}"))
-
-        // get the single output for now, though that may always be the case for a library.
-        LibVariantOutputData variantOutputData = libVariantData.outputs.get(0)
-        variantOutputData.packageLibTask = bundle
-
-        variantData.assembleVariantTask.dependsOn bundle
-        variantOutputData.assembleTask = variantData.assembleVariantTask
-
-        if (extension.defaultPublishConfig.equals(fullName)) {
-            VariantHelper.setupDefaultConfig(project,
-                    variantData.variantDependency.packageConfiguration)
-
-            // add the artifact that will be published
-            project.artifacts.add("default", bundle)
-
-            getAssembleDefault().dependsOn variantData.assembleVariantTask
-        }
-
-        // also publish the artifact with its full config name
-        if (extension.publishNonDefault) {
-            project.artifacts.add(variantData.variantDependency.publishConfiguration.name, bundle)
-            bundle.classifier = variantData.variantDependency.publishConfiguration.name
-        }
-
-
-        // configure the variant to be testable.
-        variantConfig.output = new LibraryBundle(
-                bundle.archivePath,
-                project.file("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"),
-                variantData.getName(),
-                project.getPath()) {
-
-            @Override
-            @Nullable
-            String getProjectVariant() {
-                return variantData.getName()
-            }
-
-            @NonNull
-            @Override
-            List<LibraryDependency> getDependencies() {
-                return variantConfig.directLibraries
-            }
-
-            @NonNull
-            @Override
-            List<? extends AndroidLibrary> getLibraryDependencies() {
-                return variantConfig.directLibraries
-            }
-
-            @NonNull
-            @Override
-            List<ManifestDependency> getManifestDependencies() {
-                return variantConfig.directLibraries
-            }
-
-            @Override
-            @Nullable
-            MavenCoordinates getRequestedCoordinates() {
-                return null
-            }
-
-            @Override
-            @Nullable
-            MavenCoordinates getResolvedCoordinates() {
-                return null
-            }
-
-            @Override
-            @NonNull
-            protected File getJarsRootFolder() {
-                return getFolder();
-            }
-        };
-
-        SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_LINT_TASK) {
-            createLintTasks(tasks, variantScope);
-        }
-    }
-
-    public ExtractAnnotations createExtractAnnotations(
-            String fullName, Project project, BaseVariantData variantData) {
-        GradleVariantConfiguration config = variantData.variantConfiguration
-        String dirName = config.dirName
-
-        ExtractAnnotations task = project.tasks.create(
-                "extract${fullName.capitalize()}Annotations",
-                ExtractAnnotations)
-        task.description =
-                "Extracts Android annotations for the ${fullName} variant into the archive file"
-        task.group = BasePlugin.BUILD_GROUP
-        task.variant = variantData
-        task.destinationDir = project.file("$project.buildDir/${FD_INTERMEDIATES}/$ANNOTATIONS/${dirName}")
-        task.output = new File(task.destinationDir, FN_ANNOTATIONS_ZIP)
-        task.classDir = project.file("$project.buildDir/${FD_INTERMEDIATES}/classes/${variantData.variantConfiguration.dirName}")
-        task.source = variantData.getJavaSources()
-        task.encoding = extension.compileOptions.encoding
-        task.sourceCompatibility = extension.compileOptions.sourceCompatibility
-        conventionMapping(task).map("classpath") {
-            project.files(androidBuilder.getCompileClasspath(config))
-        }
-        task.dependsOn variantData.javaCompileTask
-
-        // Setup the boot classpath just before the task actually runs since this will
-        // force the sdk to be parsed. (Same as in compileTask)
-        task.doFirst {
-            task.bootClasspath = androidBuilder.getBootClasspathAsStrings()
-        }
-
-        return task
-    }
-
-    private Task getAssembleDefault() {
-        if (assembleDefault == null) {
-            assembleDefault = project.tasks.findByName("assembleDefault");
-        }
-        return assembleDefault
-    }
-
-    private static ConventionMapping conventionMapping(Task task) {
-        task.conventionMapping
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
new file mode 100644
index 0000000..2f2c7a3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2015 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 static com.android.SdkConstants.FN_ANNOTATIONS_ZIP;
+import static com.android.SdkConstants.LIBS_FOLDER;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.MergeFileTask;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.internal.variant.LibVariantOutputData;
+import com.android.build.gradle.internal.variant.LibraryVariantData;
+import com.android.build.gradle.internal.variant.VariantHelper;
+import com.android.build.gradle.tasks.ExtractAnnotations;
+import com.android.build.gradle.tasks.MergeResources;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.dependency.LibraryBundle;
+import com.android.builder.dependency.LibraryDependency;
+import com.android.builder.dependency.ManifestDependency;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.MavenCoordinates;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.plugins.BasePlugin;
+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;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.tooling.BuildException;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * TaskManager for creating tasks in an Android library project.
+ */
+public class LibraryTaskManager extends TaskManager {
+
+    private static final String ANNOTATIONS = "annotations";
+
+    private Task assembleDefault;
+
+    public LibraryTaskManager (
+            Project project,
+            AndroidBuilder androidBuilder,
+            AndroidConfig extension,
+            SdkHandler sdkHandler,
+            DependencyManager dependencyManager,
+            ToolingModelBuilderRegistry toolingRegistry) {
+        super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry);
+    }
+
+    @Override
+    public void createTasksForVariantData(
+            @NonNull final TaskFactory tasks,
+            @NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+        final LibraryVariantData libVariantData = (LibraryVariantData) variantData;
+        final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
+        CoreBuildType buildType = variantConfig.getBuildType();
+
+        final VariantScope variantScope = variantData.getScope();
+
+        final String dirName = variantConfig.getDirName();
+
+        createAnchorTasks(tasks, variantScope);
+
+        createCheckManifestTask(tasks, variantScope);
+
+        // Add a task to create the res values
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createGenerateResValuesTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to process the manifest(s)
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createMergeLibManifestsTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to compile renderscript files.
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createRenderscriptTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        AndroidTask<MergeResources> packageRes = ThreadRecorder.get().record(
+                ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK,
+                new Recorder.Block<AndroidTask<MergeResources>>() {
+                    @Override
+                    public AndroidTask<MergeResources> call() throws Exception {
+                        // Create a merge task to only merge the resources from this library and not
+                        // the dependencies. This is what gets packaged in the aar.
+                        AndroidTask<MergeResources> mergeResourceTask =
+                                basicCreateMergeResourcesTask(
+                                        tasks,
+                                        variantScope,
+                                        "package",
+                                        new File(
+                                                variantScope.getGlobalScope().getIntermediatesDir(),
+                                                DIR_BUNDLES + "/" + variantScope
+                                                        .getVariantConfiguration().getDirName() +
+                                                        "/res"),
+                                        false /*includeDependencies*/,
+                                        false /*process9Patch*/);
+
+                        if (variantData.getVariantDependency().hasNonOptionalLibraries()) {
+                            // Add a task to merge the resource folders, including the libraries, in order to
+                            // generate the R.txt file with all the symbols, including the ones from
+                            // the dependencies.
+                            createMergeResourcesTask(tasks, variantScope);
+                        }
+
+                        mergeResourceTask.configure(tasks,
+                                new Action<Task>() {
+                                    @Override
+                                    public void execute(Task task) {
+                                        MergeResources mergeResourcesTask = (MergeResources) task;
+                                        mergeResourcesTask.setPublicFile(new File(
+                                                variantScope.getGlobalScope().getIntermediatesDir(),
+                                                DIR_BUNDLES + "/" + dirName + "/" +
+                                                        SdkConstants.FN_PUBLIC_TXT));
+                                    }
+                                });
+
+                        return mergeResourceTask;
+                    }
+                });
+
+        // Add a task to merge the assets folders
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() {
+                        createMergeAssetsTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a task to create the BuildConfig class
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createBuildConfigTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROCESS_RES_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        // Add a task to generate resource source files, directing the location
+                        // of the r.txt file to be directly in the bundle.
+                        createProcessResTask(tasks, variantScope,
+                                new File(variantScope.getGlobalScope().getIntermediatesDir(),
+                                        DIR_BUNDLES + "/" + dirName),
+                                false /*generateResourcePackage*/);
+
+                        // process java resources
+                        createProcessJavaResTasks(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_AIDL_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createAidlTask(tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // Add a compile task
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_COMPILE_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+                        TaskManager.setJavaCompilerTask(javacTask, tasks, variantScope);
+                        return null;
+                    }
+                });
+
+        // package the prebuilt native libs into the bundle folder
+        final Sync packageJniLibs = project.getTasks().create(
+                variantScope.getTaskName("package", "JniLibs"),
+                Sync.class);
+
+        // Add dependencies on NDK tasks if NDK plugin is applied.
+        if (isNdkTaskNeeded) {
+            // Add NDK tasks
+            ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_NDK_TASK,
+                    new Recorder.Block<Void>() {
+                        @Override
+                        public Void call() throws Exception {
+                            createNdkTasks(variantScope);
+                            packageJniLibs.dependsOn(variantData.ndkCompileTask);
+                            packageJniLibs.from(variantData.ndkCompileTask.getSoFolder())
+                                    .include("**/*.so");
+                            return null;
+                        }
+                    });
+        } else {
+            if (variantData.compileTask != null) {
+                variantData.compileTask.dependsOn(getNdkBuildable(variantData));
+            } else {
+                variantScope.getCompileTask().dependsOn(tasks, getNdkBuildable(variantData));
+            }
+        }
+
+        Sync packageRenderscript = ThreadRecorder.get().record(
+                ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGING_TASK,
+                new Recorder.Block<Sync>() {
+                    @Override
+                    public Sync call() throws Exception {
+                        // package from 2 sources.
+                        packageJniLibs.from(variantConfig.getJniLibsList())
+                                .include("**/*.so");
+                        packageJniLibs.into(new File(
+                                variantScope.getGlobalScope().getIntermediatesDir(),
+                                DIR_BUNDLES + "/" + dirName + "/jni"));
+
+                        // package the renderscript header files files into the bundle folder
+                        Sync packageRenderscript = project.getTasks().create(
+                                variantScope.getTaskName("package", "Renderscript"), Sync.class);
+                        // package from 3 sources. the order is important to make sure the override works well.
+                        packageRenderscript.from(variantConfig.getRenderscriptSourceList())
+                                .include("**/*.rsh");
+                        packageRenderscript.into(new File(
+                                variantScope.getGlobalScope().getIntermediatesDir(),
+                                DIR_BUNDLES + "/" + dirName + "/" + SdkConstants.FD_RENDERSCRIPT));
+                        return packageRenderscript;
+                    }
+                });
+
+        // merge consumer proguard files from different build types and flavors
+        MergeFileTask mergeProGuardFileTask = ThreadRecorder.get().record(
+                ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_PROGUARD_FILE_TASK,
+                new Recorder.Block<MergeFileTask>() {
+                    @Override
+                    public MergeFileTask call() throws Exception {
+                        MergeFileTask mergeProGuardFileTask = project.getTasks().create(
+                                variantScope.getTaskName("merge", "ProguardFiles"),
+                                MergeFileTask.class);
+                        mergeProGuardFileTask.setVariantName(variantConfig.getFullName());
+                        mergeProGuardFileTask.setInputFiles(
+                                project.files(variantConfig.getConsumerProguardFiles())
+                                        .getFiles());
+                        mergeProGuardFileTask.setOutputFile(new File(
+                                variantScope.getGlobalScope().getIntermediatesDir(),
+                                DIR_BUNDLES + "/" + dirName + "/" + LibraryBundle.FN_PROGUARD_TXT));
+                        return mergeProGuardFileTask;
+                    }
+
+                });
+
+        // copy lint.jar into the bundle folder
+        Copy lintCopy = project.getTasks().create(
+                variantScope.getTaskName("copy", "Lint"), Copy.class);
+        lintCopy.dependsOn(LINT_COMPILE);
+        lintCopy.from(new File(
+                variantScope.getGlobalScope().getIntermediatesDir(),
+                "lint/lint.jar"));
+        lintCopy.into(new File(
+                variantScope.getGlobalScope().getIntermediatesDir(),
+                DIR_BUNDLES + "/" + dirName));
+
+        final Zip bundle = project.getTasks().create(variantScope.getTaskName("bundle"), Zip.class);
+
+        if (variantData.getVariantDependency().isAnnotationsPresent()) {
+            libVariantData.generateAnnotationsTask =
+                    createExtractAnnotations(project, variantData);
+        }
+        if (libVariantData.generateAnnotationsTask != null) {
+            bundle.dependsOn(libVariantData.generateAnnotationsTask);
+        }
+
+        final boolean instrumented = variantConfig.getBuildType().isTestCoverageEnabled();
+
+        // data holding dependencies and input for the dex. This gets updated as new
+        // post-compilation steps are inserted between the compilation and dx.
+        final PostCompilationData pcDataTemp = new PostCompilationData();
+
+        final PostCompilationData pcData = ThreadRecorder.get().record(
+                ExecutionType.LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK,
+                new Recorder.Block<PostCompilationData>() {
+                    @Override
+                    public PostCompilationData call() throws Exception {
+                        pcDataTemp.setClassGeneratingTasks(Collections.singletonList(
+                                variantScope.getJavacTask().getName()));
+                        pcDataTemp.setLibraryGeneratingTasks(Collections.singletonList(
+                                variantData.getVariantDependency().getPackageConfiguration()
+                                        .getBuildDependencies()));
+                        pcDataTemp.setInputFilesCallable(new Callable<List<File>>() {
+                            @Override
+                            public List<File> call() throws Exception {
+                                return new ArrayList<File>(
+                                        variantData.javacTask.getOutputs().getFiles().getFiles());
+                            }
+
+                        });
+                        pcDataTemp.setInputDir(variantScope.getJavaOutputDir());
+                        pcDataTemp.setInputLibraries(Collections.<File>emptyList());
+
+                        // if needed, instrument the code
+                        if (instrumented) {
+                            return createJacocoTask(tasks, variantScope, pcDataTemp);
+                        }
+                        return pcDataTemp;
+                    }
+                });
+        checkState(pcData != null);
+
+        if (buildType.isMinifyEnabled()) {
+            // run proguard on output of compile task
+            ThreadRecorder.get().record(
+                    ExecutionType.LIB_TASK_MANAGER_CREATE_PROGUARD_TASK,
+                    new Recorder.Block<Void>() {
+                        @Override
+                        public Void call() throws Exception {
+                            File outFile = maybeCreateProguardTasks(tasks, variantScope,
+                                    pcData);
+                            checkNotNull(outFile);
+                            pcData.setInputFiles(Collections.singletonList(outFile));
+                            pcData.setInputDirCallable(null);
+                            pcData.setInputLibraries(Collections.<File>emptyList());
+                            return null;
+                        }
+                    });
+        } else {
+            // package the local jar in libs/
+            ThreadRecorder.get().record(
+                    ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR,
+                    new Recorder.Block<Void>() {
+                        @Override
+                        public Void call() throws Exception {
+                            Sync packageLocalJar = project.getTasks().create(
+                                    variantScope.getTaskName("package", "LocalJar"), Sync.class);
+                            packageLocalJar.from(
+                                    DependencyManager
+                                            .getPackagedLocalJarFileList(
+                                                    variantData.getVariantDependency())
+                                            .toArray());
+                            packageLocalJar.into(new File(
+                                    variantScope.getGlobalScope().getIntermediatesDir(),
+                                    DIR_BUNDLES + "/" + dirName + "/" + LIBS_FOLDER));
+
+                            // add the input libraries. This is only going to be the agent jar if applicable
+                            // due to how inputLibraries is initialized.
+                            // TODO: clean this.
+                            packageLocalJar.from(pcData.getInputLibrariesCallable());
+                            TaskManager.optionalDependsOn(
+                                    packageLocalJar,
+                                    pcData.getLibraryGeneratingTasks());
+                            pcData.setLibraryGeneratingTasks(
+                                    Collections.singletonList(packageLocalJar));
+
+                            // jar the classes.
+                            Jar jar = project.getTasks().create(
+                                    variantScope.getTaskName("package", "Jar"), Jar.class);
+                            jar.dependsOn(variantScope.getMergeJavaResourcesTask().getName());
+
+                            // add the class files (whether they are instrumented or not.
+                            jar.from(pcData.getInputDirCallable());
+                            TaskManager.optionalDependsOn(jar, pcData.getClassGeneratingTasks());
+                            pcData.setClassGeneratingTasks(Collections.singletonList(jar));
+
+                            jar.from(variantScope.getJavaResourcesDestinationDir());
+
+                            jar.setDestinationDir(new File(
+                                    variantScope.getGlobalScope().getIntermediatesDir(),
+                                    DIR_BUNDLES + "/" + dirName));
+                            jar.setArchiveName("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");
+                            if (!getExtension().getPackageBuildConfig()) {
+                                jar.exclude(packageName + "/Manifest.class");
+                                jar.exclude(packageName + "/Manifest$*.class");
+                                jar.exclude(packageName + "/BuildConfig.class");
+                            }
+
+                            if (libVariantData.generateAnnotationsTask != null) {
+                                // In case extract annotations strips out private typedef annotation classes
+                                jar.dependsOn(libVariantData.generateAnnotationsTask);
+                            }
+                            return null;
+                        }
+                    });
+        }
+
+        bundle.dependsOn(packageRes.getName(), packageRenderscript, lintCopy, packageJniLibs,
+                mergeProGuardFileTask);
+        TaskManager.optionalDependsOn(bundle, pcData.getClassGeneratingTasks());
+        TaskManager.optionalDependsOn(bundle, pcData.getLibraryGeneratingTasks());
+
+        bundle.setDescription("Assembles a bundle containing the library in " +
+                variantConfig.getFullName() + ".");
+        bundle.setDestinationDir(new File(variantScope.getGlobalScope().getOutputsDir(), "aar"));
+        bundle.setArchiveName(project.getName() + "-" + variantConfig.getBaseName() + "."
+                + BuilderConstants.EXT_LIB_ARCHIVE);
+        bundle.setExtension(BuilderConstants.EXT_LIB_ARCHIVE);
+        bundle.from(new File(
+                variantScope.getGlobalScope().getIntermediatesDir(),
+                DIR_BUNDLES + "/" + dirName));
+        bundle.from(new File(
+                variantScope.getGlobalScope().getIntermediatesDir(),
+                ANNOTATIONS + "/" + dirName));
+
+        // get the single output for now, though that may always be the case for a library.
+        LibVariantOutputData variantOutputData = libVariantData.getOutputs().get(0);
+        variantOutputData.packageLibTask = bundle;
+
+        variantData.assembleVariantTask.dependsOn(bundle);
+        variantOutputData.assembleTask = variantData.assembleVariantTask;
+
+        if (getExtension().getDefaultPublishConfig().equals(variantConfig.getFullName())) {
+            VariantHelper.setupDefaultConfig(project,
+                    variantData.getVariantDependency().getPackageConfiguration());
+
+            // add the artifact that will be published
+            project.getArtifacts().add("default", bundle);
+
+            getAssembleDefault().dependsOn(variantData.assembleVariantTask);
+        }
+
+        // also publish the artifact with its full config name
+        if (getExtension().getPublishNonDefault()) {
+            project.getArtifacts().add(
+                    variantData.getVariantDependency().getPublishConfiguration().getName(), bundle);
+            bundle.setClassifier(
+                    variantData.getVariantDependency().getPublishConfiguration().getName());
+        }
+
+        // configure the variant to be testable.
+        variantConfig.setOutput(new LibraryBundle(
+                bundle.getArchivePath(),
+                new File(variantScope.getGlobalScope().getIntermediatesDir(),
+                        DIR_BUNDLES + "/" + dirName),
+                variantData.getName(),
+                project.getPath()) {
+            @Override
+            @Nullable
+            public String getProjectVariant() {
+                return variantData.getName();
+            }
+
+            @NonNull
+            @Override
+            public List<LibraryDependency> getDependencies() {
+                return variantConfig.getDirectLibraries();
+            }
+
+            @NonNull
+            @Override
+            public List<? extends AndroidLibrary> getLibraryDependencies() {
+                return variantConfig.getDirectLibraries();
+            }
+
+            @NonNull
+            @Override
+            public List<? extends ManifestDependency> getManifestDependencies() {
+                return variantConfig.getDirectLibraries();
+            }
+
+            @Override
+            @Nullable
+            public MavenCoordinates getRequestedCoordinates() {
+                return null;
+            }
+
+            @Override
+            @Nullable
+            public MavenCoordinates getResolvedCoordinates() {
+                return null;
+            }
+
+            @Override
+            @NonNull
+            protected File getJarsRootFolder() {
+                return getFolder();
+            }
+
+            @Override
+            public boolean isOptional() {
+                return false;
+            }
+
+        });
+
+        ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_LINT_TASK,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createLintTasks(tasks, variantScope);
+                        return null;
+                    }
+                });
+    }
+
+    public ExtractAnnotations createExtractAnnotations(
+            final Project project,
+            final BaseVariantData variantData) {
+        final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+
+        final ExtractAnnotations task = project.getTasks().create(
+                variantData.getScope().getTaskName("extract", "Annotations"),
+                ExtractAnnotations.class);
+        task.setDescription(
+                "Extracts Android annotations for the " + variantData.getVariantConfiguration()
+                        .getFullName()
+                        + " variant into the archive file");
+        task.setGroup(BasePlugin.BUILD_GROUP);
+        task.variant = variantData;
+        task.setDestinationDir(new File(
+                variantData.getScope().getGlobalScope().getIntermediatesDir(),
+                ANNOTATIONS + "/" + config.getDirName()));
+        task.output = new File(task.getDestinationDir(), FN_ANNOTATIONS_ZIP);
+        task.classDir = new File(variantData.getScope().getGlobalScope().getIntermediatesDir(),
+                "classes/" + variantData.getVariantConfiguration().getDirName());
+        task.setSource(variantData.getJavaSources());
+        task.encoding = getExtension().getCompileOptions().getEncoding();
+        task.setSourceCompatibility(
+                getExtension().getCompileOptions().getSourceCompatibility().toString());
+        ConventionMappingHelper.map(task, "classpath", new Callable<ConfigurableFileCollection>() {
+            @Override
+            public ConfigurableFileCollection call() throws Exception {
+                return project.files(androidBuilder.getCompileClasspath(config));
+            }
+        });
+        task.dependsOn(variantData.getScope().getJavacTask().getName());
+
+        // Setup the boot classpath just before the task actually runs since this will
+        // force the sdk to be parsed. (Same as in compileTask)
+        task.doFirst(new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                if (task instanceof ExtractAnnotations) {
+                    ExtractAnnotations extractAnnotations = (ExtractAnnotations) task;
+                    extractAnnotations.bootClasspath = androidBuilder.getBootClasspathAsStrings();
+                }
+            }
+        });
+
+        return task;
+    }
+
+    private Task getAssembleDefault() {
+        if (assembleDefault == null) {
+            assembleDefault = project.getTasks().findByName("assembleDefault");
+        }
+        return assembleDefault;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/NdkHandler.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/NdkHandler.java
new file mode 100644
index 0000000..dca6ee0
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/NdkHandler.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2014 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 static com.android.SdkConstants.FN_LOCAL_PROPERTIES;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.core.Toolchain;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.repository.PreciseRevision;
+import com.android.utils.Pair;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Closeables;
+
+import org.gradle.api.InvalidUserDataException;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Handles NDK related information.
+ */
+public class NdkHandler {
+
+    @Nullable
+    private String compileSdkVersion;
+    private boolean resolvedSdkVersion;
+    private final Toolchain toolchain;
+    private final String toolchainVersion;
+    private final File ndkDirectory;
+
+    private Map<Pair<Toolchain, Abi>, PreciseRevision> defaultToolchainVersions = Maps.newHashMap();
+
+
+    public NdkHandler(
+            @NonNull File projectDir,
+            @Nullable String compileSdkVersion,
+            @NonNull String toolchainName,
+            @NonNull String toolchainVersion) {
+        if (compileSdkVersion != null) {
+            setCompileSdkVersion(compileSdkVersion);
+        }
+        this.toolchain = Toolchain.getByName(toolchainName);
+        this.toolchainVersion = toolchainVersion;
+        ndkDirectory = findNdkDirectory(projectDir);
+    }
+
+    @Nullable
+    public String getCompileSdkVersion() {
+        if (!resolvedSdkVersion) {
+            resolveCompileSdkVersion();
+        }
+        return compileSdkVersion;
+    }
+
+    /**
+     * Retrieve the newest supported version if it is not the specified version is not supported.
+     *
+     * An older NDK may not support the specified compiledSdkVersion.  In that case, determine what
+     * is the newest supported version and modify compileSdkVersion.
+     */
+    private void resolveCompileSdkVersion() {
+        if (compileSdkVersion == null) {
+            return;
+        }
+        File platformFolder = new File(ndkDirectory, "/platforms/" + compileSdkVersion);
+        if (!platformFolder.exists()) {
+            int targetVersion;
+            try {
+                targetVersion = Integer.parseInt(compileSdkVersion.substring("android-".length()));
+            } catch (NumberFormatException ignore) {
+                // If the targetVerison is not a number, most likely it is a preview version.
+                // In that case, assume we are using the highest available version.
+                targetVersion = Integer.MAX_VALUE;
+            }
+
+            File[] platformFolders = new File(ndkDirectory, "/platforms/").listFiles(
+                    new FileFilter() {
+                        @Override
+                        public boolean accept(File file) {
+                            return file.isDirectory();
+                        }
+                    });
+            int highestVersion = 0;
+            for(File platform :platformFolders) {
+                if (platform.getName().startsWith("android-")) {
+                    try {
+                        int version = Integer.parseInt(
+                                platform.getName().substring("android-".length()));
+                        if (version > highestVersion && version < targetVersion) {
+                            highestVersion = version;
+                            compileSdkVersion = "android-" + version;
+                        }
+                    } catch(NumberFormatException ignore) {
+                    }
+                }
+            }
+        }
+        resolvedSdkVersion = true;
+    }
+
+    public void setCompileSdkVersion(@NonNull String compileSdkVersion) {
+        // Ensure compileSdkVersion is in platform hash string format (e.g. "android-21").
+        AndroidVersion androidVersion = AndroidTargetHash.getVersionFromHash(compileSdkVersion);
+        if (androidVersion == null) {
+            this.compileSdkVersion = null;
+        } else {
+            this.compileSdkVersion = AndroidTargetHash.getPlatformHashString(androidVersion);
+        }
+        resolvedSdkVersion = false;
+    }
+
+    public Toolchain getToolchain() {
+        return toolchain;
+    }
+
+    public String getToolchainVersion() {
+        return toolchainVersion;
+    }
+
+    /**
+     * Determine the location of the NDK directory.
+     *
+     * The NDK directory can be set in the local.properties file or using the ANDROID_NDK_HOME
+     * environment variable.
+     */
+    private static File findNdkDirectory(File projectDir) {
+        File localProperties = new File(projectDir, FN_LOCAL_PROPERTIES);
+
+        if (localProperties.isFile()) {
+
+            Properties properties = new Properties();
+            InputStreamReader reader = null;
+            try {
+                //noinspection IOResourceOpenedButNotSafelyClosed
+                FileInputStream fis = new FileInputStream(localProperties);
+                reader = new InputStreamReader(fis, Charsets.UTF_8);
+                properties.load(reader);
+            } catch (FileNotFoundException ignored) {
+                // ignore since we check up front and we don't want to fail on it anyway
+                // in case there's an env var.
+            } catch (IOException e) {
+                throw new RuntimeException(String.format("Unable to read %1$s.", localProperties), e);
+            } finally {
+                try {
+                    Closeables.close(reader, true /* swallowIOException */);
+                } catch (IOException e) {
+                    // ignore.
+                }
+            }
+
+            String ndkDirProp = properties.getProperty("ndk.dir");
+            if (ndkDirProp != null) {
+                return new File(ndkDirProp);
+            }
+
+        } else {
+            String envVar = System.getenv("ANDROID_NDK_HOME");
+            if (envVar != null) {
+                return new File(envVar);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the directory of the NDK.
+     */
+    @Nullable
+    public File getNdkDirectory() {
+        return ndkDirectory;
+    }
+
+    /**
+     * Return true if NDK directory is configured.
+     */
+    public boolean isNdkDirConfigured() {
+        return ndkDirectory != null;
+    }
+
+    private static String getToolchainPrefix(Toolchain toolchain, Abi abi) {
+        if (toolchain == Toolchain.GCC) {
+            return abi.getGccToolchainPrefix();
+        } else {
+            return "llvm";
+        }
+    }
+
+    /**
+     * Return the directory containing the toolchain.
+     *
+     * @param toolchain toolchain to use.
+     * @param toolchainVersion toolchain version to use.
+     * @param abi target ABI of the toolchaina
+     * @return a directory that contains the executables.
+     */
+    private File getToolchainPath(
+            Toolchain toolchain,
+            String toolchainVersion,
+            Abi abi) {
+        String version = toolchainVersion.isEmpty()
+                ? getDefaultToolchainVersion(toolchain, abi).toString()
+                : toolchainVersion;
+
+        File prebuiltFolder = new File(
+                ndkDirectory,
+                "toolchains/" + getToolchainPrefix(toolchain, abi) + "-" + version + "/prebuilt");
+
+        String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+        String hostOs;
+        if (osName.contains("windows")) {
+            hostOs = "windows";
+        } else if (osName.contains("mac")) {
+            hostOs = "darwin";
+        } else {
+            hostOs = "linux";
+        }
+
+        // There should only be one directory in the prebuilt folder.  If there are more than one
+        // attempt to determine the right one based on the operating system.
+        File[] toolchainPaths = prebuiltFolder.listFiles(
+                new FileFilter() {
+                    @Override
+                    public boolean accept(File file) {
+                        return file.isDirectory();
+                    }
+                });
+
+        if (toolchainPaths == null) {
+            throw new InvalidUserDataException("Unable to find toolchain: " + prebuiltFolder);
+        }
+        if (toolchainPaths.length == 1) {
+            return toolchainPaths[0];
+        }
+
+        // Use 64-bit toolchain if available.
+        File toolchainPath = new File(prebuiltFolder, hostOs + "-x86_64");
+        if (toolchainPath.isDirectory()) {
+            return toolchainPath;
+        }
+
+        // Fallback to 32-bit if we can't find the 64-bit toolchain.
+        String osString = (osName.equals("windows")) ? hostOs : hostOs + "-x86";
+        toolchainPath = new File(prebuiltFolder, osString);
+        if (toolchainPath.isDirectory()) {
+            return toolchainPath;
+        } else {
+            throw new InvalidUserDataException("Unable to find toolchain prebuilt folder in: "
+                    + prebuiltFolder);
+        }
+    }
+
+    /**
+     * Returns the sysroot directory for the toolchain.
+     */
+    public String getSysroot(Abi abi) {
+        if (getCompileSdkVersion() == null) {
+            return "";
+        } else {
+            return ndkDirectory + "/platforms/" + getCompileSdkVersion() + "/arch-"
+                    + abi.getArchitecture();
+        }
+    }
+
+    /**
+     * Return the directory containing prebuilt binaries such as gdbserver.
+     */
+    public File getPrebuiltDirectory(Abi abi) {
+        return new File(ndkDirectory, "prebuilt/android-" + abi.getArchitecture());
+    }
+
+    /**
+     * Return true if compiledSdkVersion supports 64 bits ABI.
+     */
+    public boolean supports64Bits() {
+        if (getCompileSdkVersion() == null) {
+            return false;
+        }
+        String targetString = getCompileSdkVersion().replace("android-", "");
+        try {
+            return Integer.parseInt(targetString) >= 20;
+        } catch (NumberFormatException ignored) {
+            // "android-L" supports 64-bits.
+            return true;
+        }
+    }
+
+    /**
+     * Return the default version of the specified toolchain for a target abi.
+     *
+     * The default version is the highest version found in the NDK for the specified toolchain and
+     * ABI.  The result is cached for performance.
+     */
+    private PreciseRevision getDefaultToolchainVersion(Toolchain toolchain, final Abi abi) {
+        PreciseRevision defaultVersion = defaultToolchainVersions.get(Pair.of(toolchain, abi));
+        if (defaultVersion != null) {
+            return defaultVersion;
+        }
+
+        final String toolchainPrefix = getToolchainPrefix(toolchain, abi);
+        File toolchains = new File(ndkDirectory, "toolchains");
+        File[] toolchainsForAbi = toolchains.listFiles(new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return name.startsWith(toolchainPrefix);
+            }
+        });
+        if (toolchainsForAbi == null || toolchainsForAbi.length == 0) {
+            throw new RuntimeException(
+                    "No toolchains found in the NDK toolchains folder for ABI with prefix: "
+                            + toolchainPrefix);
+        }
+
+        // Once we have a list of toolchains, we look the highest version
+        PreciseRevision bestRevision = null;
+        for (File toolchainFolder : toolchainsForAbi) {
+            String folderName = toolchainFolder.getName();
+            String version = folderName.substring(toolchainPrefix.length() + 1);
+            try {
+                PreciseRevision revision = PreciseRevision.parseRevision(version);
+                if (bestRevision == null || revision.compareTo(bestRevision) > 0) {
+                    bestRevision = revision;
+                }
+            } catch (NumberFormatException ignore) {
+            }
+        }
+        defaultToolchainVersions.put(Pair.of(toolchain, abi), bestRevision);
+        if (bestRevision == null) {
+            throw new RuntimeException("Unable to find a valid toolchain in " + toolchains);
+        }
+        return bestRevision;
+    }
+
+    /**
+     * Return the version of gcc that will be used by the NDK.
+     *
+     * Gcc is used by clang for linking.  It also contains gnu-libstdc++.
+     *
+     * If the gcc toolchain is used, then it's simply the toolchain version requested by the user.
+     * If clang is used, then it depends the target abi.
+     */
+    public String getGccToolchainVersion(Abi abi) {
+        return (toolchain == Toolchain.GCC && !toolchainVersion.isEmpty())
+                ? toolchainVersion
+                : getDefaultToolchainVersion(Toolchain.GCC, abi).toString();
+    }
+
+    /**
+     * Return the folder containing gcc that will be used by the NDK.
+     */
+    public File getDefaultGccToolchainPath(Abi abi) {
+        return getToolchainPath(Toolchain.GCC, getGccToolchainVersion(abi), abi);
+    }
+
+    /**
+     * Returns a list of all ABI.
+     */
+    public static Collection<Abi> getAbiList() {
+        return ImmutableList.copyOf(Abi.values());
+    }
+
+    /**
+     * Returns a list of 32-bits ABI.
+     */
+    public static Collection<Abi> getAbiList32() {
+        ImmutableList.Builder<Abi> builder = ImmutableList.builder();
+        for (Abi abi : Abi.values()) {
+            if (!abi.supports64Bits()) {
+                builder.add(abi);
+            }
+        }
+        return builder.build();
+    }
+
+    /**
+     * Returns a list of supported ABI.
+     */
+    public Collection<Abi> getSupportedAbis() {
+        return supports64Bits() ? getAbiList() : getAbiList32();
+    }
+
+    /**
+     * Return the executable for compiling C code.
+     */
+    public File getCCompiler(Abi abi) {
+        String compiler = toolchain == Toolchain.CLANG ? "clang" : abi.getGccExecutablePrefix() + "-gcc";
+        return new File(getToolchainPath(toolchain, toolchainVersion, abi), "bin/" + compiler);
+    }
+
+    /**
+     * Return the executable for compiling C++ code.
+     */
+    public File getCppCompiler(Abi abi) {
+        String compiler = toolchain == Toolchain.CLANG ? "clang++" : abi.getGccExecutablePrefix() + "-g++";
+        return new File(getToolchainPath(toolchain, toolchainVersion, abi), "bin/" + compiler);
+    }
+
+    /**
+     * Return the executable for removing debug symbols from a shared object.
+     */
+    public File getStripCommand(Abi abi) {
+        String strip = toolchain == Toolchain.CLANG ? "ndk-strip" : abi.getGccExecutablePrefix() + "-strip";
+        return new File(getToolchainPath(toolchain, toolchainVersion, abi), "bin/" + strip);
+    }
+
+    /**
+     * Return a list of include directories for an STl.
+     */
+    public List<File> getStlIncludes(@Nullable String stlName, @NonNull Abi abi) {
+        File stlBaseDir = new File(ndkDirectory, "sources/cxx-stl/");
+        if (stlName == null || stlName.isEmpty()) {
+            stlName = "system";
+        } else if (stlName.contains("_")) {
+            stlName = stlName.substring(0, stlName.indexOf('_'));
+        }
+
+        List<File> includeDirs = Lists.newArrayList();
+        if (stlName.equals("system")) {
+            includeDirs.add(new File(stlBaseDir, "system/include"));
+        } else if (stlName.equals("stlport")) {
+            includeDirs.add(new File(stlBaseDir, "stlport/stlport"));
+            includeDirs.add(new File(stlBaseDir, "gabi++/include"));
+        } else if (stlName.equals("gnustl")) {
+            String gccToolchainVersion = getGccToolchainVersion(abi);
+            includeDirs.add(new File(stlBaseDir, "gnu-libstdc++/" + gccToolchainVersion + "/include"));
+            includeDirs.add(new File(stlBaseDir, "gnu-libstdc++/" + gccToolchainVersion +
+                    "/libs/" + abi.getName() + "/include"));
+            includeDirs.add(new File(stlBaseDir, "gnu-libstdc++/" + gccToolchainVersion +
+                    "/include/backward"));
+        } else if (stlName.equals("gabi++")) {
+            includeDirs.add(new File(stlBaseDir, "gabi++/include"));
+        } else if (stlName.equals("c++")) {
+            includeDirs.add(new File(stlBaseDir, "llvm-libc++/libcxx/include"));
+            includeDirs.add(new File(stlBaseDir, "gabi++/include"));
+            includeDirs.add(new File(stlBaseDir, "../android/support/include"));
+        }
+
+        return includeDirs;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/PostCompilationData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/PostCompilationData.java
new file mode 100644
index 0000000..e914086
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/PostCompilationData.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 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.annotations.Nullable;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Callables;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Class to hold data to setup the many optional post-compilation steps.
+ */
+public class PostCompilationData {
+
+    @Nullable
+    private List<?> classGeneratingTasks;
+
+    @Nullable
+    private List<?> libraryGeneratingTasks;
+
+    @Nullable
+    private Callable<List<File>> inputFiles;
+
+    @Nullable
+    private Callable<File> inputDir;
+
+    @Nullable
+    private Callable<File> javaResourcesInputDir;
+
+    @Nullable
+    private Callable<List<File>> inputLibraries;
+
+    @NonNull
+    public List<?> getClassGeneratingTasks() {
+        Preconditions.checkState(classGeneratingTasks != null);
+        return classGeneratingTasks;
+    }
+
+    public void setClassGeneratingTasks(@NonNull List<?> classGeneratingTasks) {
+        this.classGeneratingTasks = classGeneratingTasks;
+    }
+
+    @NonNull
+    public List<?> getLibraryGeneratingTasks() {
+        Preconditions.checkState(libraryGeneratingTasks != null);
+        return libraryGeneratingTasks;
+    }
+
+    public void setLibraryGeneratingTasks(@NonNull List<?> libraryGeneratingTasks) {
+        this.libraryGeneratingTasks = libraryGeneratingTasks;
+    }
+
+    @Nullable
+    public Callable<List<File>> getInputFilesCallable() {
+        return inputFiles;
+    }
+
+    public void setInputFiles(@Nullable List<File> inputFiles) {
+        this.inputFiles = Callables.returning(inputFiles);
+    }
+
+    public void setInputFilesCallable(@Nullable Callable<List<File>> inputFiles) {
+        this.inputFiles = inputFiles;
+    }
+
+    @Nullable
+    public Callable<File> getInputDirCallable() {
+        return inputDir;
+    }
+
+    public void setInputDir(@NonNull File inputDir) {
+        this.inputDir = Callables.returning(inputDir);
+    }
+
+    public void setInputDirCallable(@Nullable Callable<File> inputDir) {
+        this.inputDir = inputDir;
+    }
+
+    @Nullable
+    public Callable<File> getJavaResourcesInputDirCallable() {
+        return javaResourcesInputDir;
+    }
+
+    public void setJavaResourcesInputDir(@NonNull File javaResourcesInputDir) {
+        this.javaResourcesInputDir = Callables.returning(javaResourcesInputDir);
+    }
+
+    public void setJavaResourcesInputDirCallable(@Nullable Callable<File> javaResourcesInputDir) {
+        this.javaResourcesInputDir = javaResourcesInputDir;
+    }
+
+    @Nullable
+    public Callable<List<File>> getInputLibrariesCallable() {
+        return inputLibraries;
+    }
+
+    public void setInputLibraries(@NonNull List<File> inputLibraries) {
+        this.inputLibraries = Callables.returning(inputLibraries);
+    }
+
+    public void setInputLibrariesCallable(@Nullable Callable<List<File>> inputLibraries) {
+        this.inputLibraries = inputLibraries;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java
index 1aa077a..561612f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java
@@ -18,34 +18,38 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
+import com.android.builder.model.DimensionAware;
 import com.android.builder.model.ProductFlavor;
 import com.android.utils.StringHelper;
+import com.google.common.base.Predicates;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 
+import org.gradle.api.Named;
+
 import java.util.List;
 
 /**
  * A combination of product flavors for a variant, each belonging to a different flavor dimension.
  */
-public class ProductFlavorCombo {
+public class ProductFlavorCombo<T extends DimensionAware & Named> {
     private String name;
 
     @NonNull
-    private final List<GroupableProductFlavor> flavorList;
+    private final List<T> flavorList;
 
     /**
      * Create a ProductFlavorCombo.
      * @param flavors Lists of ProductFlavor.
      */
-    public ProductFlavorCombo(@NonNull GroupableProductFlavor... flavors) {
+    public ProductFlavorCombo(@NonNull T... flavors) {
         flavorList = ImmutableList.copyOf(flavors);
     }
 
-    public ProductFlavorCombo(@NonNull Iterable<GroupableProductFlavor> flavors) {
+    public ProductFlavorCombo(@NonNull Iterable<T> flavors) {
         flavorList = ImmutableList.copyOf(flavors);
     }
 
@@ -54,7 +58,7 @@
         if (name == null) {
             boolean first = true;
             StringBuilder sb = new StringBuilder();
-            for (ProductFlavor flavor : flavorList) {
+            for (T flavor : flavorList) {
                 if (first) {
                     sb.append(flavor.getName());
                     first = false;
@@ -68,7 +72,7 @@
     }
 
     @NonNull
-    public List<GroupableProductFlavor> getFlavorList() {
+    public List<T> getFlavorList() {
         return flavorList;
     }
 
@@ -79,20 +83,20 @@
      * @return A list of ProductFlavorCombo representing all combinations of ProductFlavors.
      */
     @NonNull
-    public static List<ProductFlavorCombo> createCombinations(
+    public static <S extends DimensionAware & Named> List<ProductFlavorCombo<S>> createCombinations(
             @Nullable List<String> flavorDimensions,
-            @NonNull Iterable<? extends GroupableProductFlavor> productFlavors) {
+            @NonNull Iterable<S> productFlavors) {
 
-        List <ProductFlavorCombo> result = Lists.newArrayList();
+        List <ProductFlavorCombo<S>> result = Lists.newArrayList();
         if (flavorDimensions == null || flavorDimensions.isEmpty()) {
-            for (GroupableProductFlavor flavor : productFlavors) {
-                result.add(new ProductFlavorCombo(ImmutableList.of(flavor)));
+            for (S flavor : productFlavors) {
+                result.add(new ProductFlavorCombo<S>(ImmutableList.of(flavor)));
             }
         } else {
             // need to group the flavor per dimension.
             // First a map of dimension -> list(ProductFlavor)
-            ArrayListMultimap<String, GroupableProductFlavor> map = ArrayListMultimap.create();
-            for (GroupableProductFlavor flavor : productFlavors) {
+            ArrayListMultimap<String, S> map = ArrayListMultimap.create();
+            for (S flavor : productFlavors) {
                 String flavorDimension = flavor.getDimension();
 
                 if (flavorDimension == null) {
@@ -109,7 +113,7 @@
             }
 
             createProductFlavorCombinations(result,
-                    new GroupableProductFlavor[flavorDimensions.size()],
+                    Lists.<S>newArrayListWithCapacity(flavorDimensions.size()),
                     0, flavorDimensions, map);
         }
         return result;
@@ -118,10 +122,9 @@
     /**
      * Remove all null reference from an array and create an ImmutableList it.
      */
-    private static ImmutableList<GroupableProductFlavor> filterNullFromArray(
-            GroupableProductFlavor[] flavors) {
-        ImmutableList.Builder<GroupableProductFlavor> builder = ImmutableList.builder();
-        for (GroupableProductFlavor flavor : flavors) {
+    private static ImmutableList<ProductFlavor> filterNullFromArray(ProductFlavor[] flavors) {
+        ImmutableList.Builder<ProductFlavor> builder = ImmutableList.builder();
+        for (ProductFlavor flavor : flavors) {
             if (flavor != null) {
                 builder.add(flavor);
             }
@@ -129,14 +132,14 @@
         return builder.build();
     }
 
-    private static void createProductFlavorCombinations(
-            List<ProductFlavorCombo> flavorGroups,
-            GroupableProductFlavor[] group,
+    private static <S extends DimensionAware & Named> void createProductFlavorCombinations(
+            List<ProductFlavorCombo<S>> flavorGroups,
+            List<S> group,
             int index,
             List<String> flavorDimensionList,
-            ListMultimap<String, GroupableProductFlavor> map) {
+            ListMultimap<String, S> map) {
         if (index == flavorDimensionList.size()) {
-            flavorGroups.add(new ProductFlavorCombo(filterNullFromArray(group)));
+            flavorGroups.add(new ProductFlavorCombo<S>(Iterables.filter(group, Predicates.notNull())));
             return;
         }
 
@@ -145,7 +148,7 @@
         String dimension = flavorDimensionList.get(index);
 
         // from our map, get all the possible flavors in that dimension.
-        List<GroupableProductFlavor> flavorList = map.get(dimension);
+        List<S> flavorList = map.get(dimension);
 
         // loop on all the flavors to add them to the current index and recursively fill the next
         // indices.
@@ -153,10 +156,11 @@
             throw new RuntimeException(String.format(
                     "No flavor is associated with flavor dimension '%1$s'.", dimension));
         } else {
-            for (GroupableProductFlavor flavor : flavorList) {
-                group[index] = flavor;
+            for (S flavor : flavorList) {
+                group.add(flavor);
                 createProductFlavorCombinations(
                         flavorGroups, group, index + 1, flavorDimensionList, map);
+                group.remove(group.size() - 1);
             }
         }
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy
deleted file mode 100644
index e8ad523..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy
+++ /dev/null
@@ -1,52 +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.build.gradle.internal
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.api.DefaultAndroidSourceSet
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultProductFlavor
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.api.Task
-/**
- * Class containing a ProductFlavor and associated data (sourcesets)
- */
-@CompileStatic
-public class ProductFlavorData<T extends DefaultProductFlavor> extends VariantDimensionData {
-    final T productFlavor
-    final Task assembleTask
-
-    ProductFlavorData(
-            @NonNull T productFlavor,
-            @NonNull DefaultAndroidSourceSet sourceSet,
-            @Nullable DefaultAndroidSourceSet androidTestSourceSet,
-            @Nullable DefaultAndroidSourceSet unitTestSourceSet,
-            @NonNull Project project) {
-        super(sourceSet, androidTestSourceSet, unitTestSourceSet, project)
-
-        this.productFlavor = productFlavor
-
-        if (!BuilderConstants.MAIN.equals(sourceSet.name)) {
-            assembleTask = project.tasks.create("assemble${sourceSet.name.capitalize()}")
-            assembleTask.description = "Assembles all ${sourceSet.name.capitalize()} builds."
-            assembleTask.setGroup("Build")
-        } else {
-            assembleTask = null
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.java
new file mode 100644
index 0000000..a7302bf
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.java
@@ -0,0 +1,63 @@
+/*
+ * 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.internal;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.builder.core.BuilderConstants;
+import com.android.utils.StringHelper;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.plugins.BasePlugin;
+
+/**
+ * Class containing a ProductFlavor and associated data (sourcesets)
+ */
+public class ProductFlavorData<T extends CoreProductFlavor> extends VariantDimensionData {
+    private final T productFlavor;
+    private final Task assembleTask;
+
+    ProductFlavorData(
+            @NonNull T productFlavor,
+            @NonNull DefaultAndroidSourceSet sourceSet,
+            @Nullable DefaultAndroidSourceSet androidTestSourceSet,
+            @Nullable DefaultAndroidSourceSet unitTestSourceSet,
+            @NonNull Project project) {
+        super(sourceSet, androidTestSourceSet, unitTestSourceSet, project);
+
+        this.productFlavor = productFlavor;
+
+        if (!BuilderConstants.MAIN.equals(sourceSet.getName())) {
+            String sourceSetName = StringHelper.capitalize(sourceSet.getName());
+            assembleTask = project.getTasks().create("assemble" + sourceSetName);
+            assembleTask.setDescription("Assembles all " + sourceSetName + " builds.");
+            assembleTask.setGroup(BasePlugin.BUILD_GROUP);
+        } else {
+            assembleTask = null;
+        }
+    }
+
+    public T getProductFlavor() {
+        return productFlavor;
+    }
+
+    public Task getAssembleTask() {
+        return assembleTask;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java
index a50fb0b..f106613 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java
@@ -21,6 +21,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.LibraryRequest;
 import com.android.builder.sdk.DefaultSdkLoader;
 import com.android.builder.sdk.PlatformLoader;
 import com.android.builder.sdk.SdkInfo;
@@ -29,6 +30,7 @@
 import com.android.sdklib.repository.FullRevision;
 import com.android.utils.ILogger;
 import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
 import com.google.common.io.Closeables;
 
 import org.gradle.api.Project;
@@ -38,6 +40,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.Collection;
 import java.util.Properties;
 
 /**
@@ -73,23 +76,19 @@
     }
 
     public void initTarget(
-            String targetHash,
-            FullRevision buildToolRevision,
+            @NonNull String targetHash,
+            @NonNull FullRevision buildToolRevision,
+            @NonNull Collection<LibraryRequest> usedLibraries,
             @NonNull AndroidBuilder androidBuilder) {
-        if (targetHash == null) {
-            throw new IllegalArgumentException("android.compileSdkVersion is missing!");
-        }
-
-        if (buildToolRevision == null) {
-            throw new IllegalArgumentException("android.buildToolsVersion is missing!");
-        }
+        Preconditions.checkNotNull(targetHash, "android.compileSdkVersion is missing!");
+        Preconditions.checkNotNull(buildToolRevision, "android.buildToolsVersion is missing!");
 
         SdkLoader sdkLoader = getSdkLoader();
 
         SdkInfo sdkInfo = sdkLoader.getSdkInfo(logger);
         TargetInfo targetInfo = sdkLoader.getTargetInfo(targetHash, buildToolRevision, logger);
 
-        androidBuilder.setTargetInfo(sdkInfo, targetInfo);
+        androidBuilder.setTargetInfo(sdkInfo, targetInfo, usedLibraries);
     }
 
     @Nullable
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.groovy
deleted file mode 100644
index 3d860a3..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.groovy
+++ /dev/null
@@ -1,2294 +0,0 @@
-/*
- * Copyright (C) 2014 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.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.OutputFile
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.coverage.JacocoInstrumentTask
-import com.android.build.gradle.internal.coverage.JacocoPlugin
-import com.android.build.gradle.internal.coverage.JacocoReportTask
-import com.android.build.gradle.internal.dependency.LibraryDependencyImpl
-import com.android.build.gradle.internal.dependency.ManifestDependencyImpl
-import com.android.build.gradle.internal.dependency.VariantDependencies
-import com.android.build.gradle.internal.dsl.AbiSplitOptions
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.publishing.ApkPublishArtifact
-import com.android.build.gradle.internal.publishing.MappingPublishArtifact
-import com.android.build.gradle.internal.publishing.MetadataPublishArtifact
-import com.android.build.gradle.internal.scope.AndroidTask
-import com.android.build.gradle.internal.scope.AndroidTaskRegistry
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.GlobalScope
-import com.android.build.gradle.internal.scope.VariantOutputScope
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.AndroidReportTask
-import com.android.build.gradle.internal.tasks.CheckManifest
-import com.android.build.gradle.internal.tasks.DependencyReportTask
-import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestLibraryTask
-import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
-import com.android.build.gradle.internal.tasks.FileSupplier
-import com.android.build.gradle.internal.tasks.GenerateApkDataTask
-import com.android.build.gradle.internal.tasks.InstallVariantTask
-import com.android.build.gradle.internal.tasks.MockableAndroidJarTask
-import com.android.build.gradle.internal.tasks.PrepareDependenciesTask
-import com.android.build.gradle.internal.tasks.SigningReportTask
-import com.android.build.gradle.internal.tasks.SourceSetsTask
-import com.android.build.gradle.internal.tasks.TestServerTask
-import com.android.build.gradle.internal.tasks.UninstallTask
-import com.android.build.gradle.internal.tasks.multidex.CreateMainDexList
-import com.android.build.gradle.internal.tasks.multidex.CreateManifestKeepList
-import com.android.build.gradle.internal.tasks.multidex.JarMergingTask
-import com.android.build.gradle.internal.tasks.multidex.RetraceMainDexList
-import com.android.build.gradle.internal.test.TestDataImpl
-import com.android.build.gradle.internal.test.report.ReportType
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.ApkVariantOutputData
-import com.android.build.gradle.internal.variant.ApplicationVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-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.AndroidJarTask
-import com.android.build.gradle.tasks.AndroidProGuardTask
-import com.android.build.gradle.tasks.CompatibleScreensManifest
-import com.android.build.gradle.tasks.Dex
-import com.android.build.gradle.tasks.GenerateBuildConfig
-import com.android.build.gradle.tasks.GenerateResValues
-import com.android.build.gradle.tasks.GenerateSplitAbiRes
-import com.android.build.gradle.tasks.JackTask
-import com.android.build.gradle.tasks.JillTask
-import com.android.build.gradle.tasks.Lint
-import com.android.build.gradle.tasks.MergeAssets
-import com.android.build.gradle.tasks.MergeManifests
-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.PackageSplitAbi
-import com.android.build.gradle.tasks.PackageSplitRes
-import com.android.build.gradle.tasks.PreCompilationVerificationTask
-import com.android.build.gradle.tasks.PreDex
-import com.android.build.gradle.tasks.PreprocessResourcesTask
-import com.android.build.gradle.tasks.ProcessAndroidResources
-import com.android.build.gradle.tasks.ProcessManifest
-import com.android.build.gradle.tasks.ProcessTestManifest
-import com.android.build.gradle.tasks.RenderscriptCompile
-import com.android.build.gradle.tasks.ShrinkResources
-import com.android.build.gradle.tasks.SplitZipAlign
-import com.android.build.gradle.tasks.ZipAlign
-import com.android.build.gradle.tasks.factory.JavaCompileConfigAction
-import com.android.build.gradle.tasks.factory.ProGuardTaskConfigAction
-import com.android.build.gradle.tasks.factory.ProcessJavaResConfigAction
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.dependency.LibraryDependency
-import com.android.builder.internal.testing.SimpleTestCallable
-import com.android.builder.model.AndroidProject
-import com.android.builder.testing.ConnectedDeviceProvider
-import com.android.builder.testing.TestData
-import com.android.builder.testing.api.DeviceProvider
-import com.android.builder.testing.api.TestServer
-import com.android.resources.Density
-import com.android.sdklib.AndroidTargetHash
-import com.android.sdklib.BuildToolInfo
-import com.android.sdklib.IAndroidTarget
-import com.google.common.base.CharMatcher
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableSet
-import com.google.common.collect.Iterators
-import com.google.common.collect.Lists
-import com.google.common.collect.Sets
-import groovy.transform.CompileDynamic
-import groovy.transform.CompileStatic
-import org.gradle.api.DefaultTask
-import org.gradle.api.GradleException
-import org.gradle.api.JavaVersion
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.internal.ConventionMapping
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.logging.Logger
-import org.gradle.api.logging.Logging
-import org.gradle.api.plugins.BasePlugin
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.reporting.ConfigurableReport
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.bundling.Jar
-import org.gradle.api.tasks.compile.AbstractCompile
-import org.gradle.api.tasks.compile.JavaCompile
-import org.gradle.api.tasks.testing.Test
-import org.gradle.api.tasks.testing.TestTaskReports
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-import proguard.gradle.ProGuardTask
-
-import static com.android.build.OutputFile.DENSITY
-import static com.android.builder.core.BuilderConstants.CONNECTED
-import static com.android.builder.core.BuilderConstants.DEVICE
-import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS
-import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS
-import static com.android.builder.core.BuilderConstants.FD_FLAVORS
-import static com.android.builder.core.BuilderConstants.FD_FLAVORS_ALL
-import static com.android.builder.core.BuilderConstants.FD_REPORTS
-import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.core.VariantType.DEFAULT
-import static com.android.builder.core.VariantType.UNIT_TEST
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static com.android.builder.model.AndroidProject.FD_OUTPUTS
-import static com.android.builder.model.AndroidProject.PROPERTY_APK_LOCATION
-import static com.android.sdklib.BuildToolInfo.PathId.ZIP_ALIGN
-
-/**
- * Manages tasks creation.
- */
-@CompileStatic
-abstract class TaskManager {
-
-    public static final String FILE_JACOCO_AGENT = 'jacocoagent.jar'
-
-    public static final String DEFAULT_PROGUARD_CONFIG_FILE = 'proguard-android.txt'
-
-    public final static String DIR_BUNDLES = "bundles";
-
-    public static final String INSTALL_GROUP = "Install"
-
-    public static final String BUILD_GROUP = BasePlugin.BUILD_GROUP
-
-    public static final String ANDROID_GROUP = "Android"
-
-    protected Project project
-
-    protected AndroidBuilder androidBuilder
-
-    private DependencyManager dependencyManager
-
-    protected SdkHandler sdkHandler
-
-    protected BaseExtension extension
-
-    protected ToolingModelBuilderRegistry toolingRegistry
-
-    private final GlobalScope globalScope
-
-    private AndroidTaskRegistry androidTasks = new AndroidTaskRegistry();
-
-    private Logger logger
-
-    protected boolean isNdkTaskNeeded = true
-
-    // Task names
-    // TODO: Convert to AndroidTask.
-    private static final String MAIN_PREBUILD = "preBuild"
-
-    private static final String UNINSTALL_ALL = "uninstallAll"
-
-    private static final String DEVICE_CHECK = "deviceCheck"
-
-    protected static final String CONNECTED_CHECK = "connectedCheck"
-
-    private static final String ASSEMBLE_ANDROID_TEST = "assembleAndroidTest"
-
-    private static final String SOURCE_SETS = "sourceSets"
-
-    private static final String LINT = "lint"
-
-    protected static final String LINT_COMPILE = "compileLint"
-
-    // Tasks
-    private Copy jacocoAgentTask
-
-    public MockableAndroidJarTask createMockableJar
-
-    public TaskManager(
-            Project project,
-            AndroidBuilder androidBuilder,
-            BaseExtension extension,
-            SdkHandler sdkHandler,
-            DependencyManager dependencyManager,
-            ToolingModelBuilderRegistry toolingRegistry) {
-        this.project = project
-        this.androidBuilder = androidBuilder
-        this.sdkHandler = sdkHandler
-        this.extension = extension
-        this.toolingRegistry = toolingRegistry
-        this.dependencyManager = dependencyManager
-        logger = Logging.getLogger(this.class)
-
-        globalScope = new GlobalScope(
-                project,
-                androidBuilder,
-                getArchivesBaseName(project),
-                extension,
-                sdkHandler,
-                toolingRegistry);
-    }
-
-    private boolean isVerbose() {
-        return project.logger.isEnabled(LogLevel.INFO)
-    }
-
-    private boolean isDebugLog() {
-        return project.logger.isEnabled(LogLevel.DEBUG)
-    }
-
-    /**
-     * Creates the tasks for a given BaseVariantData.
-     * @param variantData the non-null BaseVariantData.
-     * @param assembleTask an optional assembleTask to be used. If null, a new one is created.
-     */
-    abstract public void createTasksForVariantData(
-            @NonNull TaskFactory tasks,
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData)
-
-    public GlobalScope getGlobalScope() {
-        return globalScope
-    }
-
-    /**
-     * Returns a collection of buildables that creates native object.
-     *
-     * A buildable is considered to be any object that can be used as the argument to
-     * Task.dependsOn.  This could be a Task or a BuildableModelElement (e.g. BinarySpec).
-     */
-    protected Collection<Object> getNdkBuildable(BaseVariantData variantData) {
-        return Collections.singleton(variantData.ndkCompileTask)
-    }
-
-    /**
-     * Returns the directories of the NDK buildables.
-     */
-    protected Collection<File> getNdkOutputDirectories(BaseVariantData variantData) {
-        return Collections.singleton(variantData.ndkCompileTask.soFolder)
-    }
-
-    private BaseExtension getExtension() {
-        return extension
-    }
-
-    public void resolveDependencies(
-            @NonNull VariantDependencies variantDeps,
-            @Nullable VariantDependencies testedVariantDeps) {
-        dependencyManager.resolveDependencies(variantDeps, testedVariantDeps)
-    }
-
-    /**
-     * Create tasks before the evaluation (on plugin apply). This is useful for tasks that
-     * could be referenced by custom build logic.
-     */
-    public void createTasksBeforeEvaluate(TaskFactory tasks) {
-        tasks.create(UNINSTALL_ALL) {
-            it.description = "Uninstall all applications."
-            it.group = INSTALL_GROUP
-        }
-
-        tasks.create(DEVICE_CHECK) {
-            it.description = "Runs all device checks using Device Providers and Test Servers."
-            it.group = JavaBasePlugin.VERIFICATION_GROUP
-        }
-
-        tasks.create(CONNECTED_CHECK) {
-            it.description = "Runs all device checks on currently connected devices."
-            it.group = JavaBasePlugin.VERIFICATION_GROUP
-        }
-
-        tasks.create(MAIN_PREBUILD)
-
-        tasks.create(SOURCE_SETS, SourceSetsTask) {
-            it.description = "Prints out all the source sets defined in this project."
-            it.group = ANDROID_GROUP
-        }
-
-        tasks.create(ASSEMBLE_ANDROID_TEST) {
-            it.setGroup(BasePlugin.BUILD_GROUP);
-            it.setDescription("Assembles all the Test applications.");
-        }
-
-        tasks.create(LINT, Lint) {
-            it.description = "Runs lint on all variants."
-            it.group = JavaBasePlugin.VERIFICATION_GROUP
-            it.setLintOptions(getExtension().lintOptions)
-            it.setSdkHome(sdkHandler.getSdkFolder())
-            it.setToolingRegistry(toolingRegistry)
-        }
-        tasks.named(JavaBasePlugin.CHECK_TASK_NAME) {
-            it.dependsOn LINT
-        }
-        createLintCompileTask(tasks)
-    }
-
-    public void createMockableJarTask() {
-        createMockableJar = project.tasks.create("mockableAndroidJar", MockableAndroidJarTask)
-        createMockableJar.group = BUILD_GROUP
-        createMockableJar.description = "Creates a version of android.jar that's suitable for unit tests."
-
-        conventionMapping(createMockableJar).map("androidJar") {
-            new File(androidBuilder.target.getPath(IAndroidTarget.ANDROID_JAR))
-        }
-
-        CharMatcher safeCharacters = CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf('-.'))
-        String sdkName = safeCharacters.negate().replaceFrom(extension.compileSdkVersion, '-')
-
-        conventionMapping(createMockableJar).map("outputFile") {
-            project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/mockable-${sdkName}.jar")
-        }
-
-        conventionMapping(createMockableJar).map("returnDefaultValues") {
-            extension.testOptions.unitTests.returnDefaultValues
-        }
-    }
-
-    public void createMergeAppManifestsTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope variantScope) {
-
-        ApplicationVariantData appVariantData = variantScope.variantData as ApplicationVariantData
-        Set<String> screenSizes = appVariantData.getCompatibleScreens()
-
-        // loop on all outputs. The only difference will be the name of the task, and location
-        // of the generated manifest
-        for (BaseVariantOutputData vod : appVariantData.outputs) {
-            // create final var inside the loop to ensure the closures will work.
-            final BaseVariantOutputData variantOutputData = vod
-
-            VariantOutputScope scope = variantOutputData.getScope()
-
-            AndroidTask<CompatibleScreensManifest> csmTask = null;
-            if (vod.getMainOutputFile().getFilter(DENSITY) != null) {
-                csmTask = androidTasks.create(tasks,
-                        new CompatibleScreensManifest.ConfigAction(scope, screenSizes));
-                scope.compatibleScreensManifestTask = csmTask
-            }
-
-            scope.manifestProcessorTask = androidTasks.create(tasks,
-                    new MergeManifests.ConfigAction(scope))
-
-            if (csmTask != null) {
-                scope.manifestProcessorTask.dependsOn(tasks, csmTask)
-            }
-        }
-    }
-
-    @CompileDynamic
-    private static ConventionMapping conventionMapping(Task task) {
-        task.conventionMapping
-    }
-
-    /**
-     * Using the BaseConventionPlugin does not work for archivesBaseName dynamic attribute,
-     * revert to a dynamic property invocation.
-     */
-    @CompileDynamic
-    private static String getArchivesBaseName(Project project){
-        project.archivesBaseName
-    }
-
-    public void createMergeLibManifestsTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-
-        AndroidTask<ProcessManifest> processManifest = androidTasks.create(tasks,
-                new ProcessManifest.ConfigAction(scope));
-
-        processManifest.dependsOn(tasks, scope.variantData.prepareDependenciesTask)
-
-        BaseVariantOutputData variantOutputData = scope.variantData.outputs.get(0)
-        variantOutputData.scope.manifestProcessorTask = processManifest
-    }
-
-    protected void createProcessTestManifestTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-
-        AndroidTask<ProcessTestManifest> processTestManifestTask = androidTasks.create(tasks,
-                new ProcessTestManifest.ConfigAction(scope));
-
-        processTestManifestTask.dependsOn(tasks, scope.variantData.prepareDependenciesTask)
-
-        BaseVariantOutputData variantOutputData = scope.variantData.outputs.get(0)
-        variantOutputData.scope.manifestProcessorTask = processTestManifestTask
-    }
-
-    public void createRenderscriptTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        scope.renderscriptCompileTask = androidTasks.create(tasks,
-                new RenderscriptCompile.ConfigAction(scope))
-
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        GradleVariantConfiguration config = variantData.variantConfiguration
-        // get single output for now.
-        BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
-
-        scope.renderscriptCompileTask.dependsOn(tasks, variantData.prepareDependenciesTask)
-        if (config.type.isForTesting()) {
-            scope.renderscriptCompileTask.dependsOn(tasks, variantOutputData.scope.manifestProcessorTask)
-        } else {
-            scope.renderscriptCompileTask.dependsOn(tasks, scope.checkManifestTask)
-        }
-
-
-        scope.resourceGenTask.dependsOn(tasks, scope.renderscriptCompileTask)
-        // only put this dependency if rs will generate Java code
-        if (!config.renderscriptNdkModeEnabled) {
-            scope.sourceGenTask.dependsOn(tasks, scope.renderscriptCompileTask)
-        }
-    }
-
-    public AndroidTask<MergeResources> createMergeResourcesTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        return basicCreateMergeResourcesTask(
-                tasks,
-                scope,
-                "merge",
-                null /*outputLocation*/,
-                true /*includeDependencies*/,
-                true /*process9patch*/)
-    }
-
-    public AndroidTask<MergeResources> basicCreateMergeResourcesTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope,
-            @NonNull String taskNamePrefix,
-            @Nullable File outputLocation,
-            final boolean includeDependencies,
-            final boolean process9Patch) {
-        scope.mergeResourcesTask = androidTasks.create(tasks,
-                new MergeResources.ConfigAction(
-                        scope,
-                        taskNamePrefix,
-                        outputLocation,
-                        includeDependencies,
-                        process9Patch));
-        scope.mergeResourcesTask.dependsOn(tasks,
-                scope.variantData.prepareDependenciesTask,
-                scope.resourceGenTask)
-        return scope.mergeResourcesTask;
-    }
-
-
-    public void createMergeAssetsTask(TaskFactory tasks, VariantScope scope) {
-        scope.mergeAssetsTask = androidTasks.create(tasks, new MergeAssets.ConfigAction(scope))
-        scope.mergeAssetsTask.dependsOn(tasks,
-                scope.variantData.prepareDependenciesTask,
-                scope.assetGenTask)
-    }
-
-    public void createBuildConfigTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
-        scope.generateBuildConfigTask = androidTasks.create(tasks, new GenerateBuildConfig.ConfigAction(scope))
-
-        scope.sourceGenTask.dependsOn(tasks, scope.generateBuildConfigTask.name);
-        if (scope.variantConfiguration.type.isForTesting()) {
-            // in case of a test project, the manifest is generated so we need to depend
-            // on its creation.
-
-            // For test apps there should be a single output, so we get it.
-            BaseVariantOutputData variantOutputData = scope.variantData.outputs.get(0)
-
-            scope.generateBuildConfigTask.dependsOn(tasks, variantOutputData.scope.manifestProcessorTask)
-        } else {
-            scope.generateBuildConfigTask.dependsOn(tasks, scope.checkManifestTask)
-        }
-    }
-
-    public void createGenerateResValuesTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        AndroidTask<GenerateResValues> generateResValuesTask = androidTasks.create(
-                tasks, new GenerateResValues.ConfigAction(scope))
-        scope.resourceGenTask.dependsOn(tasks, generateResValuesTask)
-    }
-
-    public void createPreprocessResourcesTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        String variantName = variantData.variantConfiguration.fullName.capitalize()
-        int minSdk = variantData.variantConfiguration.minSdkVersion.getApiLevel()
-
-        if (extension.preprocessResources  && minSdk < PreprocessResourcesTask.MIN_SDK) {
-            scope.preprocessResourcesTask = androidTasks.create(
-                    tasks,
-                    "preprocess${variantName}Resources",
-                    PreprocessResourcesTask) { PreprocessResourcesTask preprocessResourcesTask ->
-                variantData.preprocessResourcesTask = preprocessResourcesTask
-
-                preprocessResourcesTask.dependsOn variantData.mergeResourcesTask
-
-                preprocessResourcesTask.mergedResDirectory =
-                        scope.getMergeResourcesOutputDir()
-                preprocessResourcesTask.generatedResDirectory =
-                        project.file(
-                                "$project.buildDir/${FD_GENERATED}/res/pngs/${variantData.variantConfiguration.dirName}")
-                preprocessResourcesTask.outputResDirectory =
-                        project.file(
-                                "$project.buildDir/${FD_INTERMEDIATES}/res/preprocessed/${variantData.variantConfiguration.dirName}")
-                preprocessResourcesTask.incrementalFolder =
-                        project.file(
-                                "$project.buildDir/${FD_INTERMEDIATES}/incremental/preprocessResourcesTask/${variantData.variantConfiguration.dirName}")
-
-                // TODO: configure this in the extension.
-                preprocessResourcesTask.densitiesToGenerate = [Density.HIGH, Density.XHIGH]
-            }
-        }
-    }
-
-    public void createProcessResTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope,
-            boolean generateResourcePackage) {
-        createProcessResTask(
-                tasks,
-                scope,
-                new File(globalScope.getBuildDir(), FD_INTERMEDIATES + "/symbols/" +
-                        scope.variantData.getVariantConfiguration().getDirName()),
-                generateResourcePackage);
-    }
-
-    public void createProcessResTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope,
-            @Nullable File symbolLocation,
-            boolean generateResourcePackage) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData()
-
-        variantData.calculateFilters(scope.getGlobalScope().getExtension().getSplits());
-
-        // loop on all outputs. The only difference will be the name of the task, and location
-        // of the generated data.
-        for (BaseVariantOutputData vod : (List<? extends BaseVariantOutputData>)variantData.outputs) {
-            // create final var inside the loop to ensure the closures will work.
-            final VariantOutputScope variantOutputScope = vod.scope;
-
-            variantOutputScope.processResourcesTask = androidTasks.create(tasks,
-                    new ProcessAndroidResources.ConfigAction(variantOutputScope, symbolLocation, generateResourcePackage));
-            variantOutputScope.processResourcesTask.dependsOn(tasks,
-                    variantOutputScope.manifestProcessorTask,
-                    scope.mergeResourcesTask,
-                    scope.mergeAssetsTask)
-
-            // TODO: Make it non-optional once this is not behind a flag.
-            variantOutputScope.processResourcesTask.optionalDependsOn(tasks,
-                    scope.preprocessResourcesTask)
-
-            if (vod.getMainOutputFile().getFilter(DENSITY) == null) {
-                scope.generateRClassTask = variantOutputScope.processResourcesTask
-                scope.sourceGenTask.optionalDependsOn(tasks, variantOutputScope.processResourcesTask)
-            }
-        }
-    }
-
-    /**
-     * Creates the split resources packages task if necessary. AAPT will produce split packages
-     * for all --split provided parameters. These split packages should be signed and moved
-     * unchanged to the APK build output directory.
-     * @param variantData the variant configuration.
-     */
-    public void createSplitResourcesTasks(@NonNull VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-
-        assert variantData.getSplitHandlingPolicy() ==
-                BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY;
-
-        VariantConfiguration config = variantData.variantConfiguration
-        Set<String> densityFilters = variantData.getFilters(OutputFile.FilterType.DENSITY)
-        Set<String> abiFilters = variantData.getFilters(OutputFile.FilterType.ABI);
-        Set<String> languageFilters = variantData.getFilters(OutputFile.FilterType.LANGUAGE);
-
-        def outputs = variantData.outputs;
-        if (outputs.size() != 1) {
-            throw new RuntimeException("In release 21 and later, there can be only one main APK, " +
-                    "found " + outputs.size());
-        }
-
-        BaseVariantOutputData variantOutputData = outputs.get(0);
-        VariantOutputScope variantOutputScope = variantOutputData.scope
-        variantOutputData.packageSplitResourcesTask =
-                project.tasks.create("package${config.fullName.capitalize()}SplitResources",
-                        PackageSplitRes);
-        variantOutputData.packageSplitResourcesTask.inputDirectory =
-                variantOutputScope.getProcessResourcePackageOutputFile().getParentFile()
-        variantOutputData.packageSplitResourcesTask.densitySplits = densityFilters
-        variantOutputData.packageSplitResourcesTask.languageSplits = languageFilters
-        variantOutputData.packageSplitResourcesTask.outputBaseName = config.baseName
-        variantOutputData.packageSplitResourcesTask.signingConfig =
-                (SigningConfig) config.signingConfig
-        variantOutputData.packageSplitResourcesTask.outputDirectory =
-                new File("$project.buildDir/${FD_INTERMEDIATES}/splits/${config.dirName}")
-        variantOutputData.packageSplitResourcesTask.androidBuilder = androidBuilder
-        variantOutputData.packageSplitResourcesTask.dependsOn variantOutputScope.processResourcesTask.name
-
-        SplitZipAlign zipAlign = project.tasks.
-                create("zipAlign${config.fullName.capitalize()}SplitPackages", SplitZipAlign)
-        conventionMapping(zipAlign).map("zipAlignExe") {
-            String path = androidBuilder.targetInfo?.buildTools?.getPath(ZIP_ALIGN)
-            if (path != null) {
-                return new File(path)
-            }
-
-            return null
-        }
-
-        zipAlign.outputDirectory = new File("$project.buildDir/outputs/apk")
-        conventionMapping(zipAlign).map("densityOrLanguageInputFiles") {
-            return  variantOutputData.packageSplitResourcesTask.getOutputFiles()
-        }
-        zipAlign.outputBaseName = config.baseName
-        zipAlign.abiFilters = abiFilters
-        zipAlign.languageFilters = languageFilters
-        zipAlign.densityFilters = densityFilters
-        File metadataDirectory = new File(zipAlign.outputDirectory.getParentFile(), "metadata")
-        zipAlign.apkMetadataFile = new File(metadataDirectory, "${config.fullName}.mtd")
-        ((ApkVariantOutputData) variantOutputData).splitZipAlign = zipAlign
-        zipAlign.dependsOn(variantOutputData.packageSplitResourcesTask)
-    }
-
-    public void createSplitAbiTasks(@NonNull VariantScope scope) {
-        ApplicationVariantData variantData = (ApplicationVariantData) scope.getVariantData()
-
-        assert variantData.getSplitHandlingPolicy() ==
-                BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY;
-
-        VariantConfiguration config = variantData.variantConfiguration
-        Set<String> filters = AbiSplitOptions.getAbiFilters(
-                getExtension().getSplits().getAbiFilters())
-        if (filters.isEmpty()) {
-            return;
-        }
-        def outputs = variantData.outputs;
-        if (outputs.size() != 1) {
-            throw new RuntimeException("In release 21 and later, there can be only one main APK, " +
-                    "found " + outputs.size());
-        }
-
-        BaseVariantOutputData variantOutputData = outputs.get(0);
-        // first create the split APK resources.
-        GenerateSplitAbiRes generateSplitAbiRes = project.tasks.
-                create("generate${config.fullName.capitalize()}SplitAbiRes",
-                        GenerateSplitAbiRes)
-        generateSplitAbiRes.androidBuilder = androidBuilder
-
-        generateSplitAbiRes.outputDirectory =
-                new File("$project.buildDir/${FD_INTERMEDIATES}/abi/${config.dirName}")
-        generateSplitAbiRes.splits = filters
-        generateSplitAbiRes.outputBaseName = config.baseName
-        generateSplitAbiRes.applicationId = config.getApplicationId()
-        generateSplitAbiRes.versionCode = config.getVersionCode()
-        generateSplitAbiRes.versionName = config.getVersionName()
-        generateSplitAbiRes.debuggable = {
-            config.buildType.debuggable
-        }
-        conventionMapping(generateSplitAbiRes).map("aaptOptions") {
-            getExtension().aaptOptions
-        }
-        generateSplitAbiRes.dependsOn variantOutputData.scope.processResourcesTask.name
-
-        // then package those resources with the appropriate JNI libraries.
-        variantOutputData.packageSplitAbiTask =
-                project.tasks.create("package${config.fullName.capitalize()}SplitAbi",
-                        PackageSplitAbi);
-        variantOutputData.packageSplitAbiTask.inputFiles = generateSplitAbiRes.getOutputFiles()
-        variantOutputData.packageSplitAbiTask.splits = filters
-        variantOutputData.packageSplitAbiTask.outputBaseName = config.baseName
-        variantOutputData.packageSplitAbiTask.signingConfig =
-                (SigningConfig) config.signingConfig
-        variantOutputData.packageSplitAbiTask.outputDirectory =
-                new File("$project.buildDir/${FD_INTERMEDIATES}/splits/${config.dirName}")
-        variantOutputData.packageSplitAbiTask.androidBuilder = androidBuilder
-        variantOutputData.packageSplitAbiTask.dependsOn generateSplitAbiRes
-        variantOutputData.packageSplitAbiTask.dependsOn getNdkBuildable(variantData)
-
-        conventionMapping(variantOutputData.packageSplitAbiTask).map("jniFolders") {
-            getJniFolders(variantData);
-        }
-        conventionMapping(variantOutputData.packageSplitAbiTask).
-                map("jniDebuggable") { config.buildType.jniDebuggable }
-        conventionMapping(variantOutputData.packageSplitAbiTask).
-                map("packagingOptions") { getExtension().packagingOptions }
-
-        ((ApkVariantOutputData) variantOutputData).splitZipAlign.abiInputFiles.addAll(
-                variantOutputData.packageSplitAbiTask.getOutputFiles())
-
-        ((ApkVariantOutputData) variantOutputData).splitZipAlign.dependsOn variantOutputData.packageSplitAbiTask
-    }
-
-    /**
-     * Calculate the list of folders that can contain jni artifacts for this variant.
-     * @param variantData the variant
-     * @return a potentially empty list of directories that exist or not and that may contains
-     * native resources.
-     */
-    @NonNull
-    public Set<File> getJniFolders(@NonNull ApkVariantData variantData) {
-        VariantConfiguration config = variantData.variantConfiguration
-        // for now only the project's compilation output.
-        Set<File> set = Sets.newHashSet()
-        set.addAll(getNdkOutputDirectories(variantData))
-        set.addAll(variantData.renderscriptCompileTask.libOutputDir)
-        set.addAll(config.libraryJniFolders)
-        set.addAll(config.jniLibsList)
-
-        if (config.mergedFlavor.renderscriptSupportModeEnabled) {
-            File rsLibs = androidBuilder.getSupportNativeLibFolder()
-            if (rsLibs != null && rsLibs.isDirectory()) {
-                set.add(rsLibs);
-            }
-        }
-
-        return set
-    }
-
-    public void createProcessJavaResTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        scope.processJavaResourcesTask = androidTasks.create(
-                tasks, new ProcessJavaResConfigAction(scope));
-    }
-
-    public void createAidlTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
-        scope.aidlCompileTask = androidTasks.create(tasks,
-                new AidlCompile.ConfigAction(scope));
-        scope.sourceGenTask.dependsOn(tasks, scope.aidlCompileTask)
-        scope.aidlCompileTask.dependsOn(tasks, scope.variantData.prepareDependenciesTask)
-    }
-
-    public void createJackAndUnitTestVerificationTask(
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> testedVariantData) {
-
-        PreCompilationVerificationTask verificationTask = project.tasks.create(
-                "preCompile${variantData.variantConfiguration.fullName.capitalize()}Java",
-                PreCompilationVerificationTask)
-        verificationTask.useJack = testedVariantData.getVariantConfiguration().getUseJack()
-        verificationTask.testSourceFiles = variantData.getJavaSources()
-        variantData.javaCompileTask.dependsOn verificationTask
-    }
-
-    public void createJavaCompileTask(
-            @NonNull final TaskFactory tasks,
-            @NonNull final VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData;
-        AndroidTask<JavaCompile> javaCompileTask = androidTasks.create(tasks,
-                new JavaCompileConfigAction(scope));
-        scope.javaCompileTask = javaCompileTask;
-
-        scope.compileTask.dependsOn(tasks, javaCompileTask);
-
-        javaCompileTask.optionalDependsOn(tasks, scope.sourceGenTask)
-        javaCompileTask.dependsOn(tasks,
-                scope.getVariantData().prepareDependenciesTask,
-                scope.processJavaResourcesTask);
-
-        // TODO - dependency information for the compile classpath is being lost.
-        // Add a temporary approximation
-        javaCompileTask.dependsOn(tasks,
-                scope.getVariantData().getVariantDependency().getCompileConfiguration()
-                        .getBuildDependencies());
-
-        if (variantData.getType().isForTesting()) {
-            BaseVariantData testedVariantData =
-                    ((TestVariantData) variantData).getTestedVariantData() as BaseVariantData
-            javaCompileTask.dependsOn(tasks,
-                    testedVariantData.javaCompileTask ?: testedVariantData.scope.javaCompileTask)
-        }
-
-
-        // Create jar task for uses by external modules.
-        if (variantData.variantDependency.classesConfiguration != null) {
-            tasks.create("package${variantData.variantConfiguration.fullName.capitalize()}JarArtifact", Jar) { Jar jar ->
-                variantData.classesJarTask = jar
-                jar.dependsOn javaCompileTask.name
-
-                // add the class files (whether they are instrumented or not.
-                jar.from({ scope.getJavaOutputDir() })
-
-                jar.destinationDir = scope.getJavaOutputDir();
-                jar.archiveName = "classes.jar"
-            }
-        }
-    }
-
-    public void createGenerateMicroApkDataTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope,
-            @NonNull Configuration config) {
-        AndroidTask<GenerateApkDataTask> generateMicroApkTask = androidTasks.create(tasks,
-                new GenerateApkDataTask.ConfigAction(scope, config));
-        generateMicroApkTask.dependsOn tasks, config
-
-        // the merge res task will need to run after this one.
-        scope.resourceGenTask.dependsOn(tasks, generateMicroApkTask);
-    }
-
-    public void createNdkTasks(
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
-        NdkCompile ndkCompile = project.tasks.create(
-                "compile${variantData.variantConfiguration.fullName.capitalize()}Ndk",
-                NdkCompile)
-
-        ndkCompile.dependsOn variantData.preBuildTask
-
-        ndkCompile.androidBuilder = androidBuilder
-        ndkCompile.ndkDirectory = sdkHandler.getNdkFolder()
-        variantData.ndkCompileTask = ndkCompile
-        variantData.compileTask.dependsOn variantData.ndkCompileTask
-
-        GradleVariantConfiguration variantConfig = variantData.variantConfiguration
-
-        if (variantConfig.mergedFlavor.renderscriptNdkModeEnabled) {
-            ndkCompile.ndkRenderScriptMode = true
-            ndkCompile.dependsOn variantData.renderscriptCompileTask
-        } else {
-            ndkCompile.ndkRenderScriptMode = false
-        }
-
-        conventionMapping(ndkCompile).map("sourceFolders") {
-            List<File> sourceList = variantConfig.jniSourceList
-            if (variantConfig.mergedFlavor.renderscriptNdkModeEnabled) {
-                sourceList.add(variantData.renderscriptCompileTask.sourceOutputDir)
-            }
-
-            return sourceList
-        }
-
-        conventionMapping(ndkCompile).map("generatedMakefile") {
-            project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/Android.mk")
-        }
-
-        conventionMapping(ndkCompile).map("ndkConfig") { variantConfig.ndkConfig }
-
-        conventionMapping(ndkCompile).map("debuggable") {
-            variantConfig.buildType.jniDebuggable
-        }
-
-        conventionMapping(ndkCompile).map("objFolder") {
-            project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/obj")
-        }
-        conventionMapping(ndkCompile).map("soFolder") {
-            project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/lib")
-        }
-    }
-
-    /**
-     * Creates the tasks to build unit tests.
-     *
-     * @param variantData the test variant
-     */
-    void createUnitTestVariantTasks(
-            @NonNull TaskFactory tasks,
-            @NonNull TestVariantData variantData) {
-        BaseVariantData testedVariantData = variantData.getTestedVariantData() as BaseVariantData
-        variantData.assembleVariantTask.dependsOn createMockableJar
-        VariantScope variantScope = variantData.getScope()
-
-        createPreBuildTasks(tasks, variantScope)
-        createProcessJavaResTask(tasks, variantScope)
-        createCompileAnchorTask(tasks, variantScope)
-        createJavaCompileTask(tasks, variantScope);
-        createJackAndUnitTestVerificationTask(variantData, testedVariantData)
-        createUnitTestTask(tasks, variantData)
-
-        // This hides the assemble unit test task from the task list.
-        variantData.assembleVariantTask.group = null
-    }
-
-    /**
-     * Creates the tasks to build android tests.
-     *
-     * @param variantData the test variant
-     */
-    public void createAndroidTestVariantTasks(
-            @NonNull TaskFactory tasks,
-            @NonNull TestVariantData variantData) {
-        VariantScope variantScope = variantData.getScope()
-
-        BaseVariantData<? extends BaseVariantOutputData> testedVariantData =
-                variantData.
-                        getTestedVariantData() as BaseVariantData<? extends BaseVariantOutputData>
-
-        // get single output for now (though this may always be the case for tests).
-        BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
-        BaseVariantOutputData testedVariantOutputData = testedVariantData.outputs.get(0)
-
-        createAnchorTasks(tasks, variantScope)
-
-        // Add a task to process the manifest
-        createProcessTestManifestTask(tasks, variantScope)
-
-        // Add a task to create the res values
-        createGenerateResValuesTask(tasks, variantScope)
-
-        // Add a task to compile renderscript files.
-        createRenderscriptTask(tasks, variantScope)
-
-        // Add a task to merge the resource folders
-        createMergeResourcesTask(tasks, variantScope)
-
-        // Add a task to merge the assets folders
-        createMergeAssetsTask(tasks, variantScope)
-
-        if (testedVariantData.variantConfiguration.type == VariantType.LIBRARY) {
-            // in this case the tested library must be fully built before test can be built!
-            if (testedVariantOutputData.assembleTask != null) {
-                variantOutputData.scope.manifestProcessorTask.dependsOn(
-                        tasks, testedVariantOutputData.assembleTask)
-                variantScope.mergeResourcesTask.dependsOn(tasks, testedVariantOutputData.assembleTask)
-            }
-        }
-
-        // Add a task to create the BuildConfig class
-        createBuildConfigTask(tasks, variantScope)
-
-        createPreprocessResourcesTask(tasks, variantScope)
-
-        // Add a task to generate resource source files
-        createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/)
-
-        // process java resources
-        createProcessJavaResTask(tasks, variantScope)
-
-        createAidlTask(tasks, variantScope)
-
-        // Add NDK tasks
-        if (isNdkTaskNeeded) {
-            createNdkTasks(variantData)
-        }
-        variantScope.setNdkBuildable(getNdkBuildable(variantData))
-        variantScope.setNdkOutputDirectories(getNdkOutputDirectories(variantData))
-
-        // Add a task to compile the test application
-        if (variantData.getVariantConfiguration().useJack) {
-            createJackTask(variantData, testedVariantData);
-        } else {
-            //createJavaCompileTask(variantData, testedVariantData)
-            createJavaCompileTask(tasks, variantScope)
-            createPostCompilationTasks(tasks, variantScope)
-        }
-
-        createPackagingTask(tasks, variantScope, false /*publishApk*/)
-
-        tasks.named(ASSEMBLE_ANDROID_TEST) {
-            it.dependsOn variantOutputData.assembleTask
-        }
-
-        createConnectedTestForVariantData(tasks, variantData, TestType.APPLICATION)
-    }
-
-    // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
-    private void createLintCompileTask(TaskFactory tasks) {
-        tasks.create(LINT_COMPILE, Task) { Task lintCompile ->
-            File outputDir = new File("$project.buildDir/${FD_INTERMEDIATES}/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<? extends BaseVariantOutputData> baseVariantData) {
-        // Only create lint targets for variants like debug and release, not debugTest
-        VariantConfiguration config = baseVariantData.variantConfiguration
-        // TODO: re-enable with Jack when possible
-        return !config.getType().isForTesting() && !config.useJack;
-    }
-
-    // Add tasks for running lint on individual variants. We've already added a
-    // lint task earlier which runs on all variants.
-    public void createLintTasks(TaskFactory tasks, VariantScope scope) {
-        final BaseVariantData<? extends BaseVariantOutputData> baseVariantData = scope.variantData
-        if (!isLintVariant(baseVariantData)) {
-            return;
-        }
-
-        // wire the main lint task dependency.
-        tasks.named(LINT) {
-            it.dependsOn(LINT_COMPILE)
-            if (baseVariantData.javaCompileTask != null) {
-                it.dependsOn(baseVariantData.javaCompileTask)
-            }
-            if (scope.javaCompileTask != null) {
-                it.dependsOn(scope.javaCompileTask.name)
-            }
-        }
-
-        AndroidTask<Lint> variantLintCheck = androidTasks.create(
-                tasks, new Lint.ConfigAction(scope))
-        variantLintCheck.dependsOn(tasks, LINT_COMPILE)
-        variantLintCheck.optionalDependsOn(tasks,
-                baseVariantData.javaCompileTask,
-                scope.javaCompileTask)
-    }
-
-    private void createLintVitalTask(@NonNull ApkVariantData variantData) {
-        assert getExtension().lintOptions.checkReleaseBuilds
-        // TODO: re-enable with Jack when possible
-        if (!variantData.variantConfiguration.buildType.debuggable &&
-                !variantData.variantConfiguration.useJack) {
-            String variantName = variantData.variantConfiguration.fullName
-            def capitalizedVariantName = variantName.capitalize()
-            def taskName = "lintVital" + capitalizedVariantName
-            Lint lintReleaseCheck = project.tasks.create(taskName, Lint)
-            // TODO: Make this task depend on lintCompile too (resolve initialization order first)
-            optionalDependsOn(lintReleaseCheck, variantData.javaCompileTask)
-            lintReleaseCheck.setLintOptions(getExtension().lintOptions)
-            lintReleaseCheck.setSdkHome(sdkHandler.getSdkFolder())
-            lintReleaseCheck.setVariantName(variantName)
-            lintReleaseCheck.setToolingRegistry(toolingRegistry)
-            lintReleaseCheck.setFatalOnly(true)
-            lintReleaseCheck.description = "Runs lint on just the fatal issues in the " +
-                    capitalizedVariantName + " build."
-            //variantData.assembleVariantTask.dependsOn lintReleaseCheck
-
-            // If lint is being run, we do not need to run lint vital.
-            project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
-                if (taskGraph.hasTask(LINT)) {
-                    lintReleaseCheck.setEnabled(false)
-                }
-            }
-        }
-    }
-
-    private void createUnitTestTask(@NonNull TaskFactory tasks, @NonNull TestVariantData variantData) {
-        BaseVariantData testedVariantData = variantData.testedVariantData as BaseVariantData
-
-        Test runTestsTask = project.tasks.create(
-                UNIT_TEST.prefix +
-                        testedVariantData.variantConfiguration.fullName.capitalize(),
-                Test)
-        runTestsTask.group = JavaBasePlugin.VERIFICATION_GROUP
-        runTestsTask.description = "Run unit tests for the " +
-                "$testedVariantData.variantConfiguration.fullName build."
-
-        fixTestTaskSources(runTestsTask)
-
-        runTestsTask.dependsOn variantData.assembleVariantTask
-
-        AbstractCompile testCompileTask = variantData.javaCompileTask
-        runTestsTask.testClassesDir = testCompileTask.destinationDir
-
-        conventionMapping(runTestsTask).map("classpath") {
-            project.files(
-                    testCompileTask.classpath,
-                    testCompileTask.outputs.files,
-                    variantData.processJavaResourcesTask.outputs,
-                    testedVariantData.processJavaResourcesTask.outputs,
-                    androidBuilder.bootClasspath.findAll {
-                        it.name != SdkConstants.FN_FRAMEWORK_LIBRARY
-                    },
-                    // Mockable JAR is last, to make sure you can shadow the classes with
-                    // dependencies.
-                    createMockableJar.outputFile)
-        }
-
-        // Put the variant name in the report path, so that different testing tasks don't
-        // overwrite each other's reports.
-        TestTaskReports testTaskReports = runTestsTask.reports
-        for (ConfigurableReport report in [testTaskReports.junitXml, testTaskReports.html]) {
-            report.destination = new File(report.destination, testedVariantData.name)
-        }
-
-        tasks.named(JavaPlugin.TEST_TASK_NAME) { Task test ->
-            test.dependsOn runTestsTask
-        }
-
-        extension.testOptions.unitTests.applyConfiguration(runTestsTask)
-    }
-
-    @CompileDynamic
-    private static void fixTestTaskSources(@NonNull Test testTask) {
-        // We are running in afterEvaluate, so the JavaBasePlugin has already added a
-        // callback to add test classes to the list of source files of the newly created task.
-        // The problem is that we haven't configured the test classes yet (JavaBasePlugin
-        // assumes all Test tasks are fully configured at this point), so we have to remove the
-        // "directory null" entry from source files and add the right value.
-        //
-        // This is an ugly hack, since we assume sourceFiles is an instance of
-        // DefaultConfigurableFileCollection.
-        testTask.inputs.sourceFiles.from.clear()
-    }
-
-    public void createTopLevelTestTasks(TaskFactory tasks, boolean hasFlavors) {
-        List<String> reportTasks = Lists.newArrayListWithExpectedSize(2)
-
-        List<DeviceProvider> providers = getExtension().deviceProviders
-
-        String connectedRootName = "${CONNECTED}${ANDROID_TEST.suffix}"
-        String defaultReportsDir = "$project.buildDir/$FD_REPORTS/$FD_ANDROID_TESTS"
-        String defaultResultsDir = "$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
-
-        // If more than one flavor, create a report aggregator task and make this the parent
-        // task for all new connected tasks.  Otherwise, create a top level connectedAndroidTest
-        // DefaultTask.
-        if (hasFlavors) {
-            tasks.create(connectedRootName, AndroidReportTask) { AndroidReportTask mainConnectedTask ->
-                mainConnectedTask.group = JavaBasePlugin.VERIFICATION_GROUP
-                mainConnectedTask.description =
-                        "Installs and runs instrumentation tests for all flavors on connected devices."
-                mainConnectedTask.reportType = ReportType.MULTI_FLAVOR
-                conventionMapping(mainConnectedTask).map("resultsDir") {
-                    String rootLocation = extension.testOptions.resultsDir ?: defaultResultsDir
-                    project.file("$rootLocation/connected/$FD_FLAVORS_ALL")
-                }
-                conventionMapping(mainConnectedTask).map("reportsDir") {
-                    String rootLocation = extension.testOptions.reportDir ?: defaultReportsDir
-                    project.file("$rootLocation/connected/$FD_FLAVORS_ALL")
-                }
-
-            }
-            reportTasks.add(connectedRootName)
-        } else {
-            tasks.create(connectedRootName) { Task connectedTask ->
-                connectedTask.group = JavaBasePlugin.VERIFICATION_GROUP
-                connectedTask.description =
-                        "Installs and runs instrumentation tests for all flavors on connected devices."
-            }
-        }
-        tasks.named(CONNECTED_CHECK) {
-            it.dependsOn connectedRootName
-        }
-
-        String mainProviderTaskName =  "${DEVICE}${ANDROID_TEST.suffix}"
-        // if more than one provider tasks, either because of several flavors, or because of
-        // more than one providers, then create an aggregate report tasks for all of them.
-        if (providers.size() > 1 || hasFlavors) {
-            tasks.create(mainProviderTaskName, AndroidReportTask) { AndroidReportTask mainProviderTask ->
-                mainProviderTask.group = JavaBasePlugin.VERIFICATION_GROUP
-                mainProviderTask.description =
-                        "Installs and runs instrumentation tests using all Device Providers."
-                mainProviderTask.reportType = ReportType.MULTI_FLAVOR
-
-                conventionMapping(mainProviderTask).map("resultsDir") {
-                    String rootLocation = extension.testOptions.resultsDir ?: defaultResultsDir
-
-                    project.file("$rootLocation/devices/$FD_FLAVORS_ALL")
-                }
-                conventionMapping(mainProviderTask).map("reportsDir") {
-                    String rootLocation = extension.testOptions.reportDir ?: defaultReportsDir
-                    project.file("$rootLocation/devices/$FD_FLAVORS_ALL")
-                }
-            }
-            reportTasks.add(mainProviderTaskName)
-        } else {
-            tasks.create(mainProviderTaskName) { Task providerTask ->
-                providerTask.group = JavaBasePlugin.VERIFICATION_GROUP
-                providerTask.description =
-                        "Installs and runs instrumentation tests using all Device Providers."
-            }
-        }
-
-        tasks.named(DEVICE_CHECK) {
-            it.dependsOn mainProviderTaskName
-        }
-
-        // Create top level unit test tasks.
-        tasks.create(JavaPlugin.TEST_TASK_NAME) { Task unitTestTask ->
-            unitTestTask.group = JavaBasePlugin.VERIFICATION_GROUP
-            unitTestTask.description = "Run unit tests for all variants."
-        }
-        tasks.named(JavaBasePlugin.CHECK_TASK_NAME) { Task check ->
-            check.dependsOn JavaPlugin.TEST_TASK_NAME
-        }
-
-        // If gradle is launched with --continue, we want to run all tests and generate an
-        // aggregate report (to help with the fact that we may have several build variants, or
-        // or several device providers).
-        // To do that, the report tasks must run even if one of their dependent tasks (flavor
-        // or specific provider tasks) fails, when --continue is used, and the report task is
-        // meant to run (== is in the task graph).
-        // To do this, we make the children tasks ignore their errors (ie they won't fail and
-        // stop the build).
-        if (!reportTasks.isEmpty() && project.gradle.startParameter.continueOnFailure) {
-            project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
-                for (String reportTask : reportTasks) {
-                    if (taskGraph.hasTask(reportTask)) {
-                        tasks.named(reportTask) { AndroidReportTask task ->
-                            task.setWillRun()
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    public enum TestType {
-
-        APPLICATION(DeviceProviderInstrumentTestTask.class),
-        LIBRARY(DeviceProviderInstrumentTestLibraryTask.class),
-
-        final private Class<? extends DeviceProviderInstrumentTestTask> taskType;
-
-        TestType(Class<? extends DeviceProviderInstrumentTestTask> provider) {
-            taskType = provider;
-        }
-    }
-
-    protected void createConnectedTestForVariantData(
-            @NonNull TaskFactory tasks,
-            final TestVariantData testVariantData,
-            TestType testType) {
-        BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
-                testVariantData.testedVariantData as BaseVariantData
-
-        // get single output for now
-        BaseVariantOutputData variantOutputData = baseVariantData.outputs.get(0)
-        BaseVariantOutputData testVariantOutputData = testVariantData.outputs.get(0)
-
-        String connectedRootName = "${CONNECTED}${ANDROID_TEST.suffix}"
-
-        String connectedTaskName =
-                "${connectedRootName}${baseVariantData.variantConfiguration.fullName.capitalize()}"
-
-        TestData testData = new TestDataImpl(testVariantData)
-        BaseVariantData<? extends BaseVariantOutputData> testedVariantData =
-                baseVariantData as BaseVariantData
-        // create the check tasks for this test
-        // first the connected one.
-        ImmutableList<Task> artifactsTasks = ImmutableList.of(
-                testVariantData.outputs.get(0).assembleTask,
-                testedVariantData.assembleVariantTask)
-
-        DeviceProviderInstrumentTestTask connectedTask =
-                createDeviceProviderInstrumentTestTask(
-                        connectedTaskName,
-                        "Installs and runs the tests for ${baseVariantData.description} on connected devices.",
-                        testType.taskType,
-                        testData,
-                        artifactsTasks,
-                        new ConnectedDeviceProvider(
-                                sdkHandler.getSdkInfo().adb,
-                                new LoggerWrapper(logger)),
-                        CONNECTED
-                )
-
-        tasks.named(connectedRootName) {
-            it.dependsOn connectedTask
-        }
-        testVariantData.connectedTestTask = connectedTask
-
-        if (baseVariantData.variantConfiguration.buildType.isTestCoverageEnabled()) {
-            def reportTask = project.tasks.create(
-                    "create${baseVariantData.variantConfiguration.fullName.capitalize()}CoverageReport",
-                    JacocoReportTask)
-            reportTask.reportName = baseVariantData.variantConfiguration.fullName
-            conventionMapping(reportTask).map("jacocoClasspath") {
-                project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
-            }
-            conventionMapping(reportTask).map("coverageFile") {
-                new File(connectedTask.getCoverageDir(),
-                        SimpleTestCallable.FILE_COVERAGE_EC)
-            }
-            conventionMapping(reportTask).map("classDir") {
-                return baseVariantData.javaCompileTask.destinationDir
-            }
-            conventionMapping(reportTask).
-                    map("sourceDir") { baseVariantData.getJavaSourceFoldersForCoverage() }
-
-            conventionMapping(reportTask).map("reportDir") {
-                project.file(
-                        "$project.buildDir/$FD_REPORTS/coverage/${baseVariantData.variantConfiguration.dirName}")
-            }
-
-            reportTask.dependsOn connectedTask
-            tasks.named(connectedRootName) {
-                it.dependsOn reportTask
-            }
-        }
-
-        String mainProviderTaskName = "${DEVICE}${ANDROID_TEST.suffix}"
-
-        List<DeviceProvider> providers = getExtension().deviceProviders
-
-        boolean hasFlavors = baseVariantData.variantConfiguration.hasFlavors()
-
-        // now the providers.
-        for (DeviceProvider deviceProvider : providers) {
-            DeviceProviderInstrumentTestTask providerTask =
-                    createDeviceProviderInstrumentTestTask(
-                            hasFlavors ?
-                                    "${deviceProvider.name}${ANDROID_TEST.suffix}${baseVariantData.variantConfiguration.fullName.capitalize()}" :
-                                    "${deviceProvider.name}${ANDROID_TEST.suffix}",
-                            "Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' using Provider '${deviceProvider.name.capitalize()}'.",
-                            testType.taskType,
-                            testData,
-                            artifactsTasks,
-                            deviceProvider,
-                            "$DEVICE/$deviceProvider.name"
-                    )
-
-            tasks.named(mainProviderTaskName) {
-                it.dependsOn providerTask
-            }
-            testVariantData.providerTestTaskList.add(providerTask)
-
-            if (!deviceProvider.isConfigured()) {
-                providerTask.enabled = false;
-            }
-        }
-
-        // now the test servers
-        // don't use an auto loop as it'll break the closure inside.
-        List<TestServer> servers = getExtension().testServers
-        for (TestServer testServer : servers) {
-            def serverTask = project.tasks.create(
-                    hasFlavors ?
-                            "${testServer.name}${"upload".capitalize()}${baseVariantData.variantConfiguration.fullName}" :
-                            "${testServer.name}${"upload".capitalize()}",
-                    TestServerTask)
-
-            serverTask.description =
-                    "Uploads APKs for Build '${baseVariantData.variantConfiguration.fullName}' to Test Server '${testServer.name.capitalize()}'."
-            serverTask.group = JavaBasePlugin.VERIFICATION_GROUP
-            serverTask.dependsOn testVariantOutputData.assembleTask,
-                    variantOutputData.assembleTask
-
-            serverTask.testServer = testServer
-
-            conventionMapping(serverTask).
-                    map("testApk") { testVariantOutputData.outputFile }
-            if (!(baseVariantData instanceof LibraryVariantData)) {
-                conventionMapping(serverTask).
-                        map("testedApk") { variantOutputData.outputFile }
-            }
-
-            conventionMapping(serverTask).
-                    map("variantName") { baseVariantData.variantConfiguration.fullName }
-
-            tasks.named(DEVICE_CHECK) {
-                it.dependsOn serverTask
-            }
-
-            if (!testServer.isConfigured()) {
-                serverTask.enabled = false;
-            }
-        }
-    }
-
-    protected DeviceProviderInstrumentTestTask createDeviceProviderInstrumentTestTask(
-            @NonNull String taskName,
-            @NonNull String description,
-            @NonNull Class<? extends DeviceProviderInstrumentTestTask> taskClass,
-            @NonNull TestData testData,
-            @NonNull List<Task> artifactsTasks,
-            @NonNull DeviceProvider deviceProvider,
-            @NonNull String subFolder) {
-        DeviceProviderInstrumentTestTask testTask = project.tasks.create(
-                taskName,
-                taskClass as Class<DeviceProviderInstrumentTestTask>)
-
-        testTask.description = description
-        testTask.group = JavaBasePlugin.VERIFICATION_GROUP
-
-        for (Task task : artifactsTasks) {
-            testTask.dependsOn task
-        }
-
-        testTask.androidBuilder = androidBuilder
-        testTask.testData = testData
-        testTask.flavorName = testData.getFlavorName()
-        testTask.deviceProvider = deviceProvider
-        testTask.installOptions = getExtension().getAdbOptions().getInstallOptions();
-
-        conventionMapping(testTask).map("resultsDir") {
-            String rootLocation = getExtension().testOptions.resultsDir != null ?
-                    getExtension().testOptions.resultsDir :
-                    "$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
-
-            String flavorFolder = testData.getFlavorName()
-            if (!flavorFolder.isEmpty()) {
-                flavorFolder = "$FD_FLAVORS/" + flavorFolder
-            }
-
-            project.file("$rootLocation/$subFolder/$flavorFolder")
-        }
-
-        conventionMapping(testTask).map("adbExec") {
-            return sdkHandler.getSdkInfo().getAdb()
-        }
-
-        conventionMapping(testTask).map("splitSelectExec") {
-            String path = androidBuilder.targetInfo?.buildTools?.getPath(
-                    BuildToolInfo.PathId.SPLIT_SELECT)
-            if (path != null) {
-                File splitSelectExe = new File(path)
-                return splitSelectExe.exists() ? splitSelectExe : null;
-            } else {
-                return null;
-            }
-        }
-        testTask.processExecutor = androidBuilder.getProcessExecutor()
-
-
-        conventionMapping(testTask).map("reportsDir") {
-            String rootLocation = getExtension().testOptions.reportDir != null ?
-                    getExtension().testOptions.reportDir :
-                    "$project.buildDir/$FD_REPORTS/$FD_ANDROID_TESTS"
-
-            String flavorFolder = testData.getFlavorName()
-            if (!flavorFolder.isEmpty()) {
-                flavorFolder = "$FD_FLAVORS/" + flavorFolder
-            }
-
-            project.file("$rootLocation/$subFolder/$flavorFolder")
-        }
-        conventionMapping(testTask).map("coverageDir") {
-            String rootLocation = "$project.buildDir/${FD_OUTPUTS}/code-coverage"
-
-            String flavorFolder = testData.getFlavorName()
-            if (!flavorFolder.isEmpty()) {
-                flavorFolder = "$FD_FLAVORS/" + flavorFolder
-            }
-
-            project.file("$rootLocation/$subFolder/$flavorFolder")
-        }
-
-        return testTask
-    }
-
-    /**
-     * Class to hold data to setup the many optional
-     * post-compilation steps.
-     */
-    public static class PostCompilationData {
-
-        List<?> classGeneratingTask
-
-        List<?> libraryGeneratingTask
-
-        Closure<List<File>> inputFiles
-
-        Closure<File> inputDir
-
-        Closure<List<File>> inputLibraries
-    }
-
-    public void createJarTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
-        BaseVariantData variantData = scope.variantData;
-
-        GradleVariantConfiguration config = variantData.variantConfiguration
-        tasks.create("jar${config.fullName.capitalize()}Classes", AndroidJarTask) { AndroidJarTask jarTask ->
-            //        AndroidJarTask jarTask = project.tasks.create(
-            //                "jar${config.fullName.capitalize()}Classes",
-            //                AndroidJarTask)
-
-            jarTask.setArchiveName("classes.jar");
-            jarTask.setDestinationDir(new File(
-                    "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/packaged/${config.dirName}/"))
-            jarTask.from(scope.javaOutputDir);
-            jarTask.dependsOn scope.javaCompileTask.name
-            variantData.binayFileProviderTask = jarTask
-        }
-    }
-
-    /**
-     * Creates the post-compilation tasks for the given Variant.
-     *
-     * These tasks create the dex file from the .class files, plus optional intermediary steps
-     * like proguard and jacoco
-     *
-     * @param variantData the variant data.
-     */
-    public void createPostCompilationTasks(TaskFactory tasks, @NonNull final VariantScope scope) {
-        ApkVariantData variantData = (ApkVariantData) scope.variantData;
-        GradleVariantConfiguration config = variantData.variantConfiguration
-
-        // data holding dependencies and input for the dex. This gets updated as new
-        // post-compilation steps are inserted between the compilation and dx.
-        PostCompilationData pcData = new PostCompilationData()
-        pcData.classGeneratingTask = [scope.javaCompileTask.name]
-        pcData.libraryGeneratingTask =
-                [variantData.variantDependency.packageConfiguration.buildDependencies]
-        pcData.inputFiles = {
-            variantData.javaCompileTask.outputs.files.files as List
-        }
-        pcData.inputDir = {
-            scope.javaOutputDir
-        }
-        pcData.inputLibraries = {
-            scope.globalScope.androidBuilder.getPackagedJars(config) as List
-        }
-
-        // ---- Code Coverage first -----
-        boolean isTestCoverageEnabled = config.buildType.isTestCoverageEnabled() &&
-                !config.type.isForTesting()
-        if (isTestCoverageEnabled) {
-            pcData = createJacocoTask(tasks, scope, pcData);
-        }
-
-        boolean isTestForApp = config.type.isForTesting() &&
-                (variantData as TestVariantData).testedVariantData.variantConfiguration.type ==
-                DEFAULT
-        boolean isMinifyEnabled = config.isMinifyEnabled()
-        boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp
-        boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode()
-
-        // ----- Minify next ----
-        File outFile = maybeCreateProguardTasks(tasks, scope, pcData);
-        if (outFile != null) {
-            pcData.inputFiles = { [outFile] }
-            pcData.inputLibraries = { [] }
-        } else if ((getExtension().dexOptions.preDexLibraries && !isMultiDexEnabled) ||
-                (isMultiDexEnabled && !isLegacyMultiDexMode)) {
-
-            AndroidTask<PreDex> preDexTask =
-                    androidTasks.create(tasks, new PreDex.ConfigAction(scope, pcData))
-
-            // update dependency.
-            preDexTask.dependsOn(tasks, pcData.libraryGeneratingTask)
-            pcData.libraryGeneratingTask = [preDexTask.name] as List<Object>
-
-            // update inputs
-            if (isMultiDexEnabled) {
-                pcData.inputLibraries = { [] }
-
-            } else {
-                pcData.inputLibraries = {
-                    project.fileTree(scope.getPreDexOutputDir()).files as List
-                }
-            }
-        }
-
-        AndroidTask<CreateMainDexList> createMainDexListTask = null;
-        AndroidTask<RetraceMainDexList> retraceTask = null;
-
-        // ----- Multi-Dex support
-        if (isMultiDexEnabled && isLegacyMultiDexMode) {
-            if (!isMinifyEnabled) {
-                // create a task that will convert the output of the compilation
-                // into a jar. This is needed by the multi-dex input.
-                AndroidTask<JarMergingTask> jarMergingTask = androidTasks.create(tasks,
-                        new JarMergingTask.ConfigAction(scope, pcData));
-
-                // update dependencies
-                jarMergingTask.optionalDependsOn(
-                        tasks,
-                        pcData.classGeneratingTask,
-                        pcData.libraryGeneratingTask)
-                pcData.libraryGeneratingTask = [jarMergingTask.name]
-                pcData.classGeneratingTask = [jarMergingTask.name]
-
-                // Update the inputs
-                pcData.inputFiles = { [scope.getJarMergingOutputFile()] }
-                pcData.inputDir = null
-                pcData.inputLibraries = { [] }
-            }
-
-            // ----------
-            // Create a task to collect the list of manifest entry points which are
-            // needed in the primary dex
-            AndroidTask<CreateManifestKeepList> manifestKeepListTask = androidTasks.create(tasks,
-                    new CreateManifestKeepList.ConfigAction(scope, pcData))
-            manifestKeepListTask.dependsOn(tasks, variantData.outputs.get(0).scope.manifestProcessorTask)
-
-            // ----------
-            // Create a proguard task to shrink the classes to manifest components
-            AndroidTask<ProGuardTask> proguardComponentsTask =
-                    androidTasks.create(tasks, new ProGuardTaskConfigAction(scope, pcData))
-
-            // update dependencies
-            proguardComponentsTask.dependsOn tasks, manifestKeepListTask
-            proguardComponentsTask.optionalDependsOn(tasks,
-                    pcData.classGeneratingTask,
-                    pcData.libraryGeneratingTask)
-
-            // ----------
-            // Compute the full list of classes for the main dex file
-            createMainDexListTask = androidTasks.create(tasks, new CreateMainDexList.ConfigAction(scope, pcData))
-            createMainDexListTask.dependsOn(tasks, proguardComponentsTask)
-            //createMainDexListTask.dependsOn { proguardMainDexTask }
-
-            // ----------
-            // If proguard is enabled, create a de-obfuscated list to aid debugging.
-            if (isMinifyEnabled) {
-                retraceTask = androidTasks.create(tasks,
-                        new RetraceMainDexList.ConfigAction(scope, pcData))
-                retraceTask.dependsOn(tasks, scope.obfuscationTask, createMainDexListTask)
-            }
-        }
-
-        AndroidTask<Dex> dexTask = androidTasks.create(tasks, new Dex.ConfigAction(scope, pcData));
-        scope.setDexTask(dexTask);
-
-        // dependencies, some of these could be null
-        dexTask.optionalDependsOn(tasks,
-                pcData.classGeneratingTask,
-                pcData.libraryGeneratingTask,
-                createMainDexListTask,
-                retraceTask)
-    }
-
-    public PostCompilationData createJacocoTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope,
-            @NonNull final PostCompilationData pcData) {
-        AndroidTask<JacocoInstrumentTask> jacocoTask = androidTasks.create(tasks,
-                new JacocoInstrumentTask.ConfigAction(scope, pcData));
-
-        jacocoTask.optionalDependsOn(tasks, pcData.classGeneratingTask)
-
-        Copy agentTask = getJacocoAgentTask()
-        jacocoTask.dependsOn(tasks, agentTask)
-
-        // update dependency.
-        PostCompilationData pcData2 = new PostCompilationData()
-        pcData2.classGeneratingTask = [jacocoTask.name]
-        pcData2.libraryGeneratingTask = [pcData.libraryGeneratingTask, agentTask]
-
-        // update inputs
-        pcData2.inputFiles = {
-            project.files(scope.variantData.jacocoInstrumentTask.getOutputDir()).files as List
-        }
-        pcData2.inputDir = {
-            scope.variantData.jacocoInstrumentTask.getOutputDir()
-        }
-        pcData2.inputLibraries = {
-            [pcData.inputLibraries.call(), [new File(agentTask.destinationDir, FILE_JACOCO_AGENT)]].
-                    flatten() as List
-        }
-
-        return pcData2
-    }
-
-    public void createJackTask(
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
-            @Nullable BaseVariantData<? extends BaseVariantOutputData> testedVariantData) {
-
-        GradleVariantConfiguration config = variantData.variantConfiguration
-
-        // ----- Create Jill tasks -----
-        JillTask jillRuntimeTask = project.tasks.create(
-                "jill${config.fullName.capitalize()}RuntimeLibraries",
-                JillTask)
-
-        jillRuntimeTask.androidBuilder = androidBuilder
-        jillRuntimeTask.dexOptions = getExtension().dexOptions
-
-        conventionMapping(jillRuntimeTask).map("inputLibs") {
-            androidBuilder.getBootClasspath()
-        }
-        conventionMapping(jillRuntimeTask).map("outputFolder") {
-            project.file(
-                    "${project.buildDir}/${FD_INTERMEDIATES}/jill/${config.dirName}/runtime")
-        }
-
-        // ----
-
-        JillTask jillPackagedTask = project.tasks.create(
-                "jill${config.fullName.capitalize()}PackagedLibraries",
-                JillTask)
-
-        jillPackagedTask.dependsOn variantData.variantDependency.packageConfiguration.buildDependencies
-        jillPackagedTask.androidBuilder = androidBuilder
-        jillPackagedTask.dexOptions = getExtension().dexOptions
-
-        conventionMapping(jillPackagedTask).map("inputLibs") {
-            androidBuilder.getPackagedJars(config)
-        }
-        conventionMapping(jillPackagedTask).map("outputFolder") {
-            project.file(
-                    "${project.buildDir}/${FD_INTERMEDIATES}/jill/${config.dirName}/packaged")
-        }
-
-        // ----- Create Jack Task -----
-        JackTask compileTask = project.tasks.create(
-                "compile${config.fullName.capitalize()}JavaWithJack",
-                JackTask)
-        compileTask.isVerbose = isVerbose()
-        compileTask.isDebugLog = isDebugLog()
-
-        // Jack is compiling and also providing the binary and mapping files.
-        variantData.javaCompileTask = compileTask
-        variantData.mappingFileProviderTask = compileTask
-        variantData.binayFileProviderTask = compileTask
-
-        variantData.javaCompileTask.dependsOn variantData.sourceGenTask, jillRuntimeTask, jillPackagedTask
-        variantData.compileTask.dependsOn variantData.javaCompileTask
-        // TODO - dependency information for the compile classpath is being lost.
-        // Add a temporary approximation
-        compileTask.dependsOn variantData.variantDependency.compileConfiguration.buildDependencies
-
-        compileTask.androidBuilder = androidBuilder
-        conventionMapping(compileTask).
-                map("javaMaxHeapSize") { getExtension().dexOptions.getJavaMaxHeapSize() }
-
-        compileTask.source = variantData.getJavaSources()
-
-        compileTask.multiDexEnabled = config.isMultiDexEnabled()
-        compileTask.minSdkVersion = config.minSdkVersion.apiLevel
-
-        // if the tested variant is an app, add its classpath. For the libraries,
-        // it's done automatically since the classpath includes the library output as a normal
-        // dependency.
-        if (testedVariantData instanceof ApplicationVariantData) {
-            JackTask jackTask = (JackTask) testedVariantData.javaCompileTask
-            conventionMapping(compileTask).map("classpath") {
-                project.fileTree(jillRuntimeTask.outputFolder) +
-                        jackTask.classpath +
-                        project.fileTree(jackTask.jackFile)
-            }
-        } else {
-            conventionMapping(compileTask).map("classpath") {
-                project.fileTree(jillRuntimeTask.outputFolder)
-            }
-        }
-
-        conventionMapping(compileTask).map("packagedLibraries") {
-            project.fileTree(jillPackagedTask.outputFolder).files
-        }
-
-        conventionMapping(compileTask).map("destinationDir") {
-            project.file("$project.buildDir/${FD_INTERMEDIATES}/dex/${config.dirName}")
-        }
-
-        conventionMapping(compileTask).map("jackFile") {
-            project.file(
-                    "$project.buildDir/${FD_INTERMEDIATES}/packaged/${config.dirName}/classes.zip")
-        }
-
-        conventionMapping(compileTask).map("tempFolder") {
-            project.file("$project.buildDir/${FD_INTERMEDIATES}/tmp/jack/${config.dirName}")
-        }
-        if (config.isMinifyEnabled()) {
-            conventionMapping(compileTask).map("proguardFiles") {
-                // since all the output use the same resources, we can use the first output
-                // to query for a proguard file.
-                BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
-
-                List<File> proguardFiles = config.getProguardFiles(true /*includeLibs*/,
-                        [getExtension().getDefaultProguardFile(DEFAULT_PROGUARD_CONFIG_FILE)])
-                File proguardResFile = variantOutputData.processResourcesTask.proguardOutputFile
-                if (proguardResFile != null) {
-                    proguardFiles.add(proguardResFile)
-                }
-                // for tested app, we only care about their aapt config since the base
-                // configs are the same files anyway.
-                if (testedVariantData != null) {
-                    // use single output for now.
-                    proguardResFile =
-                            testedVariantData.outputs.get(0).processResourcesTask.proguardOutputFile
-                    if (proguardResFile != null) {
-                        proguardFiles.add(proguardResFile)
-                    }
-                }
-
-                return proguardFiles
-            }
-
-            compileTask.mappingFile = project.file(
-                    "${project.buildDir}/${FD_OUTPUTS}/mapping/${variantData.variantConfiguration.dirName}/mapping.txt")
-        }
-
-        configureLanguageLevel(compileTask)
-    }
-
-    /**
-     * Configures the source and target language level of a compile task. If the user has set it
-     * explicitly, we obey the setting. Otherwise we change the default language level based on the
-     * compile SDK version.
-     *
-     * <p>This method modifies getExtension().compileOptions, to propagate the language level to Studio.
-     */
-    private void configureLanguageLevel(AbstractCompile compileTask) {
-        def compileOptions = getExtension().compileOptions
-        JavaVersion javaVersionToUse
-
-        Integer compileSdkLevel =
-                AndroidTargetHash.getVersionFromHash(getExtension().compileSdkVersion)?.apiLevel
-        switch (compileSdkLevel) {
-            case null:  // Default to 1.6 if we fail to parse compile SDK version.
-            case 0..20:
-                javaVersionToUse = JavaVersion.VERSION_1_6
-                break
-            default:
-                javaVersionToUse = JavaVersion.VERSION_1_7
-                break
-        }
-
-        def jdkVersion = JavaVersion.toVersion(System.getProperty("java.specification.version"))
-        if (jdkVersion < javaVersionToUse) {
-            logger.info(
-                    "Default language level for 'compileSdkVersion $compileSdkLevel' is " +
-                            "$javaVersionToUse, but the JDK used is $jdkVersion, so the JDK " +
-                            "language level will be used.")
-            javaVersionToUse = jdkVersion
-        }
-
-        compileOptions.defaultJavaVersion = javaVersionToUse
-
-        conventionMapping(compileTask).map("sourceCompatibility") {
-            compileOptions.sourceCompatibility.toString()
-        }
-        conventionMapping(compileTask).map("targetCompatibility") {
-            compileOptions.targetCompatibility.toString()
-        }
-    }
-
-    /**
-     * Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
-     * @param variantData
-     * @param assembleTask an optional assembleTask to be used. If null a new one is created. The
-     *                assembleTask is always set in the Variant.
-     * @param publishApk if true the generated APK gets published.
-     */
-    public void createPackagingTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope variantScope,
-            boolean publishApk) {
-        ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData()
-
-        GradleVariantConfiguration config = variantData.variantConfiguration
-        boolean signedApk = variantData.isSigned()
-        // use a dynamic property invocation due to gradle issue.
-        String defaultLocation = "$project.buildDir/${FD_OUTPUTS}/apk"
-        String apkLocation = defaultLocation
-        if (project.hasProperty(PROPERTY_APK_LOCATION)) {
-            apkLocation = project.getProperties().get(PROPERTY_APK_LOCATION)
-        }
-        SigningConfig sc = (SigningConfig) config.signingConfig
-
-        boolean multiOutput = variantData.outputs.size() > 1
-
-        // loop on all outputs. The only difference will be the name of the task, and location
-        // of the generated data.
-        for (ApkVariantOutputData vod : variantData.outputs) {
-            VariantOutputScope variantOutputScope = vod.scope;
-
-            // create final var inside the loop to ensure the closures will work.
-            final ApkVariantOutputData variantOutputData = vod
-
-            String outputName = variantOutputData.fullName
-
-            // When shrinking resources, rather than having the packaging task
-            // directly map to the packageOutputFile of ProcessAndroidResources,
-            // we insert the ShrinkResources task into the chain, such that its
-            // input is the ProcessAndroidResources packageOutputFile, and its
-            // output is what the PackageApplication task reads.
-            AndroidTask<ShrinkResources> shrinkTask = null;
-
-            if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() && !config
-                    .getUseJack()) {
-                shrinkTask = androidTasks.create(tasks,
-                        new ShrinkResources.ConfigAction(variantOutputScope));
-                shrinkTask.dependsOn(tasks,
-                        variantScope.obfuscationTask,
-                        variantOutputScope.manifestProcessorTask,
-                        variantOutputScope.processResourcesTask);
-            }
-
-            AndroidTask<PackageApplication> packageApp = androidTasks.create(tasks,
-                    new PackageApplication.ConfigAction(variantOutputScope));
-
-            packageApp.dependsOn(tasks,
-                    variantOutputScope.processResourcesTask,
-                    variantOutputScope.variantScope.processJavaResourcesTask,
-                    variantOutputScope.variantScope.getNdkBuildable());
-
-            packageApp.optionalDependsOn(
-                    tasks,
-                    shrinkTask,
-                    variantOutputScope.variantScope.dexTask,
-                    variantOutputScope.variantScope.javaCompileTask,
-                    variantData.javaCompileTask,  // TODO: Remove when Jack is converted to AndroidTask.
-                    variantOutputData.packageSplitResourcesTask,
-                    variantOutputData.packageSplitAbiTask);
-
-            AndroidTask appTask = packageApp
-
-            if (signedApk) {
-                if (variantData.zipAlignEnabled) {
-                    AndroidTask<ZipAlign> zipAlignTask = androidTasks.create(
-                            tasks,
-                            new ZipAlign.ConfigAction(variantOutputScope));
-                    zipAlignTask.dependsOn(tasks, packageApp);
-                    if (variantOutputData.splitZipAlign != null) {
-                        zipAlignTask.dependsOn(tasks, variantOutputData.splitZipAlign)
-                    }
-                    appTask = zipAlignTask
-                }
-            }
-
-            assert variantData.assembleVariantTask != null
-
-            // Add an assemble task
-            if (multiOutput) {
-                // create a task for this output
-                variantOutputData.assembleTask = createAssembleTask(variantOutputData)
-
-                // variant assemble task depends on each output assemble task.
-                variantData.assembleVariantTask.dependsOn variantOutputData.assembleTask
-            } else {
-                // single output
-                variantOutputData.assembleTask = variantData.assembleVariantTask
-            }
-
-            if (!signedApk && variantOutputData.packageSplitResourcesTask != null) {
-                // in case we are not signing the resulting APKs and we have some pure splits
-                // we should manually copy them from the intermediate location to the final
-                // apk location unmodified.
-                Copy copyTask = project.tasks.create(
-                        "copySplit${outputName.capitalize()}",
-                        Copy)
-                copyTask.destinationDir = new File(apkLocation as String);
-                copyTask.from(variantOutputData.packageSplitResourcesTask.getOutputDirectory())
-                variantOutputData.assembleTask.dependsOn(copyTask)
-                copyTask.mustRunAfter(appTask.name)
-            }
-
-            variantOutputData.assembleTask.dependsOn appTask.name
-
-            if (publishApk) {
-                String projectBaseName = globalScope.projectBaseName;
-
-                // if this variant is the default publish config or we also should publish non
-                // defaults, proceed with declaring our artifacts.
-                if (getExtension().defaultPublishConfig.equals(outputName)) {
-                    appTask.configure(tasks) { FileSupplier packageTask ->
-                        project.artifacts.add("default", new ApkPublishArtifact(
-                                projectBaseName,
-                                null,
-                                packageTask))
-                    }
-
-                    for (FileSupplier outputFileProvider :
-                            variantOutputData.getSplitOutputFileSuppliers()) {
-                        project.artifacts.add("default", new ApkPublishArtifact(
-                                projectBaseName,
-                                null,
-                                outputFileProvider))
-                    }
-
-                    if (variantOutputData.getMetadataFile() != null) {
-                        project.artifacts.add("default-metadata",
-                                new MetadataPublishArtifact(projectBaseName, null,
-                                        variantOutputData.getMetadataFile()));
-                    }
-
-                    if (variantData.getMappingFileProvider() != null) {
-                        project.artifacts.add("default-mapping",
-                                new MappingPublishArtifact(projectBaseName,
-                                        null,
-                                        variantData.getMappingFileProvider()));
-                    }
-                }
-
-                if (getExtension().publishNonDefault) {
-                    appTask.configure(tasks) { FileSupplier packageTask ->
-                        project.artifacts.add(
-                                variantData.variantDependency.publishConfiguration.name,
-                                new ApkPublishArtifact(
-                                        projectBaseName,
-                                        null,
-                                        packageTask))
-                    }
-
-                    for (FileSupplier outputFileProvider :
-                            variantOutputData.getSplitOutputFileSuppliers()) {
-                        project.artifacts.add(
-                                variantData.variantDependency.publishConfiguration.name,
-                                new ApkPublishArtifact(
-                                    projectBaseName,
-                                    null,
-                                    outputFileProvider))
-                    }
-
-                    if (variantOutputData.getMetadataFile() != null) {
-                        project.artifacts.add(
-                                variantData.variantDependency.metadataConfiguration.name,
-                                new MetadataPublishArtifact(projectBaseName, null,
-                                        variantOutputData.getMetadataFile()));
-                    }
-
-                    if (variantData.getMappingFileProvider() != null) {
-                        project.artifacts.add(
-                                variantData.variantDependency.mappingConfiguration.name,
-                                new MappingPublishArtifact(projectBaseName,
-                                        null,
-                                        variantData.getMappingFileProvider()));
-                    }
-
-                    if (variantData.classesJarTask != null) {
-                        project.artifacts.add(
-                                variantData.variantDependency.classesConfiguration.name,
-                                variantData.classesJarTask)
-                    }
-                }
-            }
-        }
-
-        // create install task for the variant Data. This will deal with finding the
-        // right output if there are more than one.
-        // Add a task to install the application package
-        if (signedApk) {
-            AndroidTask<InstallVariantTask> installTask = androidTasks.create(
-                    tasks,
-                    new InstallVariantTask.ConfigAction(variantScope));
-            installTask.dependsOn(tasks, variantData.assembleVariantTask)
-        }
-
-        if (getExtension().lintOptions.checkReleaseBuilds) {
-            createLintVitalTask(variantData)
-        }
-
-        // add an uninstall task
-        AndroidTask<UninstallTask> uninstallTask = androidTasks.create(
-                tasks,
-                new UninstallTask.ConfigAction(variantScope));
-
-        tasks.named(UNINSTALL_ALL) {
-            it.dependsOn uninstallTask.name
-        }
-    }
-
-    public Task createAssembleTask(
-            @NonNull BaseVariantOutputData variantOutputData) {
-        Task assembleTask = project.tasks.
-                create("assemble${variantOutputData.fullName.capitalize()}")
-        return assembleTask
-    }
-
-    public Task createAssembleTask(
-            TaskFactory tasks,
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
-        Task assembleTask = project.tasks.
-                create("assemble${variantData.variantConfiguration.fullName.capitalize()}")
-        return assembleTask;
-    }
-
-    public Copy getJacocoAgentTask() {
-        if (jacocoAgentTask == null) {
-            jacocoAgentTask = project.tasks.create("unzipJacocoAgent", Copy)
-            jacocoAgentTask.from {
-                project.configurations.getByName(JacocoPlugin.AGENT_CONFIGURATION_NAME).
-                        collect { project.zipTree(it) }
-            }
-            jacocoAgentTask.include FILE_JACOCO_AGENT
-            jacocoAgentTask.into "$project.buildDir/${FD_INTERMEDIATES}/jacoco"
-        }
-
-        return jacocoAgentTask
-    }
-
-    /**
-     * creates a zip align. This does not use convention mapping,
-     * and is meant to let other plugin create zip align tasks.
-     *
-     * @param name the name of the task
-     * @param inputFile the input file
-     * @param outputFile the output file
-     *
-     * @return the task
-     */
-    @NonNull
-    ZipAlign createZipAlignTask(
-            @NonNull String name,
-            @NonNull File inputFile,
-            @NonNull File outputFile) {
-        // Add a task to zip align application package
-        def zipAlignTask = project.tasks.create(name, ZipAlign)
-
-        zipAlignTask.inputFile = inputFile
-        zipAlignTask.outputFile = outputFile
-        conventionMapping(zipAlignTask).map("zipAlignExe") {
-            String path = androidBuilder.targetInfo?.buildTools?.getPath(ZIP_ALIGN)
-            if (path != null) {
-                return new File(path)
-            }
-
-            return null
-        }
-
-        return zipAlignTask
-    }
-
-    /**
-     * Creates the proguarding task for the given Variant if necessary.
-     * @param variantData the variant data.
-     * @param testedVariantData optional. variant data representing the tested variant, null if the
-     *                          variant is not a test variant
-     * @return null if the proguard task was not created, otherwise the expected outputFile.
-     */
-    @Nullable
-    public File maybeCreateProguardTasks(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope,
-            @NonNull final PostCompilationData pcData) {
-        if (!scope.variantData.getVariantConfiguration().isMinifyEnabled()) {
-            return null;
-        }
-
-        AndroidTask<AndroidProGuardTask> proguardTask =
-                androidTasks.create(tasks, new AndroidProGuardTask.ConfigAction(scope, pcData));
-        scope.obfuscationTask = proguardTask
-
-        // update dependency.
-        proguardTask.optionalDependsOn(tasks, pcData.classGeneratingTask, pcData.libraryGeneratingTask)
-        pcData.libraryGeneratingTask = [proguardTask.name]
-        pcData.classGeneratingTask = [proguardTask.name]
-
-        // Return output file.
-        return scope.getProguardOutputFile();
-    }
-
-    public void createReportTasks(
-            List<BaseVariantData<? extends BaseVariantOutputData>> variantDataList) {
-        def dependencyReportTask = project.tasks.create("androidDependencies", DependencyReportTask)
-        dependencyReportTask.setDescription("Displays the Android dependencies of the project.")
-        dependencyReportTask.setVariants(variantDataList)
-        dependencyReportTask.setGroup(ANDROID_GROUP)
-
-        def signingReportTask = project.tasks.create("signingReport", SigningReportTask)
-        signingReportTask.setDescription("Displays the signing info for each variant.")
-        signingReportTask.setVariants(variantDataList)
-        signingReportTask.setGroup(ANDROID_GROUP)
-    }
-
-    public void createAnchorTasks(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        createPreBuildTasks(tasks, scope)
-
-        // also create sourceGenTask
-        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        scope.sourceGenTask = androidTasks.create(tasks,
-                "generate${variantData.variantConfiguration.fullName.capitalize()}Sources") { Task task ->
-            variantData.sourceGenTask = task
-        }
-        // and resGenTask
-        scope.resourceGenTask = androidTasks.create(tasks,
-                "generate${variantData.variantConfiguration.fullName.capitalize()}Resources") { Task task ->
-            variantData.resourceGenTask = task
-        }
-
-        scope.assetGenTask = androidTasks.create(tasks,
-                "generate${variantData.variantConfiguration.fullName.capitalize()}Assets") { Task task ->
-            variantData.assetGenTask = task
-        }
-
-        // and compile task
-        createCompileAnchorTask(tasks, scope)
-    }
-
-    private void createPreBuildTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        variantData.preBuildTask = project.tasks.create(
-                "pre${variantData.variantConfiguration.fullName.capitalize()}Build")
-
-        def prepareDependenciesTask = project.tasks.create(
-                "prepare${variantData.variantConfiguration.fullName.capitalize()}Dependencies",
-                PrepareDependenciesTask)
-
-        variantData.prepareDependenciesTask = prepareDependenciesTask
-        prepareDependenciesTask.dependsOn variantData.preBuildTask
-
-        prepareDependenciesTask.androidBuilder = androidBuilder
-        prepareDependenciesTask.variant = variantData
-
-        // for all libraries required by the configurations of this variant, make this task
-        // depend on all the tasks preparing these libraries.
-        VariantDependencies configurationDependencies = variantData.variantDependency
-        prepareDependenciesTask.addChecker(configurationDependencies.checker)
-
-        for (LibraryDependencyImpl lib : configurationDependencies.libraries) {
-            dependencyManager.
-                    addDependencyToPrepareTask(variantData, prepareDependenciesTask, lib)
-        }
-    }
-
-    private void createCompileAnchorTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        scope.compileTask = androidTasks.create(tasks,
-                "compile${variantData.variantConfiguration.fullName.capitalize()}Sources") { DefaultTask task ->
-            variantData.compileTask = task
-            variantData.compileTask.group = BUILD_GROUP
-        }
-        variantData.assembleVariantTask.dependsOn scope.compileTask.name
-    }
-
-    public void createCheckManifestTask(
-            @NonNull TaskFactory tasks,
-            @NonNull VariantScope scope) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-        String name = variantData.variantConfiguration.fullName
-        scope.checkManifestTask = androidTasks.create(tasks,
-                "check${name.capitalize()}Manifest",
-                CheckManifest) { CheckManifest checkManifestTask ->
-            variantData.checkManifestTask = checkManifestTask
-            checkManifestTask.variantName = name
-            ConventionMappingHelper.map(variantData.checkManifestTask, "manifest") {
-                variantData.variantConfiguration.getDefaultSourceSet().manifestFile
-            }
-        }
-        scope.checkManifestTask.dependsOn(tasks, variantData.preBuildTask)
-        variantData.prepareDependenciesTask.dependsOn(scope.checkManifestTask.name)
-    }
-
-    public static void optionalDependsOn(@NonNull Task main, Task... dependencies) {
-        for (Task dependency : dependencies) {
-            if (dependency != null) {
-                main.dependsOn dependency
-            }
-        }
-    }
-
-    public static void optionalDependsOn(@NonNull Task main, @NonNull List<?> dependencies) {
-        for (Object dependency : dependencies) {
-            if (dependency != null) {
-                main.dependsOn dependency
-            }
-        }
-    }
-
-    @NonNull
-    private List<ManifestDependencyImpl> getManifestDependencies(
-            List<LibraryDependency> libraries) {
-
-        List<ManifestDependencyImpl> list = Lists.newArrayListWithCapacity(libraries.size())
-
-        for (LibraryDependency lib : libraries) {
-            // get the dependencies
-            List<ManifestDependencyImpl> children = getManifestDependencies(lib.dependencies)
-            list.add(new ManifestDependencyImpl(lib.getName(), lib.manifest, children))
-        }
-
-        return list
-    }
-
-    @NonNull
-    protected Logger getLogger() {
-        return logger
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
new file mode 100644
index 0000000..4c646b5
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
@@ -0,0 +1,2459 @@
+/*
+ * Copyright (C) 2014 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 static com.android.build.OutputFile.DENSITY;
+import static com.android.builder.core.BuilderConstants.CONNECTED;
+import static com.android.builder.core.BuilderConstants.DEVICE;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS;
+import static com.android.builder.core.BuilderConstants.FD_FLAVORS_ALL;
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.DEFAULT;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+import static com.android.sdklib.BuildToolInfo.PathId.ZIP_ALIGN;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.OutputFile;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.AndroidGradleOptions;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.coverage.JacocoInstrumentTask;
+import com.android.build.gradle.internal.coverage.JacocoPlugin;
+import com.android.build.gradle.internal.coverage.JacocoReportTask;
+import com.android.build.gradle.internal.dependency.LibraryDependencyImpl;
+import com.android.build.gradle.internal.dependency.ManifestDependencyImpl;
+import com.android.build.gradle.internal.dependency.VariantDependencies;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AbiSplitOptions;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.publishing.ApkPublishArtifact;
+import com.android.build.gradle.internal.publishing.MappingPublishArtifact;
+import com.android.build.gradle.internal.publishing.MetadataPublishArtifact;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.android.build.gradle.internal.scope.AndroidTaskRegistry;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.GlobalScope;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantOutputScope;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.AndroidReportTask;
+import com.android.build.gradle.internal.tasks.CheckManifest;
+import com.android.build.gradle.internal.tasks.DependencyReportTask;
+import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
+import com.android.build.gradle.internal.tasks.ExtractJavaResourcesTask;
+import com.android.build.gradle.internal.tasks.FileSupplier;
+import com.android.build.gradle.internal.tasks.GenerateApkDataTask;
+import com.android.build.gradle.internal.tasks.InstallVariantTask;
+import com.android.build.gradle.internal.tasks.MergeJavaResourcesTask;
+import com.android.build.gradle.internal.tasks.MockableAndroidJarTask;
+import com.android.build.gradle.internal.tasks.PrepareDependenciesTask;
+import com.android.build.gradle.internal.tasks.SigningReportTask;
+import com.android.build.gradle.internal.tasks.SourceSetsTask;
+import com.android.build.gradle.internal.tasks.TestServerTask;
+import com.android.build.gradle.internal.tasks.UninstallTask;
+import com.android.build.gradle.internal.tasks.multidex.CreateMainDexList;
+import com.android.build.gradle.internal.tasks.multidex.CreateManifestKeepList;
+import com.android.build.gradle.internal.tasks.multidex.JarMergingTask;
+import com.android.build.gradle.internal.tasks.multidex.RetraceMainDexList;
+import com.android.build.gradle.internal.test.TestDataImpl;
+import com.android.build.gradle.internal.test.report.ReportType;
+import com.android.build.gradle.internal.variant.ApkVariantData;
+import com.android.build.gradle.internal.variant.ApkVariantOutputData;
+import com.android.build.gradle.internal.variant.ApplicationVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.internal.variant.LibraryVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.build.gradle.tasks.AidlCompile;
+import com.android.build.gradle.tasks.AndroidJarTask;
+import com.android.build.gradle.tasks.AndroidProGuardTask;
+import com.android.build.gradle.tasks.CompatibleScreensManifest;
+import com.android.build.gradle.tasks.Dex;
+import com.android.build.gradle.tasks.GenerateBuildConfig;
+import com.android.build.gradle.tasks.GenerateResValues;
+import com.android.build.gradle.tasks.GenerateSplitAbiRes;
+import com.android.build.gradle.tasks.JackTask;
+import com.android.build.gradle.tasks.JavaResourcesProvider;
+import com.android.build.gradle.tasks.JillTask;
+import com.android.build.gradle.tasks.Lint;
+import com.android.build.gradle.tasks.MergeAssets;
+import com.android.build.gradle.tasks.MergeManifests;
+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.PackageSplitAbi;
+import com.android.build.gradle.tasks.PackageSplitRes;
+import com.android.build.gradle.tasks.PreDex;
+import com.android.build.gradle.tasks.ProcessAndroidResources;
+import com.android.build.gradle.tasks.ProcessManifest;
+import com.android.build.gradle.tasks.ProcessTestManifest;
+import com.android.build.gradle.tasks.RenderscriptCompile;
+import com.android.build.gradle.tasks.ShrinkResources;
+import com.android.build.gradle.tasks.SplitZipAlign;
+import com.android.build.gradle.tasks.ZipAlign;
+import com.android.build.gradle.tasks.factory.JavaCompileConfigAction;
+import com.android.build.gradle.tasks.factory.ProGuardTaskConfigAction;
+import com.android.build.gradle.tasks.factory.ProcessJavaResConfigAction;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.core.VariantType;
+import com.android.builder.dependency.LibraryDependency;
+import com.android.builder.internal.testing.SimpleTestCallable;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.builder.testing.ConnectedDeviceProvider;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.IAndroidTarget;
+import com.android.utils.StringHelper;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.plugins.BasePlugin;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.reporting.ConfigurableReport;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.api.tasks.testing.TestTaskReports;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import groovy.lang.Closure;
+import proguard.gradle.ProGuardTask;
+
+/**
+ * Manages tasks creation.
+ */
+public abstract class TaskManager {
+
+    public static final String FILE_JACOCO_AGENT = "jacocoagent.jar";
+
+    public static final String DEFAULT_PROGUARD_CONFIG_FILE = "proguard-android.txt";
+
+    public static final String DIR_BUNDLES = "bundles";
+
+    public static final String INSTALL_GROUP = "Install";
+
+    public static final String BUILD_GROUP = BasePlugin.BUILD_GROUP;
+
+    public static final String ANDROID_GROUP = "Android";
+
+    protected Project project;
+
+    protected AndroidBuilder androidBuilder;
+
+    private DependencyManager dependencyManager;
+
+    protected SdkHandler sdkHandler;
+
+    protected AndroidConfig extension;
+
+    protected ToolingModelBuilderRegistry toolingRegistry;
+
+    private final GlobalScope globalScope;
+
+    private AndroidTaskRegistry androidTasks = new AndroidTaskRegistry();
+
+    private Logger logger;
+
+    protected boolean isNdkTaskNeeded = true;
+
+    // Task names
+    // TODO: Convert to AndroidTask.
+    private static final String MAIN_PREBUILD = "preBuild";
+
+    private static final String UNINSTALL_ALL = "uninstallAll";
+
+    private static final String DEVICE_CHECK = "deviceCheck";
+
+    protected static final String CONNECTED_CHECK = "connectedCheck";
+
+    private static final String ASSEMBLE_ANDROID_TEST = "assembleAndroidTest";
+
+    private static final String SOURCE_SETS = "sourceSets";
+
+    private static final String LINT = "lint";
+
+    protected static final String LINT_COMPILE = "compileLint";
+
+    // Tasks
+    private Copy jacocoAgentTask;
+
+    public MockableAndroidJarTask createMockableJar;
+
+    public TaskManager(
+            Project project,
+            AndroidBuilder androidBuilder,
+            AndroidConfig extension,
+            SdkHandler sdkHandler,
+            DependencyManager dependencyManager,
+            ToolingModelBuilderRegistry toolingRegistry) {
+        this.project = project;
+        this.androidBuilder = androidBuilder;
+        this.sdkHandler = sdkHandler;
+        this.extension = extension;
+        this.toolingRegistry = toolingRegistry;
+        this.dependencyManager = dependencyManager;
+        logger = Logging.getLogger(this.getClass());
+
+        globalScope = new GlobalScope(
+                project,
+                androidBuilder,
+                checkNotNull((String) project.getProperties().get("archivesBaseName")),
+                extension,
+                sdkHandler,
+                toolingRegistry);
+    }
+
+    private boolean isVerbose() {
+        return project.getLogger().isEnabled(LogLevel.INFO);
+    }
+
+    private boolean isDebugLog() {
+        return project.getLogger().isEnabled(LogLevel.DEBUG);
+    }
+
+    /**
+     * Creates the tasks for a given BaseVariantData.
+     */
+    public abstract void createTasksForVariantData(@NonNull TaskFactory tasks,
+            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData);
+
+    public GlobalScope getGlobalScope() {
+        return globalScope;
+    }
+
+    /**
+     * Returns a collection of buildables that creates native object.
+     *
+     * A buildable is considered to be any object that can be used as the argument to
+     * Task.dependsOn.  This could be a Task or a BuildableModelElement (e.g. BinarySpec).
+     */
+    protected Collection<Object> getNdkBuildable(BaseVariantData variantData) {
+        return Collections.<Object>singleton(variantData.ndkCompileTask);
+    }
+
+    /**
+     * Override to configure NDK data in the scope.
+     */
+    public void configureScopeForNdk(@NonNull VariantScope scope) {
+        final BaseVariantData variantData = scope.getVariantData();
+        scope.setNdkSoFolder(Collections.singleton(new File(
+                scope.getGlobalScope().getIntermediatesDir(),
+                "ndk/" + variantData.getVariantConfiguration().getDirName() + "/lib")));
+        File objFolder = new File(scope.getGlobalScope().getIntermediatesDir(),
+                "ndk/" + variantData.getVariantConfiguration().getDirName() + "/obj");
+        scope.setNdkObjFolder(objFolder);
+        for (Abi abi : NdkHandler.getAbiList()) {
+            scope.addNdkDebuggableLibraryFolders(abi,
+                    new File(objFolder, "local/" + abi.getName()));
+        }
+
+    }
+
+    protected AndroidConfig getExtension() {
+        return extension;
+    }
+
+    public void resolveDependencies(
+            @NonNull VariantDependencies variantDeps,
+            @Nullable VariantDependencies testedVariantDeps,
+            @Nullable String testedProjectPath) {
+        dependencyManager.resolveDependencies(variantDeps, testedVariantDeps, testedProjectPath);
+    }
+
+    /**
+     * Create tasks before the evaluation (on plugin apply). This is useful for tasks that
+     * could be referenced by custom build logic.
+     */
+    public void createTasksBeforeEvaluate(@NonNull TaskFactory tasks) {
+        tasks.create(UNINSTALL_ALL, new Action<Task>() {
+            @Override
+            public void execute(Task uninstallAllTask) {
+                uninstallAllTask.setDescription("Uninstall all applications.");
+                uninstallAllTask.setGroup(INSTALL_GROUP);
+            }
+        });
+
+        tasks.create(DEVICE_CHECK, new Action<Task>() {
+            @Override
+            public void execute(Task deviceCheckTask) {
+                deviceCheckTask.setDescription(
+                        "Runs all device checks using Device Providers and Test Servers.");
+                deviceCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+            }
+        });
+
+        tasks.create(CONNECTED_CHECK, new Action<Task>() {
+            @Override
+            public void execute(Task connectedCheckTask) {
+                connectedCheckTask.setDescription(
+                        "Runs all device checks on currently connected devices.");
+                connectedCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+            }
+        });
+
+        tasks.create(MAIN_PREBUILD);
+
+        tasks.create(SOURCE_SETS, SourceSetsTask.class, new Action<SourceSetsTask>() {
+            @Override
+            public void execute(SourceSetsTask sourceSetsTask) {
+                sourceSetsTask.setConfig(extension);
+                sourceSetsTask.setDescription(
+                        "Prints out all the source sets defined in this project.");
+                sourceSetsTask.setGroup(ANDROID_GROUP);
+            }
+        });
+
+        tasks.create(ASSEMBLE_ANDROID_TEST, new Action<Task>() {
+            @Override
+            public void execute(Task assembleAndroidTestTask) {
+                assembleAndroidTestTask.setGroup(BasePlugin.BUILD_GROUP);
+                assembleAndroidTestTask.setDescription("Assembles all the Test applications.");
+            }
+        });
+
+        tasks.create(LINT, Lint.class, new Action<Lint>() {
+            @Override
+            public void execute(Lint lintTask) {
+                lintTask.setDescription("Runs lint on all variants.");
+                lintTask.setVariantName("");
+                lintTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+                lintTask.setLintOptions(getExtension().getLintOptions());
+                lintTask.setSdkHome(sdkHandler.getSdkFolder());
+                lintTask.setToolingRegistry(toolingRegistry);
+            }
+        });
+        tasks.named(JavaBasePlugin.CHECK_TASK_NAME, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(LINT);
+            }
+        });
+        createLintCompileTask(tasks);
+    }
+
+    public void createMockableJarTask() {
+        createMockableJar =
+                project.getTasks().create("mockableAndroidJar", MockableAndroidJarTask.class);
+        createMockableJar.setGroup(BUILD_GROUP);
+        createMockableJar.setDescription(
+                "Creates a version of android.jar that's suitable for unit tests.");
+        createMockableJar.setReturnDefaultValues(
+                extension.getTestOptions().getUnitTests().isReturnDefaultValues());
+
+        ConventionMappingHelper.map(createMockableJar, "androidJar", new Callable<File>() {
+            @Override
+            public File call() throws Exception {
+                checkNotNull(androidBuilder.getTarget(), "ensureTargetSetup not called");
+                return new File(androidBuilder.getTarget().getPath(IAndroidTarget.ANDROID_JAR));
+            }
+        });
+
+        ConventionMappingHelper.map(createMockableJar, "outputFile", new Callable<File>() {
+            @Override
+            public File call() throws Exception {
+                // Since the file ends up in $rootProject.buildDir, it will survive clean
+                // operations - projects generated by AS don't have a top-level clean task that
+                // would delete the top-level build directory. This means that the name has to
+                // encode all the necessary information, otherwise the task will be UP-TO-DATE
+                // even if the file should be regenerated. That's why we put the SDK version and
+                // "default-values" in there, so if one project uses the returnDefaultValues flag,
+                // it will just generate a new file and not change the semantics for other
+                // sub-projects. There's an implicit "v1" there as well, if we ever change the
+                // generator logic, the names will have to be changed.
+                String fileExt;
+                if (createMockableJar.getReturnDefaultValues()) {
+                    fileExt = ".default-values.jar";
+                } else {
+                    fileExt = ".jar";
+                }
+                File outDir = new File(
+                        project.getRootProject().getBuildDir(),
+                        AndroidProject.FD_GENERATED);
+
+                CharMatcher safeCharacters =
+                        CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf("-."));
+                String sdkName =
+                        safeCharacters.negate().replaceFrom(extension.getCompileSdkVersion(), '-');
+
+                return new File(outDir, "mockable-" + sdkName + fileExt);
+            }
+        });
+    }
+
+    public void createMergeAppManifestsTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope variantScope) {
+
+        ApplicationVariantData appVariantData =
+                (ApplicationVariantData) variantScope.getVariantData();
+        Set<String> screenSizes = appVariantData.getCompatibleScreens();
+
+        // loop on all outputs. The only difference will be the name of the task, and location
+        // of the generated manifest
+        for (final BaseVariantOutputData vod : appVariantData.getOutputs()) {
+            VariantOutputScope scope = vod.getScope();
+
+            AndroidTask<CompatibleScreensManifest> csmTask = null;
+            if (vod.getMainOutputFile().getFilter(DENSITY) != null) {
+                csmTask = androidTasks.create(tasks,
+                        new CompatibleScreensManifest.ConfigAction(scope, screenSizes));
+                scope.setCompatibleScreensManifestTask(csmTask);
+            }
+
+            scope.setManifestProcessorTask(androidTasks.create(tasks,
+                    new MergeManifests.ConfigAction(scope)));
+
+            if (csmTask != null) {
+                scope.getManifestProcessorTask().dependsOn(tasks, csmTask);
+            }
+        }
+    }
+
+    public void createMergeLibManifestsTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+
+        AndroidTask<ProcessManifest> processManifest = androidTasks.create(tasks,
+                new ProcessManifest.ConfigAction(scope));
+
+        processManifest.dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
+
+        BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
+        variantOutputData.getScope().setManifestProcessorTask(processManifest);
+    }
+
+    protected void createProcessTestManifestTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+
+        AndroidTask<ProcessTestManifest> processTestManifestTask = androidTasks.create(tasks,
+                new ProcessTestManifest.ConfigAction(scope));
+
+        processTestManifestTask.dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
+
+        BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
+        variantOutputData.getScope().setManifestProcessorTask(processTestManifestTask);
+    }
+
+    public void createRenderscriptTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+        scope.setRenderscriptCompileTask(
+                androidTasks.create(tasks, new RenderscriptCompile.ConfigAction(scope)));
+
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        GradleVariantConfiguration config = variantData.getVariantConfiguration();
+        // get single output for now.
+        BaseVariantOutputData variantOutputData = variantData.getOutputs().get(0);
+
+        scope.getRenderscriptCompileTask().dependsOn(tasks, variantData.prepareDependenciesTask);
+        if (config.getType().isForTesting()) {
+            scope.getRenderscriptCompileTask().dependsOn(tasks,
+                    variantOutputData.getScope().getManifestProcessorTask());
+        } else {
+            scope.getRenderscriptCompileTask().dependsOn(tasks, scope.getCheckManifestTask());
+        }
+
+        scope.getResourceGenTask().dependsOn(tasks, scope.getRenderscriptCompileTask());
+        // only put this dependency if rs will generate Java code
+        if (!config.getRenderscriptNdkModeEnabled()) {
+            scope.getSourceGenTask().dependsOn(tasks, scope.getRenderscriptCompileTask());
+        }
+
+    }
+
+    public AndroidTask<MergeResources> createMergeResourcesTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+        return basicCreateMergeResourcesTask(
+                tasks,
+                scope,
+                "merge",
+                null /*outputLocation*/,
+                true /*includeDependencies*/,
+                true /*process9patch*/);
+    }
+
+    public AndroidTask<MergeResources> basicCreateMergeResourcesTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope,
+            @NonNull String taskNamePrefix,
+            @Nullable File outputLocation,
+            final boolean includeDependencies,
+            final boolean process9Patch) {
+        AndroidTask<MergeResources> mergeResourcesTask = androidTasks.create(tasks,
+                new MergeResources.ConfigAction(
+                        scope,
+                        taskNamePrefix,
+                        outputLocation,
+                        includeDependencies,
+                        process9Patch));
+        mergeResourcesTask.dependsOn(tasks,
+                scope.getVariantData().prepareDependenciesTask,
+                scope.getResourceGenTask());
+        scope.setMergeResourcesTask(mergeResourcesTask);
+        scope.setResourceOutputDir(
+                Objects.firstNonNull(outputLocation, scope.getDefaultMergeResourcesOutputDir()));
+        return scope.getMergeResourcesTask();
+    }
+
+
+    public void createMergeAssetsTask(TaskFactory tasks, VariantScope scope) {
+        AndroidTask<MergeAssets> mergeAssetsTask = androidTasks.create(tasks, new MergeAssets.ConfigAction(scope));
+        mergeAssetsTask.dependsOn(tasks,
+                scope.getVariantData().prepareDependenciesTask,
+                scope.getAssetGenTask());
+        scope.setMergeAssetsTask(mergeAssetsTask);
+    }
+
+    public void createBuildConfigTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+        AndroidTask<GenerateBuildConfig> generateBuildConfigTask =
+                androidTasks.create(tasks, new GenerateBuildConfig.ConfigAction(scope));
+        scope.setGenerateBuildConfigTask(generateBuildConfigTask);
+        scope.getSourceGenTask().dependsOn(tasks, generateBuildConfigTask.getName());
+        if (scope.getVariantConfiguration().getType().isForTesting()) {
+            // in case of a test project, the manifest is generated so we need to depend
+            // on its creation.
+
+            // For test apps there should be a single output, so we get it.
+            BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
+
+            generateBuildConfigTask.dependsOn(
+                    tasks, variantOutputData.getScope().getManifestProcessorTask());
+        } else {
+            generateBuildConfigTask.dependsOn(tasks, scope.getCheckManifestTask());
+        }
+    }
+
+    public void createGenerateResValuesTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+        AndroidTask<GenerateResValues> generateResValuesTask = androidTasks.create(
+                tasks, new GenerateResValues.ConfigAction(scope));
+        scope.getResourceGenTask().dependsOn(tasks, generateResValuesTask);
+    }
+
+    public void createProcessResTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope,
+            boolean generateResourcePackage) {
+        createProcessResTask(
+                tasks,
+                scope,
+                new File(globalScope.getIntermediatesDir(),
+                        "symbols/" + scope.getVariantData().getVariantConfiguration().getDirName()),
+                generateResourcePackage);
+    }
+
+    public void createProcessResTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope,
+            @Nullable File symbolLocation,
+            boolean generateResourcePackage) {
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+        variantData.calculateFilters(scope.getGlobalScope().getExtension().getSplits());
+
+        // loop on all outputs. The only difference will be the name of the task, and location
+        // of the generated data.
+        for (BaseVariantOutputData vod : variantData.getOutputs()) {
+            final VariantOutputScope variantOutputScope = vod.getScope();
+
+            variantOutputScope.setProcessResourcesTask(androidTasks.create(tasks,
+                    new ProcessAndroidResources.ConfigAction(variantOutputScope, symbolLocation,
+                            generateResourcePackage)));
+            variantOutputScope.getProcessResourcesTask().dependsOn(tasks,
+                    variantOutputScope.getManifestProcessorTask(),
+                    scope.getMergeResourcesTask(),
+                    scope.getMergeAssetsTask());
+
+            if (vod.getMainOutputFile().getFilter(DENSITY) == null) {
+                scope.setGenerateRClassTask(variantOutputScope.getProcessResourcesTask());
+                scope.getSourceGenTask().optionalDependsOn(tasks,
+                        variantOutputScope.getProcessResourcesTask());
+            }
+
+        }
+
+    }
+
+    /**
+     * Creates the split resources packages task if necessary. AAPT will produce split packages for
+     * all --split provided parameters. These split packages should be signed and moved unchanged to
+     * the APK build output directory.
+     */
+    public void createSplitResourcesTasks(@NonNull VariantScope scope) {
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+        checkState(variantData.getSplitHandlingPolicy().equals(
+                        BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY),
+                "Can only create split resources tasks for pure splits.");
+
+        final VariantConfiguration config = variantData.getVariantConfiguration();
+        Set<String> densityFilters = variantData.getFilters(OutputFile.FilterType.DENSITY);
+        Set<String> abiFilters = variantData.getFilters(OutputFile.FilterType.ABI);
+        Set<String> languageFilters = variantData.getFilters(OutputFile.FilterType.LANGUAGE);
+
+        List<? extends BaseVariantOutputData> outputs = variantData.getOutputs();
+        if (outputs.size() != 1) {
+            throw new RuntimeException(
+                    "In release 21 and later, there can be only one main APK, " +
+                            "found " + outputs.size());
+        }
+
+        final BaseVariantOutputData variantOutputData = outputs.get(0);
+        VariantOutputScope variantOutputScope = variantOutputData.getScope();
+        variantOutputData.packageSplitResourcesTask = project.getTasks().create(
+                scope.getTaskName("package", "SplitResources"),
+                PackageSplitRes.class);
+        variantOutputData.packageSplitResourcesTask.setInputDirectory(
+                variantOutputScope.getProcessResourcePackageOutputFile().getParentFile());
+        variantOutputData.packageSplitResourcesTask.setDensitySplits(densityFilters);
+        variantOutputData.packageSplitResourcesTask.setLanguageSplits(languageFilters);
+        variantOutputData.packageSplitResourcesTask.setOutputBaseName(config.getBaseName());
+        variantOutputData.packageSplitResourcesTask.setSigningConfig(config.getSigningConfig());
+        variantOutputData.packageSplitResourcesTask.setOutputDirectory(new File(
+                scope.getGlobalScope().getIntermediatesDir(), "splits/" + config.getDirName()));
+        variantOutputData.packageSplitResourcesTask.setAndroidBuilder(androidBuilder);
+        variantOutputData.packageSplitResourcesTask.setVariantName(config.getFullName());
+        variantOutputData.packageSplitResourcesTask.dependsOn(
+                variantOutputScope.getProcessResourcesTask().getName());
+
+        SplitZipAlign zipAlign = project.getTasks().create(
+                scope.getTaskName("zipAlign", "SplitPackages"),
+                SplitZipAlign.class);
+        zipAlign.setVariantName(config.getFullName());
+        ConventionMappingHelper.map(zipAlign, "zipAlignExe", new Callable<File>() {
+            @Override
+            public File call() throws Exception {
+                final TargetInfo info = androidBuilder.getTargetInfo();
+                if (info == null) {
+                    return null;
+                }
+                String path = info.getBuildTools().getPath(ZIP_ALIGN);
+                if (path == null) {
+                    return null;
+                }
+                return new File(path);
+            }
+        });
+
+        zipAlign.setOutputDirectory(new File(scope.getGlobalScope().getBuildDir(), "outputs/apk"));
+        ConventionMappingHelper.map(zipAlign, "densityOrLanguageInputFiles",
+                new Callable<List<File>>() {
+                    @Override
+                    public List<File> call() {
+                        return variantOutputData.packageSplitResourcesTask.getOutputFiles();
+                    }
+                });
+        zipAlign.setOutputBaseName(config.getBaseName());
+        zipAlign.setAbiFilters(abiFilters);
+        zipAlign.setLanguageFilters(languageFilters);
+        zipAlign.setDensityFilters(densityFilters);
+        File metadataDirectory = new File(zipAlign.getOutputDirectory().getParentFile(),
+                "metadata");
+        zipAlign.setApkMetadataFile(new File(metadataDirectory, config.getFullName() + ".mtd"));
+        ((ApkVariantOutputData) variantOutputData).splitZipAlign = zipAlign;
+        zipAlign.dependsOn(variantOutputData.packageSplitResourcesTask);
+    }
+
+    public void createSplitAbiTasks(@NonNull final VariantScope scope) {
+        ApplicationVariantData variantData = (ApplicationVariantData) scope.getVariantData();
+
+        checkState(variantData.getSplitHandlingPolicy().equals(
+                BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY),
+                "split ABI tasks are only compatible with pure splits.");
+
+        final VariantConfiguration config = variantData.getVariantConfiguration();
+        Set<String> filters = AbiSplitOptions.getAbiFilters(
+                getExtension().getSplits().getAbiFilters());
+        if (filters.isEmpty()) {
+            return;
+        }
+
+        List<ApkVariantOutputData> outputs = variantData.getOutputs();
+        if (outputs.size() != 1) {
+            throw new RuntimeException(
+                    "In release 21 and later, there can be only one main APK, " +
+                            "found " + outputs.size());
+        }
+
+        BaseVariantOutputData variantOutputData = outputs.get(0);
+        // first create the split APK resources.
+        GenerateSplitAbiRes generateSplitAbiRes = project.getTasks().create(
+                scope.getTaskName("generate", "SplitAbiRes"),
+                GenerateSplitAbiRes.class);
+        generateSplitAbiRes.setAndroidBuilder(androidBuilder);
+        generateSplitAbiRes.setVariantName(config.getFullName());
+
+        generateSplitAbiRes.setOutputDirectory(new File(
+                scope.getGlobalScope().getIntermediatesDir(), "abi/" + config.getDirName()));
+        generateSplitAbiRes.setSplits(filters);
+        generateSplitAbiRes.setOutputBaseName(config.getBaseName());
+        generateSplitAbiRes.setApplicationId(config.getApplicationId());
+        generateSplitAbiRes.setVersionCode(config.getVersionCode());
+        generateSplitAbiRes.setVersionName(config.getVersionName());
+        ConventionMappingHelper.map(generateSplitAbiRes, "debuggable", new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                return config.getBuildType().isDebuggable();
+            }
+        });
+        ConventionMappingHelper.map(generateSplitAbiRes, "aaptOptions",
+                new Callable<AaptOptions>() {
+                    @Override
+                    public AaptOptions call() throws Exception {
+                        return getExtension().getAaptOptions();
+                    }
+                });
+        generateSplitAbiRes.dependsOn(
+                variantOutputData.getScope().getProcessResourcesTask().getName());
+
+        // then package those resources with the appropriate JNI libraries.
+        variantOutputData.packageSplitAbiTask = project.getTasks().create(
+                scope.getTaskName("package", "SplitAbi"), PackageSplitAbi.class);
+        variantOutputData.packageSplitAbiTask.setInputFiles(generateSplitAbiRes.getOutputFiles());
+        variantOutputData.packageSplitAbiTask.setSplits(filters);
+        variantOutputData.packageSplitAbiTask.setOutputBaseName(config.getBaseName());
+        variantOutputData.packageSplitAbiTask.setSigningConfig(config.getSigningConfig());
+        variantOutputData.packageSplitAbiTask.setOutputDirectory(new File(
+                scope.getGlobalScope().getIntermediatesDir(), "splits/" + config.getDirName()));
+        variantOutputData.packageSplitAbiTask.setMergingFolder(
+                new File(scope.getGlobalScope().getIntermediatesDir(),
+                        "package-merge/" + variantOutputData.getDirName()));
+        variantOutputData.packageSplitAbiTask.setAndroidBuilder(androidBuilder);
+        variantOutputData.packageSplitAbiTask.setVariantName(config.getFullName());
+        variantOutputData.packageSplitAbiTask.dependsOn(generateSplitAbiRes);
+        variantOutputData.packageSplitAbiTask.dependsOn(scope.getNdkBuildable());
+
+        ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "jniFolders",
+                new Callable<Set<File>>() {
+                    @Override
+                    public Set<File> call() throws Exception {
+                        return getJniFolders(scope);
+                    }
+
+                });
+        ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "jniDebuggable",
+                new Callable<Boolean>() {
+                    @Override
+                    public Boolean call() throws Exception {
+                        return config.getBuildType().isJniDebuggable();
+                    }
+                });
+        ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "packagingOptions",
+                new Callable<PackagingOptions>() {
+                    @Override
+                    public PackagingOptions call() throws Exception {
+                        return getExtension().getPackagingOptions();
+                    }
+                });
+        ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "packagingOptionsFilter",
+                new Callable<SignedJarBuilder.IZipEntryFilter>() {
+                    @Override
+                    public SignedJarBuilder.IZipEntryFilter call() throws Exception {
+                        return scope.getPackagingOptionsFilter();
+                    }
+                });
+
+        ((ApkVariantOutputData) variantOutputData).splitZipAlign.getAbiInputFiles().addAll(
+                variantOutputData.packageSplitAbiTask.getOutputFiles());
+
+        ((ApkVariantOutputData) variantOutputData).splitZipAlign.dependsOn(
+                variantOutputData.packageSplitAbiTask);
+    }
+
+    /**
+     * Calculate the list of folders that can contain jni artifacts for this variant.
+     *
+     * @return a potentially empty list of directories that exist or not and that may contains
+     * native resources.
+     */
+    @NonNull
+    public Set<File> getJniFolders(@NonNull VariantScope scope) {
+
+        BaseVariantData variantData = scope.getVariantData();
+        VariantConfiguration config = variantData.getVariantConfiguration();
+        // for now only the project's compilation output.
+        Set<File> set = Sets.newHashSet();
+        addAllIfNotNull(set, scope.getNdkSoFolder());
+        set.add(variantData.renderscriptCompileTask.getLibOutputDir());
+        //noinspection unchecked
+        addAllIfNotNull(set, config.getLibraryJniFolders());
+        //noinspection unchecked
+        addAllIfNotNull(set, config.getJniLibsList());
+
+        if (Boolean.TRUE.equals(config.getMergedFlavor().getRenderscriptSupportModeEnabled())) {
+            File rsLibs = androidBuilder.getSupportNativeLibFolder();
+            if (rsLibs != null && rsLibs.isDirectory()) {
+                set.add(rsLibs);
+            }
+
+        }
+
+        return set;
+    }
+
+    private static <T> void addAllIfNotNull(@NonNull Collection<T> main, @Nullable Collection<T> toAdd) {
+        if (toAdd != null) {
+            main.addAll(toAdd);
+        }
+    }
+
+    /**
+     * Creates the java resources processing tasks.
+     *
+     * The java processing will happen in three steps :
+     * <ul>{@link ExtractJavaResourcesTask} will extract all java resources from packaged jar files
+     * dependencies. Each jar file will be extracted in a separate folder. Each folder will be
+     * located under {@link VariantScope#getPackagedJarsJavaResDestinationDir()}</ul>
+     * <ul>{@link ProcessJavaResConfigAction} will sync all source folders into a single folder
+     * identified by {@link VariantScope#getSourceFoldersJavaResDestinationDir()}</ul>
+     * <ul>{@link MergeJavaResourcesTask} will take all these folders and will create a single
+     * merged folder with the {@link PackagingOptions} settings applied. The folder is located at
+     * {@link VariantScope#getJavaResourcesDestinationDir()}</ul>
+     *
+     * the result of 3 is the final set of java resources to can be either directly embedded in
+     * the resulting APK or fed into the obfuscation tool to produce obfuscated resources.
+     *
+     * @param tasks tasks factory to create tasks.
+     * @param scope the variant scope we are operating under.
+     */
+    public void createProcessJavaResTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+        // first create the incremental task that will extract all libraries java resources
+        // in separate folders.
+        AndroidTask<ExtractJavaResourcesTask> extractJavaResourcesTask = androidTasks
+                .create(tasks, new ExtractJavaResourcesTask.Config(scope));
+
+        // now copy the source folders java resources into the temporary location, mainly to
+        // maintain the PluginDsl COPY semantics.
+        scope.setProcessJavaResourcesTask(
+                androidTasks.create(tasks, new ProcessJavaResConfigAction(scope)));
+
+        // and create the merge tasks that will merge everything.
+        AndroidTask<MergeJavaResourcesTask> mergeJavaResourcesTask = androidTasks
+                .create(tasks, new MergeJavaResourcesTask.Config(scope));
+        // the merge task is the official provider for merged java resources to be bundled in the
+        // final variant specific APK, this may change if obfuscation is turned on.
+        scope.setJavaResourcesProvider(
+                JavaResourcesProvider.Adapter.build(tasks, mergeJavaResourcesTask));
+
+        // set the dependencies.
+        extractJavaResourcesTask.dependsOn(tasks, variantData.prepareDependenciesTask);
+        scope.getProcessJavaResourcesTask().dependsOn(tasks, extractJavaResourcesTask);
+        mergeJavaResourcesTask.dependsOn(tasks, scope.getProcessJavaResourcesTask());
+
+        scope.setMergeJavaResourcesTask(mergeJavaResourcesTask);
+
+    }
+
+    public void createAidlTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+        scope.setAidlCompileTask(androidTasks.create(tasks, new AidlCompile.ConfigAction(scope)));
+        scope.getSourceGenTask().dependsOn(tasks, scope.getAidlCompileTask());
+        scope.getAidlCompileTask().dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
+    }
+
+    /**
+     * Creates the task for creating *.class files using javac. These tasks are created regardless
+     * of whether Jack is used or not, but assemble will not depend on them if it is. They are
+     * always used when running unit tests.
+     */
+    public AndroidTask<JavaCompile> createJavacTask(
+            @NonNull final TaskFactory tasks,
+            @NonNull final VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        final AndroidTask<JavaCompile> javacTask = androidTasks.create(tasks,
+                new JavaCompileConfigAction(scope));
+        scope.setJavacTask(javacTask);
+
+        javacTask.optionalDependsOn(tasks, scope.getSourceGenTask());
+        javacTask.dependsOn(tasks,
+                scope.getVariantData().prepareDependenciesTask,
+                scope.getMergeJavaResourcesTask());
+
+        // TODO - dependency information for the compile classpath is being lost.
+        // Add a temporary approximation
+        javacTask.dependsOn(tasks,
+                scope.getVariantData().getVariantDependency().getCompileConfiguration()
+                        .getBuildDependencies());
+
+        if (variantData.getType().isForTesting()) {
+            BaseVariantData testedVariantData =
+                    (BaseVariantData) ((TestVariantData) variantData).getTestedVariantData();
+            final JavaCompile testedJavacTask = testedVariantData.javacTask;
+            javacTask.dependsOn(tasks,
+                    testedJavacTask != null ? testedJavacTask :
+                            testedVariantData.getScope().getJavacTask());
+        }
+
+        // Create jar task for uses by external modules.
+        if (variantData.getVariantDependency().getClassesConfiguration() != null) {
+            tasks.create(scope.getTaskName("package", "JarArtifact"), Jar.class, new Action<Jar>() {
+                @Override
+                public void execute(Jar jar) {
+                    variantData.classesJarTask = jar;
+                    jar.dependsOn(javacTask.getName());
+
+                    // add the class files (whether they are instrumented or not.
+                    jar.from(scope.getJavaOutputDir());
+
+                    jar.setDestinationDir(new File(
+                            scope.getGlobalScope().getIntermediatesDir(),
+                            "classes-jar/" +
+                                    variantData.getVariantConfiguration().getDirName()));
+                    jar.setArchiveName("classes.jar");
+                }
+            });
+        }
+
+        return javacTask;
+    }
+
+    /**
+     * Makes the given task the one used by top-level "compile" task.
+     */
+    public static void setJavaCompilerTask(
+            @NonNull AndroidTask<? extends AbstractCompile> javaCompilerTask,
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+        scope.getCompileTask().dependsOn(tasks, javaCompilerTask);
+        scope.setJavaCompilerTask(javaCompilerTask);
+
+        // TODO: Get rid of it once we stop keeping tasks in variant data.
+        //noinspection VariableNotUsedInsideIf
+        if (scope.getVariantData().javacTask != null) {
+            // This is not the experimental plugin, let's update variant data, so Variants API
+            // keeps working.
+            scope.getVariantData().javaCompilerTask =  (AbstractCompile) tasks.named(javaCompilerTask.getName());
+        }
+
+    }
+
+    public void createGenerateMicroApkDataTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope,
+            @NonNull Configuration config) {
+        AndroidTask<GenerateApkDataTask> generateMicroApkTask = androidTasks.create(tasks,
+                new GenerateApkDataTask.ConfigAction(scope, config));
+        generateMicroApkTask.dependsOn(tasks, config);
+
+        // the merge res task will need to run after this one.
+        scope.getResourceGenTask().dependsOn(tasks, generateMicroApkTask);
+    }
+
+    public void createNdkTasks(@NonNull VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        NdkCompile ndkCompile = project.getTasks().create(
+                scope.getTaskName("compile", "Ndk"),
+                NdkCompile.class);
+
+        ndkCompile.dependsOn(variantData.preBuildTask);
+
+        ndkCompile.setAndroidBuilder(androidBuilder);
+        ndkCompile.setVariantName(variantData.getName());
+        ndkCompile.setNdkDirectory(sdkHandler.getNdkFolder());
+        ndkCompile.setIsForTesting(variantData.getType().isForTesting());
+        variantData.ndkCompileTask = ndkCompile;
+        variantData.compileTask.dependsOn(variantData.ndkCompileTask);
+
+        final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
+
+        if (Boolean.TRUE.equals(variantConfig.getMergedFlavor().getRenderscriptNdkModeEnabled())) {
+            ndkCompile.setNdkRenderScriptMode(true);
+            ndkCompile.dependsOn(variantData.renderscriptCompileTask);
+        } else {
+            ndkCompile.setNdkRenderScriptMode(false);
+        }
+
+        ConventionMappingHelper.map(ndkCompile, "sourceFolders", new Callable<List<File>>() {
+            @Override
+            public List<File> call() {
+                List<File> sourceList = variantConfig.getJniSourceList();
+                if (Boolean.TRUE.equals(
+                        variantConfig.getMergedFlavor().getRenderscriptNdkModeEnabled())) {
+                    sourceList.add(variantData.renderscriptCompileTask.getSourceOutputDir());
+                }
+
+                return sourceList;
+            }
+        });
+
+        ndkCompile.setGeneratedMakefile(new File(scope.getGlobalScope().getIntermediatesDir(),
+                "ndk/" + variantData.getVariantConfiguration().getDirName() + "/Android.mk"));
+
+        ConventionMappingHelper.map(ndkCompile, "ndkConfig", new Callable<CoreNdkOptions>() {
+            @Override
+            public CoreNdkOptions call() {
+                return variantConfig.getNdkConfig();
+            }
+        });
+
+        ConventionMappingHelper.map(ndkCompile, "debuggable", new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return variantConfig.getBuildType().isJniDebuggable();
+            }
+        });
+
+        ndkCompile.setObjFolder(new File(scope.getGlobalScope().getIntermediatesDir(),
+                "ndk/" + variantData.getVariantConfiguration().getDirName() + "/obj"));
+
+        Collection<File> ndkSoFolder = scope.getNdkSoFolder();
+        if (ndkSoFolder != null && !ndkSoFolder.isEmpty()) {
+            ndkCompile.setSoFolder(ndkSoFolder.iterator().next());
+        }
+    }
+
+    /**
+     * Creates the tasks to build unit tests.
+     */
+    public void createUnitTestVariantTasks(
+            @NonNull TaskFactory tasks,
+            @NonNull TestVariantData variantData) {
+        variantData.assembleVariantTask.dependsOn(createMockableJar);
+        VariantScope variantScope = variantData.getScope();
+
+        createPreBuildTasks(variantScope);
+        createProcessJavaResTasks(tasks, variantScope);
+        createCompileAnchorTask(tasks, variantScope);
+        AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+        setJavaCompilerTask(javacTask, tasks, variantScope);
+        createUnitTestTask(tasks, variantData);
+
+        // This hides the assemble unit test task from the task list.
+        variantData.assembleVariantTask.setGroup(null);
+    }
+
+    /**
+     * Creates the tasks to build android tests.
+     */
+    public void createAndroidTestVariantTasks(@NonNull TaskFactory tasks,
+            @NonNull TestVariantData variantData) {
+        VariantScope variantScope = variantData.getScope();
+
+        // get single output for now (though this may always be the case for tests).
+        final BaseVariantOutputData variantOutputData = variantData.getOutputs().get(0);
+
+        final BaseVariantData<BaseVariantOutputData> baseTestedVariantData =
+                (BaseVariantData<BaseVariantOutputData>) variantData.getTestedVariantData();
+        final BaseVariantOutputData testedVariantOutputData =
+                baseTestedVariantData.getOutputs().get(0);
+
+        createAnchorTasks(tasks, variantScope);
+
+        // Add a task to process the manifest
+        createProcessTestManifestTask(tasks, variantScope);
+
+        // Add a task to create the res values
+        createGenerateResValuesTask(tasks, variantScope);
+
+        // Add a task to compile renderscript files.
+        createRenderscriptTask(tasks, variantScope);
+
+        // Add a task to merge the resource folders
+        createMergeResourcesTask(tasks, variantScope);
+
+        // Add a task to merge the assets folders
+        createMergeAssetsTask(tasks, variantScope);
+
+        if (variantData.getTestedVariantData().getVariantConfiguration().getType().equals(
+                VariantType.LIBRARY)) {
+            // in this case the tested library must be fully built before test can be built!
+            if (testedVariantOutputData.assembleTask != null) {
+                variantOutputData.getScope().getManifestProcessorTask().dependsOn(
+                        tasks, testedVariantOutputData.assembleTask);
+                variantScope.getMergeResourcesTask().dependsOn(
+                        tasks, testedVariantOutputData.assembleTask);
+            }
+        }
+
+        // Add a task to create the BuildConfig class
+        createBuildConfigTask(tasks, variantScope);
+
+        // Add a task to generate resource source files
+        createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/);
+
+        // process java resources
+        createProcessJavaResTasks(tasks, variantScope);
+
+        createAidlTask(tasks, variantScope);
+
+        // Add NDK tasks
+        if (isNdkTaskNeeded) {
+            createNdkTasks(variantScope);
+        }
+
+        variantScope.setNdkBuildable(getNdkBuildable(variantData));
+
+        // Add a task to compile the test application
+        if (variantData.getVariantConfiguration().getUseJack()) {
+            createJackTask(tasks, variantScope);
+        } else {
+            AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+            setJavaCompilerTask(javacTask, tasks, variantScope);
+            createPostCompilationTasks(tasks, variantScope);
+        }
+
+        createPackagingTask(tasks, variantScope, false /*publishApk*/);
+
+        tasks.named(ASSEMBLE_ANDROID_TEST, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(variantOutputData.assembleTask);
+            }
+        });
+
+        createConnectedTestForVariant(tasks, variantScope);
+    }
+
+    // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
+    private void createLintCompileTask(TaskFactory tasks) {
+
+        // TODO: move doFirst into dedicated task class.
+        tasks.create(LINT_COMPILE, Task.class,
+                new Action<Task>() {
+                    @Override
+                    public void execute(Task lintCompile) {
+                        final File outputDir =
+                                new File(getGlobalScope().getIntermediatesDir(), "lint");
+
+                        lintCompile.doFirst(new Action<Task>() {
+                            @Override
+                            public void execute(Task task) {
+                                // 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<? extends BaseVariantOutputData> baseVariantData) {
+        // Only create lint targets for variants like debug and release, not debugTest
+        VariantConfiguration config = baseVariantData.getVariantConfiguration();
+        return !config.getType().isForTesting();
+    }
+
+    /**
+     * Add tasks for running lint on individual variants. We've already added a
+     * lint task earlier which runs on all variants.
+     */
+    public void createLintTasks(TaskFactory tasks, final VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
+                scope.getVariantData();
+        if (!isLintVariant(baseVariantData)) {
+            return;
+        }
+
+        // wire the main lint task dependency.
+        tasks.named(LINT, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(LINT_COMPILE);
+                it.dependsOn(scope.getJavacTask().getName());
+            }
+        });
+
+        AndroidTask<Lint> variantLintCheck = androidTasks.create(
+                tasks, new Lint.ConfigAction(scope));
+        variantLintCheck.dependsOn(tasks, LINT_COMPILE, scope.getJavacTask());
+    }
+
+    private void createLintVitalTask(@NonNull ApkVariantData variantData) {
+        checkState(getExtension().getLintOptions().isCheckReleaseBuilds());
+        // TODO: re-enable with Jack when possible
+        if (!variantData.getVariantConfiguration().getBuildType().isDebuggable() &&
+                !variantData.getVariantConfiguration().getUseJack()) {
+            String variantName = variantData.getVariantConfiguration().getFullName();
+            String capitalizedVariantName = StringHelper.capitalize(variantName);
+            String taskName = "lintVital" + capitalizedVariantName;
+            final Lint lintReleaseCheck = project.getTasks().create(taskName, Lint.class);
+            // TODO: Make this task depend on lintCompile too (resolve initialization order first)
+            optionalDependsOn(lintReleaseCheck, variantData.javacTask);
+            lintReleaseCheck.setLintOptions(getExtension().getLintOptions());
+            lintReleaseCheck.setSdkHome(sdkHandler.getSdkFolder());
+            lintReleaseCheck.setVariantName(variantName);
+            lintReleaseCheck.setToolingRegistry(toolingRegistry);
+            lintReleaseCheck.setFatalOnly(true);
+            lintReleaseCheck.setDescription(
+                    "Runs lint on just the fatal issues in the " + capitalizedVariantName
+                            + " build.");
+            //variantData.assembleVariantTask.dependsOn lintReleaseCheck
+
+            // If lint is being run, we do not need to run lint vital.
+            // TODO: Find a better way to do this.
+            project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+                public void doCall(TaskExecutionGraph taskGraph) {
+                    if (taskGraph.hasTask(LINT)) {
+                        lintReleaseCheck.setEnabled(false);
+                    }
+                }
+            });
+        }
+    }
+
+    private void createUnitTestTask(@NonNull TaskFactory tasks,
+            @NonNull final TestVariantData variantData) {
+        final BaseVariantData testedVariantData =
+                (BaseVariantData) variantData.getTestedVariantData();
+
+        final Test runTestsTask = project.getTasks().create(
+                variantData.getScope().getTaskName(UNIT_TEST.getPrefix()),
+                Test.class);
+        runTestsTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+        runTestsTask.setDescription(
+                "Run unit tests for the " +
+                        testedVariantData.getVariantConfiguration().getFullName() + " build.");
+
+        fixTestTaskSources(runTestsTask);
+
+        runTestsTask.dependsOn(variantData.assembleVariantTask);
+
+        final AbstractCompile testCompileTask = variantData.javacTask;
+        runTestsTask.setTestClassesDir(testCompileTask.getDestinationDir());
+
+        ConventionMappingHelper.map(runTestsTask, "classpath",
+                new Callable<ConfigurableFileCollection>() {
+                    @Override
+                    public ConfigurableFileCollection call() throws Exception {
+                        Iterable<File> filteredBootClasspath = Iterables.filter(
+                                androidBuilder.getBootClasspath(),
+                                new Predicate<File>() {
+                                    @Override
+                                    public boolean apply(@Nullable File file) {
+                                        return file != null &&
+                                                !SdkConstants.FN_FRAMEWORK_LIBRARY
+                                                        .equals(file.getName());
+                                    }
+                                });
+
+                        return project.files(
+                                testCompileTask.getClasspath(),
+                                testCompileTask.getOutputs().getFiles(),
+                                variantData.processJavaResourcesTask.getOutputs(),
+                                testedVariantData.processJavaResourcesTask.getOutputs(),
+                                filteredBootClasspath,
+                                // Mockable JAR is last, to make sure you can shadow the classes
+                                // withdependencies.
+                                createMockableJar.getOutputFile());
+                    }
+                });
+
+        // Put the variant name in the report path, so that different testing tasks don't
+        // overwrite each other's reports.
+        TestTaskReports testTaskReports = runTestsTask.getReports();
+
+        for (ConfigurableReport report : new ConfigurableReport[] {
+                testTaskReports.getJunitXml(), testTaskReports.getHtml()}) {
+            report.setDestination(new File(report.getDestination(), testedVariantData.getName()));
+        }
+
+        tasks.named(JavaPlugin.TEST_TASK_NAME, new Action<Task>() {
+            @Override
+            public void execute(Task test) {
+                test.dependsOn(runTestsTask);
+            }
+
+        });
+
+        extension.getTestOptions().getUnitTests().applyConfiguration(runTestsTask);
+    }
+
+    private static void fixTestTaskSources(@NonNull Test testTask) {
+        // We are running in afterEvaluate, so the JavaBasePlugin has already added a
+        // callback to add test classes to the list of source files of the newly created task.
+        // The problem is that we haven't configured the test classes yet (JavaBasePlugin
+        // assumes all Test tasks are fully configured at this point), so we have to remove the
+        // "directory null" entry from source files and add the right value.
+        //
+        // This is an ugly hack, since we assume sourceFiles is an instance of
+        // DefaultConfigurableFileCollection.
+        ((DefaultConfigurableFileCollection) testTask.getInputs().getSourceFiles()).getFrom().clear();
+    }
+
+    public void createTopLevelTestTasks(final TaskFactory tasks, boolean hasFlavors) {
+        final List<String> reportTasks = Lists.newArrayListWithExpectedSize(2);
+
+        List<DeviceProvider> providers = getExtension().getDeviceProviders();
+
+        final String connectedRootName = CONNECTED + ANDROID_TEST.getSuffix();
+        final String defaultReportsDir = getGlobalScope().getReportsDir().getAbsolutePath()
+                + "/" + FD_ANDROID_TESTS;
+        final String defaultResultsDir = getGlobalScope().getOutputsDir().getAbsolutePath()
+                + "/" + FD_ANDROID_RESULTS;
+
+        // If more than one flavor, create a report aggregator task and make this the parent
+        // task for all new connected tasks.  Otherwise, create a top level connectedAndroidTest
+        // DefaultTask.
+        if (hasFlavors) {
+            tasks.create(connectedRootName, AndroidReportTask.class,
+                    new Action<AndroidReportTask>() {
+                        @Override
+                        public void execute(AndroidReportTask mainConnectedTask) {
+                            mainConnectedTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+                            mainConnectedTask.setDescription("Installs and runs instrumentation "
+                                    + "tests for all flavors on connected devices.");
+                            mainConnectedTask.setReportType(ReportType.MULTI_FLAVOR);
+                            mainConnectedTask.setVariantName("");
+                            ConventionMappingHelper.map(mainConnectedTask, "resultsDir",
+                                    new Callable<File>() {
+                                        @Override
+                                        public File call() {
+                                            final String dir =
+                                                    extension.getTestOptions().getResultsDir();
+                                            String rootLocation = dir != null && !dir.isEmpty()
+                                                    ? dir : defaultResultsDir;
+                                            return project.file(rootLocation + "/connected/"
+                                                    + FD_FLAVORS_ALL);
+                                        }
+                                    });
+                            ConventionMappingHelper.map(mainConnectedTask, "reportsDir",
+                                    new Callable<File>() {
+                                        @Override
+                                        public File call() {
+                                            final String dir =
+                                                    extension.getTestOptions().getReportDir();
+                                            String rootLocation = dir != null && !dir.isEmpty()
+                                                    ? dir : defaultReportsDir;
+                                            return project.file(rootLocation + "/connected/"
+                                                    + FD_FLAVORS_ALL);
+                                        }
+                                    });
+
+                        }
+
+                    });
+            reportTasks.add(connectedRootName);
+        } else {
+            tasks.create(connectedRootName, new Action<Task>() {
+                @Override
+                public void execute(Task connectedTask) {
+                    connectedTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+                    connectedTask.setDescription(
+                            "Installs and runs instrumentation tests for all flavors on connected devices.");
+                }
+
+            });
+        }
+
+        tasks.named(CONNECTED_CHECK, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(connectedRootName);
+            }
+
+        });
+
+        final String mainProviderTaskName = DEVICE + ANDROID_TEST.getSuffix();
+        // if more than one provider tasks, either because of several flavors, or because of
+        // more than one providers, then create an aggregate report tasks for all of them.
+        if (providers.size() > 1 || hasFlavors) {
+            tasks.create(mainProviderTaskName, AndroidReportTask.class,
+                    new Action<AndroidReportTask>() {
+                        @Override
+                        public void execute(AndroidReportTask mainProviderTask) {
+                            mainProviderTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+                            mainProviderTask.setDescription(
+                                    "Installs and runs instrumentation tests using all Device Providers.");
+                            mainProviderTask.setReportType(ReportType.MULTI_FLAVOR);
+
+                            ConventionMappingHelper.map(mainProviderTask, "resultsDir",
+                                    new Callable<File>() {
+                                        @Override
+                                        public File call() throws Exception {
+                                            final String dir =
+                                                    extension.getTestOptions().getResultsDir();
+                                            String rootLocation =  dir != null && !dir.isEmpty()
+                                                    ? dir : defaultResultsDir;
+
+                                            return project.file(rootLocation + "/devices/"
+                                                    + FD_FLAVORS_ALL);
+                                        }
+                                    });
+                            ConventionMappingHelper.map(mainProviderTask, "reportsDir",
+                                    new Callable<File>() {
+                                        @Override
+                                        public File call() throws Exception {
+                                            final String dir =
+                                                    extension.getTestOptions().getReportDir();
+                                            String rootLocation =  dir != null && !dir.isEmpty()
+                                                    ? dir : defaultReportsDir;
+                                            return project.file(rootLocation + "/devices/"
+                                                    + FD_FLAVORS_ALL);
+                                        }
+                                    });
+                        }
+
+                    });
+            reportTasks.add(mainProviderTaskName);
+        } else {
+            tasks.create(mainProviderTaskName, new Action<Task>() {
+                @Override
+                public void execute(Task providerTask) {
+                    providerTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+                    providerTask.setDescription(
+                            "Installs and runs instrumentation tests using all Device Providers.");
+                }
+            });
+        }
+
+        tasks.named(DEVICE_CHECK, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(mainProviderTaskName);
+            }
+        });
+
+        // Create top level unit test tasks.
+        tasks.create(JavaPlugin.TEST_TASK_NAME, new Action<Task>() {
+            @Override
+            public void execute(Task unitTestTask) {
+                unitTestTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+                unitTestTask.setDescription("Run unit tests for all variants.");
+            }
+
+        });
+        tasks.named(JavaBasePlugin.CHECK_TASK_NAME,
+                new Action<Task>() {
+                    @Override
+                    public void execute(Task check) {
+                        check.dependsOn(JavaPlugin.TEST_TASK_NAME);
+                    }
+                });
+
+        // If gradle is launched with --continue, we want to run all tests and generate an
+        // aggregate report (to help with the fact that we may have several build variants, or
+        // or several device providers).
+        // To do that, the report tasks must run even if one of their dependent tasks (flavor
+        // or specific provider tasks) fails, when --continue is used, and the report task is
+        // meant to run (== is in the task graph).
+        // To do this, we make the children tasks ignore their errors (ie they won't fail and
+        // stop the build).
+        if (!reportTasks.isEmpty() && project.getGradle().getStartParameter()
+                .isContinueOnFailure()) {
+            project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+                public void doCall(TaskExecutionGraph taskGraph) {
+                    for (String reportTask : reportTasks) {
+                        if (taskGraph.hasTask(reportTask)) {
+                            tasks.named(reportTask, new Action<Task>() {
+                                @Override
+                                public void execute(Task task) {
+                                    ((AndroidReportTask) task).setWillRun();
+                                }
+                            });
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    protected void createConnectedTestForVariant(
+            @NonNull TaskFactory tasks,
+            @NonNull final VariantScope variantScope) {
+        final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
+                variantScope.getTestedVariantData();
+        final TestVariantData testVariantData = (TestVariantData) variantScope.getVariantData();
+
+        // get single output for now
+        final BaseVariantOutputData variantOutputData = baseVariantData.getOutputs().get(0);
+        final BaseVariantOutputData testVariantOutputData = testVariantData.getOutputs().get(0);
+
+        String connectedRootName = CONNECTED + ANDROID_TEST.getSuffix();
+
+        TestDataImpl testData = new TestDataImpl(testVariantData);
+        testData.setExtraInstrumentationTestRunnerArgs(
+                AndroidGradleOptions.getExtraInstrumentationTestRunnerArgs(project));
+
+        // create the check tasks for this test
+        // first the connected one.
+        ImmutableList<Task> artifactsTasks = ImmutableList.of(
+                testVariantData.getOutputs().get(0).assembleTask,
+                baseVariantData.assembleVariantTask);
+
+        final AndroidTask<DeviceProviderInstrumentTestTask> connectedTask = androidTasks.create(
+                tasks,
+                new DeviceProviderInstrumentTestTask.ConfigAction(
+                        testVariantData.getScope(),
+                        new ConnectedDeviceProvider(sdkHandler.getSdkInfo().getAdb(),
+                                new LoggerWrapper(logger)), testData));
+
+        connectedTask.dependsOn(tasks, artifactsTasks);
+
+        tasks.named(connectedRootName, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(connectedTask.getName());
+            }
+        });
+
+        if (baseVariantData.getVariantConfiguration().getBuildType().isTestCoverageEnabled()
+                && !baseVariantData.getVariantConfiguration().getUseJack()) {
+            final JacocoReportTask reportTask = project.getTasks().create(
+                    variantScope.getTaskName("create", "CoverageReport"),
+                    JacocoReportTask.class);
+            reportTask.setReportName(baseVariantData.getVariantConfiguration().getFullName());
+            ConventionMappingHelper.map(reportTask, "jacocoClasspath",
+                    new Callable<FileCollection>() {
+                        @Override
+                        public FileCollection call() throws Exception {
+                            return project.getConfigurations().getAt(
+                                    JacocoPlugin.ANT_CONFIGURATION_NAME);
+                        }
+                    });
+            ConventionMappingHelper.map(reportTask, "coverageFile", new Callable<File>() {
+                @Override
+                public File call() {
+                    return new File(((TestVariantData) testVariantData.getScope()
+                            .getVariantData()).connectedTestTask.getCoverageDir(),
+                            SimpleTestCallable.FILE_COVERAGE_EC);
+                }
+            });
+            ConventionMappingHelper.map(reportTask, "classDir", new Callable<File>() {
+                @Override
+                public File call() {
+                    return baseVariantData.javacTask.getDestinationDir();
+                }
+            });
+            ConventionMappingHelper.map(reportTask, "sourceDir", new Callable<List<File>>() {
+                @Override
+                public List<File> call() {
+                    return baseVariantData.getJavaSourceFoldersForCoverage();
+                }
+            });
+
+            ConventionMappingHelper.map(reportTask, "reportDir", new Callable<File>() {
+                @Override
+                public File call() {
+                    return new File(variantScope.getGlobalScope().getReportsDir(),
+                            "/coverage/" + baseVariantData.getVariantConfiguration().getDirName());
+                }
+            });
+
+            reportTask.dependsOn(connectedTask.getName());
+            tasks.named(connectedRootName, new Action<Task>() {
+                @Override
+                public void execute(Task it) {
+                    it.dependsOn(reportTask);
+                }
+            });
+        }
+
+        String mainProviderTaskName = DEVICE + ANDROID_TEST.getSuffix();
+
+        List<DeviceProvider> providers = getExtension().getDeviceProviders();
+
+        boolean hasFlavors = baseVariantData.getVariantConfiguration().hasFlavors();
+
+        // now the providers.
+        for (DeviceProvider deviceProvider : providers) {
+
+            final AndroidTask<DeviceProviderInstrumentTestTask> providerTask = androidTasks
+                    .create(tasks, new DeviceProviderInstrumentTestTask.ConfigAction(
+                            testVariantData.getScope(), deviceProvider, testData));
+
+            tasks.named(mainProviderTaskName, new Action<Task>() {
+                @Override
+                public void execute(Task it) {
+                    it.dependsOn(providerTask.getName());
+                }
+            });
+            providerTask.dependsOn(tasks, artifactsTasks);
+        }
+
+        // now the test servers
+        List<TestServer> servers = getExtension().getTestServers();
+        for (TestServer testServer : servers) {
+            final TestServerTask serverTask = project.getTasks().create(
+                    hasFlavors ?
+                            baseVariantData.getScope().getTaskName(testServer.getName() + "Upload")
+                            :
+                                    testServer.getName() + ("Upload"),
+                    TestServerTask.class);
+
+            serverTask.setDescription(
+                    "Uploads APKs for Build \'" + baseVariantData.getVariantConfiguration()
+                            .getFullName() + "\' to Test Server \'" +
+                            StringHelper.capitalize(testServer.getName()) + "\'.");
+            serverTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+            serverTask.setVariantName(
+                    baseVariantData.getScope().getVariantConfiguration().getFullName());
+            serverTask.dependsOn(testVariantOutputData.assembleTask,
+                    variantOutputData.assembleTask);
+
+            serverTask.setTestServer(testServer);
+
+            ConventionMappingHelper.map(serverTask, "testApk", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return testVariantOutputData.getOutputFile();
+                }
+            });
+            if (!(baseVariantData instanceof LibraryVariantData)) {
+                ConventionMappingHelper.map(serverTask, "testedApk", new Callable<File>() {
+                    @Override
+                    public File call() throws Exception {
+                        return variantOutputData.getOutputFile();
+                    }
+                });
+            }
+
+            ConventionMappingHelper.map(serverTask, "variantName", new Callable<String>() {
+                @Override
+                public String call() throws Exception {
+                    return baseVariantData.getVariantConfiguration().getFullName();
+                }
+            });
+
+            tasks.named(DEVICE_CHECK, new Action<Task>() {
+                @Override
+                public void execute(Task it) {
+                    it.dependsOn(serverTask);
+                }
+            });
+
+            if (!testServer.isConfigured()) {
+                serverTask.setEnabled(false);
+            }
+        }
+    }
+
+    public static void createJarTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
+        final BaseVariantData variantData = scope.getVariantData();
+
+        final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+        tasks.create(
+                scope.getTaskName("jar", "Classes"),
+                AndroidJarTask.class,
+                new Action<AndroidJarTask>() {
+                    @Override
+                    public void execute(AndroidJarTask jarTask) {
+                        //        AndroidJarTask jarTask = project.tasks.create(
+                        //                "jar${config.fullName.capitalize()}Classes",
+                        //                AndroidJarTask)
+
+                        jarTask.setArchiveName("classes.jar");
+                        jarTask.setDestinationDir(new File(
+                                scope.getGlobalScope().getIntermediatesDir(),
+                                "packaged/" + config.getDirName() + "/"));
+                        jarTask.from(scope.getJavaOutputDir());
+                        jarTask.dependsOn(scope.getJavacTask().getName());
+                        variantData.binayFileProviderTask = jarTask;
+                    }
+
+                });
+    }
+
+    /**
+     * Creates the post-compilation tasks for the given Variant.
+     *
+     * These tasks create the dex file from the .class files, plus optional intermediary steps like
+     * proguard and jacoco
+     *
+     */
+    public void createPostCompilationTasks(TaskFactory tasks, @NonNull final VariantScope scope) {
+        checkNotNull(scope.getJavacTask());
+
+        final ApkVariantData variantData = (ApkVariantData) scope.getVariantData();
+        final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+
+        // data holding dependencies and input for the dex. This gets updated as new
+        // post-compilation steps are inserted between the compilation and dx.
+        PostCompilationData pcData = new PostCompilationData();
+        pcData.setClassGeneratingTasks(Collections.singletonList(scope.getJavacTask().getName()));
+        pcData.setLibraryGeneratingTasks(ImmutableList.of(variantData.prepareDependenciesTask,
+                variantData.getVariantDependency().getPackageConfiguration()
+                        .getBuildDependencies()));
+        pcData.setInputFilesCallable(new Callable<List<File>>() {
+            @Override
+            public List<File> call() {
+                return new ArrayList<File>(
+                        variantData.javacTask.getOutputs().getFiles().getFiles());
+            }
+
+        });
+        pcData.setInputDir(scope.getJavaOutputDir());
+
+        pcData.setJavaResourcesInputDir(scope.getJavaResourcesDestinationDir());
+
+        pcData.setInputLibrariesCallable(new Callable<List<File>>() {
+            @Override
+            public List<File> call() {
+                return new ArrayList<File>(
+                        scope.getGlobalScope().getAndroidBuilder().getPackagedJars(config));
+            }
+
+        });
+
+        // ---- Code Coverage first -----
+        boolean isTestCoverageEnabled = config.getBuildType().isTestCoverageEnabled() && !config
+                .getType().isForTesting();
+        if (isTestCoverageEnabled) {
+            pcData = createJacocoTask(tasks, scope, pcData);
+        }
+
+        boolean isTestForApp = config.getType().isForTesting() &&
+                ((TestVariantData) variantData).getTestedVariantData().getVariantConfiguration()
+                        .getType().equals(DEFAULT);
+        boolean isMinifyEnabled = config.isMinifyEnabled();
+        boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp;
+        boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode();
+
+        // ----- Minify next ----
+        File outFile = maybeCreateProguardTasks(tasks, scope, pcData);
+        if (outFile != null) {
+            pcData.setInputFiles(Collections.singletonList(outFile));
+            pcData.setInputLibraries(Collections.<File>emptyList());
+        } else if ((getExtension().getDexOptions().getPreDexLibraries() && !isMultiDexEnabled) || (
+                isMultiDexEnabled && !isLegacyMultiDexMode)) {
+
+            AndroidTask<PreDex> preDexTask = androidTasks
+                    .create(tasks, new PreDex.ConfigAction(scope, pcData));
+
+            // update dependency.
+            preDexTask.dependsOn(tasks, pcData.getLibraryGeneratingTasks());
+            pcData.setLibraryGeneratingTasks(Collections.singletonList(preDexTask.getName()));
+
+            // update inputs
+            if (isMultiDexEnabled) {
+                pcData.setInputLibraries(Collections.<File>emptyList());
+
+            } else {
+                pcData.setInputLibrariesCallable(new Callable<List<File>>() {
+                    @Override
+                    public List<File> call() {
+                        return new ArrayList<File>(
+                                project.fileTree(scope.getPreDexOutputDir()).getFiles());
+                    }
+
+                });
+            }
+        }
+
+        AndroidTask<CreateMainDexList> createMainDexListTask = null;
+        AndroidTask<RetraceMainDexList> retraceTask = null;
+
+        // ----- Multi-Dex support
+        if (isMultiDexEnabled && isLegacyMultiDexMode) {
+            if (!isMinifyEnabled) {
+                // create a task that will convert the output of the compilation
+                // into a jar. This is needed by the multi-dex input.
+                AndroidTask<JarMergingTask> jarMergingTask = androidTasks.create(tasks,
+                        new JarMergingTask.ConfigAction(scope, pcData));
+
+                // update dependencies
+                jarMergingTask.optionalDependsOn(tasks,
+                        pcData.getClassGeneratingTasks(),
+                        pcData.getLibraryGeneratingTasks());
+                pcData.setLibraryGeneratingTasks(
+                        Collections.singletonList(jarMergingTask.getName()));
+                pcData.setClassGeneratingTasks(Collections.singletonList(jarMergingTask.getName()));
+
+                // Update the inputs
+                pcData.setInputFiles(Collections.singletonList(scope.getJarMergingOutputFile()));
+                pcData.setInputDirCallable(null);
+                pcData.setInputLibraries(Collections.<File>emptyList());
+            }
+
+            // ----------
+            // Create a task to collect the list of manifest entry points which are
+            // needed in the primary dex
+            AndroidTask<CreateManifestKeepList> manifestKeepListTask = androidTasks.create(tasks,
+                    new CreateManifestKeepList.ConfigAction(scope, pcData));
+            manifestKeepListTask.dependsOn(tasks,
+                    variantData.getOutputs().get(0).getScope().getManifestProcessorTask());
+
+            // ----------
+            // Create a proguard task to shrink the classes to manifest components
+            AndroidTask<ProGuardTask> proguardComponentsTask =
+                    androidTasks.create(tasks, new ProGuardTaskConfigAction(scope, pcData));
+
+            // update dependencies
+            proguardComponentsTask.dependsOn(tasks, manifestKeepListTask);
+            proguardComponentsTask.optionalDependsOn(tasks,
+                    pcData.getClassGeneratingTasks(),
+                    pcData.getLibraryGeneratingTasks());
+
+            // ----------
+            // Compute the full list of classes for the main dex file
+            createMainDexListTask =
+                    androidTasks.create(tasks, new CreateMainDexList.ConfigAction(scope, pcData));
+            createMainDexListTask.dependsOn(tasks, proguardComponentsTask);
+            //createMainDexListTask.dependsOn { proguardMainDexTask }
+
+            // ----------
+            // If proguard is enabled, create a de-obfuscated list to aid debugging.
+            if (isMinifyEnabled) {
+                retraceTask = androidTasks.create(tasks,
+                        new RetraceMainDexList.ConfigAction(scope, pcData));
+                retraceTask.dependsOn(tasks, scope.getObfuscationTask(), createMainDexListTask);
+            }
+
+        }
+
+        AndroidTask<Dex> dexTask = androidTasks.create(tasks, new Dex.ConfigAction(scope, pcData));
+        scope.setDexTask(dexTask);
+
+        // dependencies, some of these could be null
+        dexTask.optionalDependsOn(tasks,
+                pcData.getClassGeneratingTasks(),
+                pcData.getLibraryGeneratingTasks(),
+                createMainDexListTask,
+                retraceTask);
+    }
+
+    public PostCompilationData createJacocoTask(
+            @NonNull TaskFactory tasks,
+            @NonNull final VariantScope scope,
+            @NonNull final PostCompilationData pcData) {
+        AndroidTask<JacocoInstrumentTask> jacocoTask =
+                androidTasks.create(tasks, new JacocoInstrumentTask.ConfigAction(scope, pcData));
+
+        jacocoTask.optionalDependsOn(tasks, pcData.getClassGeneratingTasks());
+
+        final Copy agentTask = getJacocoAgentTask();
+        jacocoTask.dependsOn(tasks, agentTask);
+
+        // update dependency.
+        PostCompilationData pcData2 = new PostCompilationData();
+        pcData2.setClassGeneratingTasks(Collections.singletonList(jacocoTask.getName()));
+        pcData2.setLibraryGeneratingTasks(
+                Arrays.asList(pcData.getLibraryGeneratingTasks(), agentTask));
+
+        // update inputs
+        pcData2.setInputFilesCallable(new Callable<List<File>>() {
+            @Override
+            public List<File> call() {
+                return new ArrayList<File>(
+                        project.files(scope.getVariantData().jacocoInstrumentTask.getOutputDir())
+                                .getFiles());
+            }
+
+        });
+        pcData2.setInputDirCallable(new Callable<File>() {
+            @Override
+            public File call() {
+                return scope.getVariantData().jacocoInstrumentTask.getOutputDir();
+            }
+
+        });
+        pcData2.setInputLibrariesCallable(new Callable<List<File>>() {
+            @Override
+            public List<File> call() throws Exception {
+                List<File> files = null;
+                files = new ArrayList<File>(pcData.getInputLibrariesCallable().call());
+                files.add(new File(agentTask.getDestinationDir(), FILE_JACOCO_AGENT));
+                return files;
+            }
+
+        });
+
+        return pcData2;
+    }
+
+    public void createJackTask(
+            @NonNull TaskFactory tasks,
+            @NonNull VariantScope scope) {
+
+        // ----- Create Jill tasks -----
+        final AndroidTask<JillTask> jillRuntimeTask = androidTasks.create(tasks,
+                new JillTask.RuntimeTaskConfigAction(scope));
+
+        final AndroidTask<JillTask> jillPackagedTask = androidTasks.create(tasks,
+                new JillTask.PackagedConfigAction(scope));
+
+        jillPackagedTask.dependsOn(tasks,
+                scope.getVariantData().getVariantDependency().getPackageConfiguration()
+                        .getBuildDependencies());
+
+        // ----- Create Jack Task -----
+        AndroidTask<JackTask> jackTask = androidTasks.create(tasks,
+                new JackTask.ConfigAction(scope, isVerbose(), isDebugLog()));
+
+
+        // Jack is compiling and also providing the binary and mapping files.
+        setJavaCompilerTask(jackTask, tasks, scope);
+        jackTask.dependsOn(tasks, scope.getMergeJavaResourcesTask());
+        jackTask.dependsOn(tasks,
+                scope.getVariantData().sourceGenTask,
+                jillRuntimeTask,
+                jillPackagedTask,
+                // TODO - dependency information for the compile classpath is being lost.
+                // Add a temporary approximation
+                scope.getVariantData().getVariantDependency().getCompileConfiguration()
+                        .getBuildDependencies());
+
+    }
+
+    /**
+     * Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
+     *
+     * @param publishApk if true the generated APK gets published.
+     */
+    public void createPackagingTask(@NonNull TaskFactory tasks, @NonNull VariantScope variantScope,
+            boolean publishApk) {
+        final ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData();
+
+        GradleVariantConfiguration config = variantData.getVariantConfiguration();
+        boolean signedApk = variantData.isSigned();
+        File apkLocation = new File(variantScope.getGlobalScope().getApkLocation());
+
+        boolean multiOutput = variantData.getOutputs().size() > 1;
+
+        // loop on all outputs. The only difference will be the name of the task, and location
+        // of the generated data.
+        for (final ApkVariantOutputData variantOutputData : variantData.getOutputs()) {
+            VariantOutputScope variantOutputScope = variantOutputData.getScope();
+
+            final String outputName = variantOutputData.getFullName();
+
+            // When shrinking resources, rather than having the packaging task
+            // directly map to the packageOutputFile of ProcessAndroidResources,
+            // we insert the ShrinkResources task into the chain, such that its
+            // input is the ProcessAndroidResources packageOutputFile, and its
+            // output is what the PackageApplication task reads.
+            AndroidTask<ShrinkResources> shrinkTask = null;
+
+            if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() &&
+                    !config.getUseJack()) {
+                shrinkTask = androidTasks.create(
+                        tasks, new ShrinkResources.ConfigAction(variantOutputScope));
+                shrinkTask.dependsOn(tasks, variantScope.getObfuscationTask(),
+                        variantOutputScope.getManifestProcessorTask(),
+                        variantOutputScope.getProcessResourcesTask());
+            }
+
+            AndroidTask<PackageApplication> packageApp = androidTasks.create(
+                    tasks, new PackageApplication.ConfigAction(variantOutputScope));
+
+            packageApp.dependsOn(tasks, variantOutputScope.getProcessResourcesTask(),
+                    variantOutputScope.getVariantScope().getMergeJavaResourcesTask(),
+                    variantOutputScope.getVariantScope().getNdkBuildable());
+
+            packageApp.optionalDependsOn(
+                    tasks,
+                    shrinkTask,
+                    // TODO: When Jack is converted, add activeDexTask to VariantScope.
+                    variantOutputScope.getVariantScope().getDexTask(),
+                    variantOutputScope.getVariantScope().getJavaCompilerTask(),
+                    variantData.javaCompilerTask, // TODO: Remove when Jack is converted to AndroidTask.
+                    variantOutputData.packageSplitResourcesTask,
+                    variantOutputData.packageSplitAbiTask);
+
+            AndroidTask appTask = packageApp;
+
+            if (signedApk) {
+                if (variantData.getZipAlignEnabled()) {
+                    AndroidTask<ZipAlign> zipAlignTask = androidTasks.create(
+                            tasks, new ZipAlign.ConfigAction(variantOutputScope));
+                    zipAlignTask.dependsOn(tasks, packageApp);
+                    if (variantOutputData.splitZipAlign != null) {
+                        zipAlignTask.dependsOn(tasks, variantOutputData.splitZipAlign);
+                    }
+
+                    appTask = zipAlignTask;
+                }
+
+            }
+
+            checkState(variantData.assembleVariantTask != null);
+
+            // Add an assemble task
+            if (multiOutput) {
+                // create a task for this output
+                variantOutputData.assembleTask = createAssembleTask(variantOutputData);
+
+                // variant assemble task depends on each output assemble task.
+                variantData.assembleVariantTask.dependsOn(variantOutputData.assembleTask);
+            } else {
+                // single output
+                variantOutputData.assembleTask = variantData.assembleVariantTask;
+            }
+
+            if (!signedApk && variantOutputData.packageSplitResourcesTask != null) {
+                // in case we are not signing the resulting APKs and we have some pure splits
+                // we should manually copy them from the intermediate location to the final
+                // apk location unmodified.
+                Copy copyTask = project.getTasks().create(
+                        variantOutputScope.getTaskName("copySplit"), Copy.class);
+                copyTask.setDestinationDir(apkLocation);
+                copyTask.from(variantOutputData.packageSplitResourcesTask.getOutputDirectory());
+                variantOutputData.assembleTask.dependsOn(copyTask);
+                copyTask.mustRunAfter(appTask.getName());
+            }
+
+            variantOutputData.assembleTask.dependsOn(appTask.getName());
+
+            if (publishApk) {
+                final String projectBaseName = globalScope.getProjectBaseName();
+
+                // if this variant is the default publish config or we also should publish non
+                // defaults, proceed with declaring our artifacts.
+                if (getExtension().getDefaultPublishConfig().equals(outputName)) {
+                    appTask.configure(tasks, new Action<Task>() {
+                        @Override
+                        public void execute(Task packageTask) {
+                            project.getArtifacts().add("default",
+                                    new ApkPublishArtifact(projectBaseName,
+                                            null,
+                                            (FileSupplier) packageTask));
+                        }
+
+                    });
+
+                    for (FileSupplier outputFileProvider :
+                            variantOutputData.getSplitOutputFileSuppliers()) {
+                        project.getArtifacts().add("default",
+                                new ApkPublishArtifact(projectBaseName, null, outputFileProvider));
+                    }
+
+                    try {
+                        if (variantOutputData.getMetadataFile() != null) {
+                            project.getArtifacts().add("default-metadata",
+                                    new MetadataPublishArtifact(projectBaseName, null,
+                                            variantOutputData.getMetadataFile()));
+                        }
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+
+                    if (variantData.getMappingFileProvider() != null) {
+                        project.getArtifacts().add("default-mapping",
+                                new MappingPublishArtifact(projectBaseName, null,
+                                        variantData.getMappingFileProvider()));
+                    }
+                }
+
+                if (getExtension().getPublishNonDefault()) {
+                    appTask.configure(tasks, new Action<Task>() {
+                        @Override
+                        public void execute(Task packageTask) {
+                            project.getArtifacts().add(
+                                    variantData.getVariantDependency().getPublishConfiguration().getName(),
+                                    new ApkPublishArtifact(
+                                            projectBaseName,
+                                            null,
+                                            (FileSupplier) packageTask));
+                        }
+
+                    });
+
+                    for (FileSupplier outputFileProvider :
+                            variantOutputData.getSplitOutputFileSuppliers()) {
+                        project.getArtifacts().add(
+                                variantData.getVariantDependency().getPublishConfiguration().getName(),
+                                new ApkPublishArtifact(
+                                        projectBaseName,
+                                        null,
+                                        outputFileProvider));
+                    }
+
+                    try {
+                        if (variantOutputData.getMetadataFile() != null) {
+                            project.getArtifacts().add(
+                                    variantData.getVariantDependency().getMetadataConfiguration().getName(),
+                                    new MetadataPublishArtifact(
+                                            projectBaseName,
+                                            null,
+                                            variantOutputData.getMetadataFile()));
+                        }
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+
+                    if (variantData.getMappingFileProvider() != null) {
+                        project.getArtifacts().add(
+                                variantData.getVariantDependency().getMappingConfiguration().getName(),
+                                new MappingPublishArtifact(
+                                        projectBaseName,
+                                        null,
+                                        variantData.getMappingFileProvider()));
+                    }
+
+                    if (variantData.classesJarTask != null) {
+                        project.getArtifacts().add(
+                                variantData.getVariantDependency().getClassesConfiguration().getName(),
+                                variantData.classesJarTask);
+                    }
+                }
+            }
+        }
+
+        // create install task for the variant Data. This will deal with finding the
+        // right output if there are more than one.
+        // Add a task to install the application package
+        if (signedApk) {
+            AndroidTask<InstallVariantTask> installTask = androidTasks.create(
+                    tasks, new InstallVariantTask.ConfigAction(variantScope));
+            installTask.dependsOn(tasks, variantData.assembleVariantTask);
+        }
+
+        if (getExtension().getLintOptions().isCheckReleaseBuilds()) {
+            createLintVitalTask(variantData);
+        }
+
+        // add an uninstall task
+        final AndroidTask<UninstallTask> uninstallTask = androidTasks.create(
+                tasks, new UninstallTask.ConfigAction(variantScope));
+
+        tasks.named(UNINSTALL_ALL, new Action<Task>() {
+            @Override
+            public void execute(Task it) {
+                it.dependsOn(uninstallTask.getName());
+            }
+        });
+    }
+
+    public Task createAssembleTask(@NonNull final BaseVariantOutputData variantOutputData) {
+        Task assembleTask =
+                project.getTasks().create(variantOutputData.getScope().getTaskName("assemble"));
+        return assembleTask;
+    }
+
+    public Task createAssembleTask(TaskFactory tasks,
+            @NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+        Task assembleTask =
+                project.getTasks().create(variantData.getScope().getTaskName("assemble"));
+        return assembleTask;
+    }
+
+    public Copy getJacocoAgentTask() {
+        if (jacocoAgentTask == null) {
+            jacocoAgentTask = project.getTasks().create("unzipJacocoAgent", Copy.class);
+            jacocoAgentTask.from(new Callable<List<FileTree>>() {
+                @Override
+                public List<FileTree> call() throws Exception {
+                    return Lists.newArrayList(Iterables.transform(
+                            project.getConfigurations().getByName(
+                                    JacocoPlugin.AGENT_CONFIGURATION_NAME),
+                            new Function<Object, FileTree>() {
+                                @Override
+                                public FileTree apply(@Nullable Object it) {
+                                    return project.zipTree(it);
+                                }
+                            }));
+                }
+            });
+            jacocoAgentTask.include(FILE_JACOCO_AGENT);
+            jacocoAgentTask.into(new File(getGlobalScope().getIntermediatesDir(), "jacoco"));
+        }
+
+        return jacocoAgentTask;
+    }
+
+    /**
+     * creates a zip align. This does not use convention mapping, and is meant to let other plugin
+     * create zip align tasks.
+     *
+     * @param name       the name of the task
+     * @param inputFile  the input file
+     * @param outputFile the output file
+     * @return the task
+     */
+    @NonNull
+    public ZipAlign createZipAlignTask(
+            @NonNull String name,
+            @NonNull File inputFile,
+            @NonNull File outputFile) {
+        // Add a task to zip align application package
+        ZipAlign zipAlignTask = project.getTasks().create(name, ZipAlign.class);
+
+        zipAlignTask.setInputFile(inputFile);
+        zipAlignTask.setOutputFile(outputFile);
+        ConventionMappingHelper.map(zipAlignTask, "zipAlignExe", new Callable<File>() {
+            @Override
+            public File call() throws Exception {
+                final TargetInfo info = androidBuilder.getTargetInfo();
+                if (info != null) {
+                    String path = info.getBuildTools().getPath(ZIP_ALIGN);
+                    if (path != null) {
+                        return new File(path);
+                    }
+                }
+
+                return null;
+            }
+        });
+
+        return zipAlignTask;
+    }
+
+    /**
+     * Creates the proguarding task for the given Variant if necessary.
+     *
+     * @return null if the proguard task was not created, otherwise the expected outputFile.
+     */
+    @Nullable
+    public File maybeCreateProguardTasks(
+            @NonNull final TaskFactory tasks,
+            @NonNull VariantScope scope,
+            @NonNull final PostCompilationData pcData) {
+        if (!scope.getVariantData().getVariantConfiguration().isMinifyEnabled()) {
+            return null;
+        }
+
+        final AndroidTask<AndroidProGuardTask> proguardTask = androidTasks.create(
+                tasks, new AndroidProGuardTask.ConfigAction(scope, pcData));
+        scope.setObfuscationTask(proguardTask);
+        scope.setJavaResourcesProvider(JavaResourcesProvider.Adapter.build(tasks, proguardTask));
+
+        // update dependency.
+        proguardTask.optionalDependsOn(tasks, pcData.getClassGeneratingTasks(),
+                pcData.getLibraryGeneratingTasks());
+        pcData.setLibraryGeneratingTasks(Collections.singletonList(proguardTask.getName()));
+        pcData.setClassGeneratingTasks(Collections.singletonList(proguardTask.getName()));
+
+        // Return output file.
+        return scope.getProguardOutputFile();
+    }
+
+    public void createReportTasks(
+            List<BaseVariantData<? extends BaseVariantOutputData>> variantDataList) {
+        DependencyReportTask dependencyReportTask =
+                project.getTasks().create("androidDependencies", DependencyReportTask.class);
+        dependencyReportTask.setDescription("Displays the Android dependencies of the project.");
+        dependencyReportTask.setVariants(variantDataList);
+        dependencyReportTask.setGroup(ANDROID_GROUP);
+
+        SigningReportTask signingReportTask =
+                project.getTasks().create("signingReport", SigningReportTask.class);
+        signingReportTask.setDescription("Displays the signing info for each variant.");
+        signingReportTask.setVariants(variantDataList);
+        signingReportTask.setGroup(ANDROID_GROUP);
+    }
+
+    public void createAnchorTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+        createPreBuildTasks(scope);
+
+        // also create sourceGenTask
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        scope.setSourceGenTask(androidTasks.create(tasks,
+                scope.getTaskName("generate", "Sources"),
+                Task.class,
+                new Action<Task>() {
+                    @Override
+                    public void execute(Task task) {
+                        variantData.sourceGenTask = task;
+                    }
+                }));
+        // and resGenTask
+        scope.setResourceGenTask(androidTasks.create(tasks,
+                scope.getTaskName("generate", "Resources"),
+                Task.class,
+                new Action<Task>() {
+                    @Override
+                    public void execute(Task task) {
+                        variantData.resourceGenTask = task;
+                    }
+
+                }));
+
+        scope.setAssetGenTask(androidTasks.create(tasks,
+                scope.getTaskName("generate", "Assets"),
+                Task.class,
+                new Action<Task>() {
+                    @Override
+                    public void execute(Task task) {
+                        variantData.assetGenTask = task;
+                    }
+                }));
+
+        // and compile task
+        createCompileAnchorTask(tasks, scope);
+    }
+
+    private void createPreBuildTasks(@NonNull VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        variantData.preBuildTask = project.getTasks().create(scope.getTaskName("pre", "Build"));
+        variantData.preBuildTask.dependsOn(MAIN_PREBUILD);
+
+        PrepareDependenciesTask prepareDependenciesTask = project.getTasks().create(
+                scope.getTaskName("prepare", "Dependencies"), PrepareDependenciesTask.class);
+
+        variantData.prepareDependenciesTask = prepareDependenciesTask;
+        prepareDependenciesTask.dependsOn(variantData.preBuildTask);
+
+        prepareDependenciesTask.setAndroidBuilder(androidBuilder);
+        prepareDependenciesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+        prepareDependenciesTask.setVariant(variantData);
+
+        // for all libraries required by the configurations of this variant, make this task
+        // depend on all the tasks preparing these libraries.
+        VariantDependencies configurationDependencies = variantData.getVariantDependency();
+        prepareDependenciesTask.addChecker(configurationDependencies.getChecker());
+
+        for (LibraryDependencyImpl lib : configurationDependencies.getLibraries()) {
+            dependencyManager.addDependencyToPrepareTask(variantData, prepareDependenciesTask, lib);
+        }
+    }
+
+    private void createCompileAnchorTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        scope.setCompileTask(androidTasks.create(tasks, new TaskConfigAction<Task>() {
+            @Override
+            public String getName() {
+                return scope.getTaskName("compile", "Sources");
+            }
+
+            @Override
+            public Class<Task> getType() {
+                return Task.class;
+            }
+
+            @Override
+            public void execute(Task task) {
+                variantData.compileTask = task;
+                variantData.compileTask.setGroup(BUILD_GROUP);
+            }
+        }));
+        variantData.assembleVariantTask.dependsOn(scope.getCompileTask().getName());
+    }
+
+    public void createCheckManifestTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+        final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        final String name = variantData.getVariantConfiguration().getFullName();
+        scope.setCheckManifestTask(androidTasks.create(tasks,
+                scope.getTaskName("check", "Manifest"),
+                CheckManifest.class,
+                new Action<CheckManifest>() {
+                    @Override
+                    public void execute(CheckManifest checkManifestTask) {
+                        variantData.checkManifestTask = checkManifestTask;
+                        checkManifestTask.setVariantName(name);
+                        ConventionMappingHelper.map(checkManifestTask, "manifest",
+                                new Callable<File>() {
+                                    @Override
+                                    public File call() throws Exception {
+                                        return variantData.getVariantConfiguration()
+                                                .getDefaultSourceSet().getManifestFile();
+                                    }
+                                });
+                    }
+
+                }));
+        scope.getCheckManifestTask().dependsOn(tasks, variantData.preBuildTask);
+        variantData.prepareDependenciesTask.dependsOn(scope.getCheckManifestTask().getName());
+    }
+
+    public static void optionalDependsOn(@NonNull Task main, Task... dependencies) {
+        for (Task dependency : dependencies) {
+            if (dependency != null) {
+                main.dependsOn(dependency);
+            }
+
+        }
+
+    }
+
+    public static void optionalDependsOn(@NonNull Task main, @NonNull List<?> dependencies) {
+        for (Object dependency : dependencies) {
+            if (dependency != null) {
+                main.dependsOn(dependency);
+            }
+
+        }
+
+    }
+
+    @NonNull
+    private static List<ManifestDependencyImpl> getManifestDependencies(
+            List<LibraryDependency> libraries) {
+
+        List<ManifestDependencyImpl> list = Lists.newArrayListWithCapacity(libraries.size());
+
+        for (LibraryDependency lib : libraries) {
+            // get the dependencies
+            List<ManifestDependencyImpl> children = getManifestDependencies(lib.getDependencies());
+            list.add(new ManifestDependencyImpl(lib.getName(), lib.getManifest(), children));
+        }
+
+        return list;
+    }
+
+    @NonNull
+    protected Logger getLogger() {
+        return logger;
+    }
+
+    @NonNull
+    protected AndroidTaskRegistry getAndroidTasks() {
+        return androidTasks;
+    }
+
+    private File getDefaultProguardFile(String name) {
+        File sdkDir = sdkHandler.getAndCheckSdkFolder();
+        return new File(sdkDir,
+                SdkConstants.FD_TOOLS + File.separatorChar + SdkConstants.FD_PROGUARD
+                        + File.separatorChar + name);
+    }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java
index 1111641..ed65971 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java
@@ -16,12 +16,13 @@
 
 package com.android.build.gradle.internal;
 
-import static com.android.builder.core.BuilderConstants.CONNECTED;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.TestExtension;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.TestAndroidConfig;
+import com.android.build.gradle.internal.scope.AndroidTask;
 import com.android.build.gradle.internal.scope.VariantScope;
 import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
 import com.android.build.gradle.internal.test.TestApplicationTestData;
@@ -43,11 +44,11 @@
 import org.gradle.api.artifacts.dsl.DependencyHandler;
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
 
-import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
-
-
 import java.io.File;
-import java.util.Locale;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
 
 import proguard.ParseException;
 
@@ -60,7 +61,7 @@
 
     public TestApplicationTaskManager(Project project,
             AndroidBuilder androidBuilder,
-            BaseExtension extension,
+            AndroidConfig extension,
             SdkHandler sdkHandler,
             DependencyManager dependencyManager,
             ToolingModelBuilderRegistry toolingRegistry) {
@@ -68,16 +69,16 @@
     }
 
     @Override
-    public void createTasksForVariantData(TaskFactory tasks,
+    public void createTasksForVariantData(@NonNull TaskFactory tasks,
             @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
 
         super.createTasksForVariantData(tasks, variantData);
 
         // create a new configuration with the target application coordinates.
-        Configuration testTarget = project.getConfigurations().create("testTarget");
+        final Configuration testTarget = project.getConfigurations().create("testTarget");
 
         DependencyHandler dependencyHandler = project.getDependencies();
-        TestExtension testExtension = (TestExtension) extension;
+        TestAndroidConfig testExtension = (TestAndroidConfig) extension;
         dependencyHandler.add("testTarget",
                 dependencyHandler.project(
                         ImmutableMap.of(
@@ -85,7 +86,7 @@
                                 "configuration", testExtension.getTargetVariant())));
 
         // and create the configuration for the project's metadata.
-        Configuration testTargetMetadata = project.getConfigurations().create("testTargetMetadata");
+        final Configuration testTargetMetadata = project.getConfigurations().create("testTargetMetadata");
 
         dependencyHandler.add("testTargetMetadata", dependencyHandler.project(
                         ImmutableMap.of(
@@ -97,77 +98,39 @@
                 variantData, testTarget, testTargetMetadata, androidBuilder);
 
         // create the test connected check task.
-        DeviceProviderInstrumentTestTask testConnectedCheck =
-                createDeviceProviderInstrumentTestTask(
-                        project.getName() + "ConnectedCheck",
-                        "Installs and runs the tests for " + variantData.getDescription()
-                                + " on connected devices.",
-                        DeviceProviderInstrumentTestTask.class,
-                        testData,
-                        ImmutableList.of(variantData.assembleVariantTask),
-                        new ConnectedDeviceProvider(
-                                sdkHandler.getSdkInfo().getAdb(),
-                                new LoggerWrapper(getLogger())),
-                        CONNECTED
-                );
+        AndroidTask<DeviceProviderInstrumentTestTask> testConnectedCheck =
+                getAndroidTasks().create(
+                        tasks,
+                        new DeviceProviderInstrumentTestTask.ConfigAction(
+                                variantData.getScope(),
+                                new ConnectedDeviceProvider(
+                                        sdkHandler.getSdkInfo().getAdb(),
+                                        new LoggerWrapper(getLogger())),
+                                testData));
 
         // make the test application connectedCheck depends on the configuration added above so
         // we can retrieve its artifacts
-        testConnectedCheck.dependsOn(testTarget);
-        testConnectedCheck.dependsOn(testTargetMetadata);
+
+        testConnectedCheck.dependsOn(tasks,
+                testTarget,
+                testTargetMetadata,
+                variantData.assembleVariantTask);
 
         // make the main ConnectedCheck task depends on this test connectedCheck
         Task connectedCheck = tasks.named(CONNECTED_CHECK);
         if (connectedCheck != null) {
-            connectedCheck.dependsOn(testConnectedCheck);
+            connectedCheck.dependsOn(testConnectedCheck.getName());
         }
     }
 
     @Override
     @Nullable
     public File maybeCreateProguardTasks(
-            TaskFactory tasks,
-            VariantScope scope,
+            @NonNull final TaskFactory tasks,
+            @NonNull final VariantScope scope,
             @NonNull final PostCompilationData pcData) {
-        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
-
-        final TestModuleProGuardTask proguardTask = project.getTasks().create(
-                "proguard"+ variantData.getVariantConfiguration().getFullName().toUpperCase(
-                        Locale.getDefault()),
-                TestModuleProGuardTask.class);
-
-        variantData.obfuscationTask = proguardTask;
-
-        // --- Output File ---
-
-        final File outFile = variantData instanceof LibraryVariantData ?
-                project.file(String.format("%s/%s/%s/%s/classes.jar",
-                        project.getBuildDir(),
-                        AndroidProject.FD_INTERMEDIATES,
-                        TaskManager.DIR_BUNDLES,
-                        variantData.getVariantConfiguration().getDirName())) :
-                project.file(String.format("%s/%s/classes-proguard/%s/classes.jar",
-                        project.getBuildDir(),
-                        AndroidProject.FD_INTERMEDIATES,
-                        variantData.getVariantConfiguration().getDirName()));
-        variantData.obfuscatedClassesJar = outFile;
-
         DependencyHandler dependencyHandler = project.getDependencies();
-        TestExtension testExtension = (TestExtension) extension;
-
-        // and create the configuration for the project's classes.jar file.
-        Configuration testClassesMapping = project.getConfigurations().create("testTargetClasses");
-
-        dependencyHandler.add("testTargetClasses", dependencyHandler.project(
-                ImmutableMap.of(
-                        "path", testExtension.getTargetProjectPath(),
-                        "configuration", testExtension.getTargetVariant() + "-classes"
-                )));
-
-        // Input the original .class files so the compiler can compile the test code correctly.
-        proguardTask.setClassesConfiguration(testClassesMapping);
-
-        // and create the configuration for the project's mapping file.
+        TestAndroidConfig testExtension = (TestAndroidConfig) extension;
         Configuration testTargetMapping = project.getConfigurations().create("testTargetMapping");
 
         dependencyHandler.add("testTargetMapping", dependencyHandler.project(
@@ -176,8 +139,24 @@
                         "configuration", testExtension.getTargetVariant() + "-mapping"
                 )));
 
+        if (testTargetMapping.getFiles().isEmpty()
+                || scope.getVariantConfiguration().getProvidedOnlyJars().isEmpty()) {
+            return null;
+        }
+
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+        final TestModuleProGuardTask proguardTask = project.getTasks().create(
+                scope.getTaskName("proguard"),
+                TestModuleProGuardTask.class);
+
+        variantData.obfuscationTask = proguardTask;
+        proguardTask.setLogger(getLogger());
+
+        // and create the configuration for the project's mapping file.
         // Input the mapping from the tested app so that we can deal with obfuscated code.
         proguardTask.setMappingConfiguration(testTargetMapping);
+        proguardTask.setVariantConfiguration(scope.getVariantConfiguration());
 
         // --- Proguard Config ---
 
@@ -210,17 +189,40 @@
         });
 
         try {
+
             // injar: the compilation output
-            proguardTask.injars(pcData.getInputDir());
+            proguardTask.injars(pcData.getInputDirCallable());
+            if (pcData.getJavaResourcesInputDirCallable() != null) {
+                proguardTask.injars(pcData.getJavaResourcesInputDirCallable());
+            }
+
+            // injar: the packaged dependencies
+            LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1);
+            map.put("filter", "!META-INF/MANIFEST.MF");
+            proguardTask.injars(map, new Callable<Set<File>>() {
+                @Override
+                public Set<File> call() throws Exception {
+                    return scope.getVariantConfiguration().getPackagedJars();
+                }
+            });
+
+            proguardTask.libraryjars(new Callable<List<File>>() {
+                @Override
+                public List<File> call() throws Exception {
+                    return scope.getVariantConfiguration().getProvidedOnlyJars();
+                }
+            });
+
 
             // All -dontwarn rules for test dependencies should go in here:
             proguardTask.configuration(
                     variantData.getVariantConfiguration().getTestProguardFiles());
 
 
+
             // --- Out files ---
 
-            proguardTask.outjars(outFile);
+            proguardTask.outjars(scope.getProguardOutputFile());
 
             final File proguardOut = project.file(
                     new File(project.getBuildDir(),
@@ -240,18 +242,21 @@
             proguardTask.doFirst(new Action<Task>() {
                 @Override
                 public void execute(Task task) {
-                    proguardOut.mkdirs();
+                    if (!proguardOut.mkdirs()) {
+                        throw new RuntimeException(
+                                "Cannot create proguard output folder " + proguardOut);
+                    }
                 }
             });
 
             // update dependency.
-            optionalDependsOn(proguardTask, pcData.getClassGeneratingTask());
-            optionalDependsOn(proguardTask, pcData.getLibraryGeneratingTask());
-            pcData.setLibraryGeneratingTask(ImmutableList.of(proguardTask));
-            pcData.setClassGeneratingTask(ImmutableList.of(proguardTask));
+            optionalDependsOn(proguardTask, pcData.getClassGeneratingTasks());
+            optionalDependsOn(proguardTask, pcData.getLibraryGeneratingTasks());
+            pcData.setLibraryGeneratingTasks(ImmutableList.of(proguardTask));
+            pcData.setClassGeneratingTasks(ImmutableList.of(proguardTask));
 
             // Update the inputs
-            return outFile;
+            return scope.getProguardOutputFile();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java
index 873dfcd..fd2e2d1 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java
@@ -16,38 +16,26 @@
 
 package com.android.build.gradle.internal;
 
-import static com.android.build.OutputFile.NO_FILTER;
-import static com.android.builder.core.BuilderConstants.DEBUG;
 import static com.android.builder.core.BuilderConstants.LINT;
 import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.LIBRARY;
 import static com.android.builder.core.VariantType.UNIT_TEST;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_KEY_ALIAS;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_KEY_PASSWORD;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_FILE;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_PASSWORD;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_TYPE;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.TestedExtension;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.AndroidGradleOptions;
+import com.android.build.gradle.TestAndroidConfig;
+import com.android.build.gradle.TestedAndroidConfig;
 import com.android.build.gradle.api.AndroidSourceSet;
-import com.android.build.gradle.api.BaseVariant;
 import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
 import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
-import com.android.build.gradle.internal.api.TestVariantImpl;
-import com.android.build.gradle.internal.api.TestedVariant;
-import com.android.build.gradle.internal.api.UnitTestVariantImpl;
 import com.android.build.gradle.internal.api.VariantFilter;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.internal.dependency.VariantDependencies;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
-import com.android.build.gradle.internal.dsl.SigningConfig;
-import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
 import com.android.build.gradle.internal.profile.SpanRecorders;
-import com.android.build.gradle.internal.variant.ApplicationVariantFactory;
 import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.build.gradle.internal.variant.BaseVariantOutputData;
 import com.android.build.gradle.internal.variant.TestVariantData;
@@ -55,6 +43,8 @@
 import com.android.build.gradle.internal.variant.VariantFactory;
 import com.android.builder.core.AndroidBuilder;
 import com.android.builder.core.VariantType;
+import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.SigningConfig;
 import com.android.builder.profile.ExecutionType;
 import com.android.builder.profile.Recorder;
 import com.android.builder.profile.ThreadRecorder;
@@ -74,9 +64,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
-
-import groovy.lang.Closure;
 
 /**
  * Class to create, manage variants.
@@ -91,7 +78,7 @@
     @NonNull
     private final AndroidBuilder androidBuilder;
     @NonNull
-    private final BaseExtension extension;
+    private final AndroidConfig extension;
     @NonNull
     private final VariantFactory variantFactory;
     @NonNull
@@ -99,11 +86,11 @@
     @NonNull
     private final Instantiator instantiator;
     @NonNull
-    private ProductFlavorData<ProductFlavor> defaultConfigData;
+    private ProductFlavorData<CoreProductFlavor> defaultConfigData;
     @NonNull
     private final Map<String, BuildTypeData> buildTypes = Maps.newHashMap();
     @NonNull
-    private final Map<String, ProductFlavorData<GroupableProductFlavor>> productFlavors = Maps.newHashMap();
+    private final Map<String, ProductFlavorData<CoreProductFlavor>> productFlavors = Maps.newHashMap();
     @NonNull
     private final Map<String, SigningConfig> signingConfigs = Maps.newHashMap();
 
@@ -120,7 +107,7 @@
     public VariantManager(
             @NonNull Project project,
             @NonNull AndroidBuilder androidBuilder,
-            @NonNull BaseExtension extension,
+            @NonNull AndroidConfig extension,
             @NonNull VariantFactory variantFactory,
             @NonNull TaskManager taskManager,
             @NonNull Instantiator instantiator) {
@@ -145,7 +132,7 @@
                             .getByName(UNIT_TEST.getPrefix());
         }
 
-        defaultConfigData = new ProductFlavorData<ProductFlavor>(
+        defaultConfigData = new ProductFlavorData<CoreProductFlavor>(
                 extension.getDefaultConfig(), mainSourceSet,
                 androidTestSourceSet, unitTestSourceSet, project);
         signingOverride = createSigningOverride();
@@ -153,7 +140,7 @@
 
     @NonNull
     @Override
-    public ProductFlavorData<ProductFlavor> getDefaultConfig() {
+    public ProductFlavorData<CoreProductFlavor> getDefaultConfig() {
         return defaultConfigData;
     }
 
@@ -165,7 +152,7 @@
 
     @Override
     @NonNull
-    public Map<String, ProductFlavorData<GroupableProductFlavor>> getProductFlavors() {
+    public Map<String, ProductFlavorData<CoreProductFlavor>> getProductFlavors() {
         return productFlavors;
     }
 
@@ -184,9 +171,7 @@
      * and adding it to the map.
      * @param buildType the build type.
      */
-    public void addBuildType(@NonNull BuildType buildType) {
-        buildType.init(signingConfigs.get(DEBUG));
-
+    public void addBuildType(@NonNull CoreBuildType buildType) {
         String name = buildType.getName();
         checkName(name, "BuildType");
 
@@ -194,18 +179,17 @@
             throw new RuntimeException("BuildType names cannot collide with ProductFlavor names");
         }
 
-        DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSetsContainer().maybeCreate(name);
+        DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSets().maybeCreate(name);
 
         DefaultAndroidSourceSet unitTestSourceSet = null;
         if (variantFactory.hasTestScope()) {
             unitTestSourceSet = (DefaultAndroidSourceSet) extension
-                    .getSourceSetsContainer().maybeCreate(
+                    .getSourceSets().maybeCreate(
                             computeSourceSetName(buildType.getName(), UNIT_TEST));
         }
 
         BuildTypeData buildTypeData = new BuildTypeData(
                 buildType, project, mainSourceSet, unitTestSourceSet);
-        project.getTasks().getByName("assemble").dependsOn(buildTypeData.getAssembleTask());
 
         buildTypes.put(name, buildTypeData);
     }
@@ -216,7 +200,7 @@
      *
      * @param productFlavor the product flavor
      */
-    public void addProductFlavor(@NonNull GroupableProductFlavor productFlavor) {
+    public void addProductFlavor(@NonNull CoreProductFlavor productFlavor) {
         String name = productFlavor.getName();
         checkName(name, "ProductFlavor");
 
@@ -224,22 +208,22 @@
             throw new RuntimeException("ProductFlavor names cannot collide with BuildType names");
         }
 
-        DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSetsContainer().maybeCreate(
+        DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSets().maybeCreate(
                 productFlavor.getName());
 
         DefaultAndroidSourceSet androidTestSourceSet = null;
         DefaultAndroidSourceSet unitTestSourceSet = null;
         if (variantFactory.hasTestScope()) {
             androidTestSourceSet = (DefaultAndroidSourceSet) extension
-                    .getSourceSetsContainer().maybeCreate(
+                    .getSourceSets().maybeCreate(
                             computeSourceSetName(productFlavor.getName(), ANDROID_TEST));
             unitTestSourceSet = (DefaultAndroidSourceSet) extension
-                    .getSourceSetsContainer().maybeCreate(
+                    .getSourceSets().maybeCreate(
                             computeSourceSetName(productFlavor.getName(), UNIT_TEST));
         }
 
-        ProductFlavorData<GroupableProductFlavor> productFlavorData =
-                new ProductFlavorData<GroupableProductFlavor>(
+        ProductFlavorData<CoreProductFlavor> productFlavorData =
+                new ProductFlavorData<CoreProductFlavor>(
                         productFlavor,
                         mainSourceSet,
                         androidTestSourceSet,
@@ -266,7 +250,14 @@
 
         final TaskFactory tasks = new TaskContainerAdaptor(project.getTasks());
         if (variantDataList.isEmpty()) {
-            populateVariantDataList();
+            ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_VARIANTS,
+                    new Recorder.Block<Void>() {
+                        @Override
+                        public Void call() throws Exception {
+                            populateVariantDataList();
+                            return null;
+                        }
+                    });
         }
 
         // Create top level test tasks.
@@ -280,6 +271,7 @@
                 });
 
         for (final BaseVariantData<? extends BaseVariantOutputData> variantData : variantDataList) {
+
             SpanRecorders.record(project, ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
                     new Recorder.Block<Void>() {
                         @Override
@@ -291,9 +283,6 @@
                     new Recorder.Property(SpanRecorders.VARIANT, variantData.getName()));
         }
 
-        // Create the variant API objects after the tasks have been created!
-        createApiObjects();
-
         taskManager.createReportTasks(variantDataList);
     }
 
@@ -321,7 +310,7 @@
 
                 // each flavor
                 GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
-                for (GroupableProductFlavor flavor : variantConfig.getProductFlavors()) {
+                for (CoreProductFlavor flavor : variantConfig.getProductFlavors()) {
                     productFlavors.get(flavor.getName()).getAssembleTask()
                              .dependsOn(variantData.assembleVariantTask);
                 }
@@ -357,8 +346,20 @@
      * Create tasks for the specified variantData.
      */
     public void createTasksForVariantData(
-            TaskFactory tasks,
-            BaseVariantData<? extends BaseVariantOutputData> variantData) {
+            final TaskFactory tasks,
+            final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+
+        // Add dependency of assemble task on assemble build type task.
+        tasks.named("assemble", new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                BuildTypeData buildTypeData = buildTypes.get(
+                                variantData.getVariantConfiguration().getBuildType().getName());
+                task.dependsOn(buildTypeData.getAssembleTask());
+            }
+        });
+
+
         VariantType variantType = variantData.getType();
 
         createAssembleTaskForVariantData(tasks, variantData);
@@ -379,9 +380,8 @@
                 testVariantProviders.add(buildTypeConfigurationProvider);
             }
 
-            for (com.android.build.gradle.api.GroupableProductFlavor productFlavor :
-                    testVariantConfig.getProductFlavors()) {
-                ProductFlavorData<GroupableProductFlavor> data =
+            for (CoreProductFlavor productFlavor : testVariantConfig.getProductFlavors()) {
+                ProductFlavorData<CoreProductFlavor> data =
                         productFlavors.get(productFlavor.getName());
                 testVariantProviders.add(data.getTestConfigurationProvider(variantType));
             }
@@ -390,8 +390,9 @@
             testVariantProviders.add(defaultConfigData.getTestConfigurationProvider(variantType));
 
             assert(testVariantConfig.getTestedConfig() != null);
+            VariantDependencies parentVariant = null;
             if (testVariantConfig.getTestedConfig().getType() == VariantType.LIBRARY) {
-                testVariantProviders.add(testedVariantData.getVariantDependency());
+                parentVariant = testedVariantData.getVariantDependency();
             }
 
             // If the variant being tested is a library variant, VariantDependencies must be
@@ -401,6 +402,7 @@
                     project, testVariantConfig.getFullName(),
                     false /*publishVariant*/,
                     variantType,
+                    parentVariant,
                     testVariantProviders.toArray(
                             new ConfigurationProvider[testVariantProviders.size()]));
             variantData.setVariantDependency(variantDep);
@@ -412,7 +414,8 @@
                             taskManager.resolveDependencies(variantDep,
                                     testVariantConfig.getTestedConfig().getType() == VariantType.LIBRARY
                                             ? null
-                                            : testedVariantData.getVariantDependency());
+                                            : testedVariantData.getVariantDependency(),
+                                    null /*testedProjectPath*/);
                             return null;
                         }
                     },
@@ -438,53 +441,44 @@
      */
     public void populateVariantDataList() {
         if (productFlavors.isEmpty()) {
-            createVariantDataForProductFlavors(
-                    Collections.<com.android.build.gradle.api.GroupableProductFlavor>emptyList());
+            createVariantDataForProductFlavors(Collections.<ProductFlavor>emptyList());
         } else {
             List<String> flavorDimensionList = extension.getFlavorDimensionList();
 
-            // Create iterable to get GroupableProductFlavor from ProductFlavorData.
-            Iterable<GroupableProductFlavor> flavorDsl =
+            // Create iterable to get GradleProductFlavor from ProductFlavorData.
+            Iterable<CoreProductFlavor> flavorDsl =
                     Iterables.transform(
                             productFlavors.values(),
-                            new Function<ProductFlavorData<GroupableProductFlavor>, GroupableProductFlavor>() {
+                            new Function<ProductFlavorData<CoreProductFlavor>, CoreProductFlavor>() {
                                 @Override
-                                public GroupableProductFlavor apply(
-                                        ProductFlavorData<GroupableProductFlavor> data) {
+                                public CoreProductFlavor apply(
+                                        ProductFlavorData<CoreProductFlavor> data) {
                                     return data.getProductFlavor();
                                 }
                             });
 
             // Get a list of all combinations of product flavors.
-            List<ProductFlavorCombo> flavorComboList = ProductFlavorCombo.createCombinations(
-                    flavorDimensionList,
-                    flavorDsl);
+            List<ProductFlavorCombo<CoreProductFlavor>> flavorComboList =
+                    ProductFlavorCombo.createCombinations(
+                            flavorDimensionList,
+                            flavorDsl);
 
-            for (ProductFlavorCombo flavorCombo : flavorComboList) {
-                createVariantDataForProductFlavors(flavorCombo.getFlavorList());
+            for (ProductFlavorCombo<CoreProductFlavor>  flavorCombo : flavorComboList) {
+                //noinspection unchecked
+                createVariantDataForProductFlavors(
+                        (List<ProductFlavor>) (List) flavorCombo.getFlavorList());
             }
         }
     }
 
     /**
-     * Create a VariantData for a specific combination of BuildType and GroupableProductFlavor list.
+     * Create a VariantData for a specific combination of BuildType and ProductFlavor list.
      */
     public BaseVariantData<? extends BaseVariantOutputData> createVariantData(
             @NonNull com.android.builder.model.BuildType buildType,
-            @NonNull List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavorList) {
-        Splits splits = extension.getSplits();
-        Set<String> densities = splits.getDensityFilters();
-        Set<String> abis = splits.getAbiFilters();
-
-        // check against potentially empty lists. We always need to generate at least one output
-        densities = densities.isEmpty() ? Collections.singleton(NO_FILTER) : densities;
-        abis = abis.isEmpty() ? Collections.singleton(NO_FILTER) : abis;
-
+            @NonNull List<? extends ProductFlavor> productFlavorList) {
         BuildTypeData buildTypeData = buildTypes.get(buildType.getName());
 
-        Set<String> compatibleScreens = extension.getSplits().getDensity()
-                .getCompatibleScreens();
-
         GradleVariantConfiguration variantConfig = new GradleVariantConfiguration(
                 defaultConfigData.getProductFlavor(),
                 defaultConfigData.getSourceSet(),
@@ -493,14 +487,21 @@
                 variantFactory.getVariantConfigurationType(),
                 signingOverride);
 
+        if (variantConfig.getType() == LIBRARY && variantConfig.getUseJack()) {
+            project.getLogger().warn(
+                    "{}, {}: Jack compiler is not supported in library projects, falling back to javac.",
+                    project.getPath(),
+                    variantConfig.getFullName());
+        }
+
         // sourceSetContainer in case we are creating variant specific sourceSets.
         NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer = extension
-                .getSourceSetsContainer();
+                .getSourceSets();
 
         // We must first add the flavors to the variant config, in order to get the proper
         // variant-specific and multi-flavor name as we add/create the variant providers later.
-        for (com.android.build.gradle.api.GroupableProductFlavor productFlavor : productFlavorList) {
-            ProductFlavorData<GroupableProductFlavor> data = productFlavors.get(
+        for (ProductFlavor productFlavor : productFlavorList) {
+            ProductFlavorData<CoreProductFlavor> data = productFlavors.get(
                     productFlavor.getName());
 
             String dimensionName = productFlavor.getDimension();
@@ -544,7 +545,7 @@
         }
 
         // 4. the flavors.
-        for (com.android.build.gradle.api.GroupableProductFlavor productFlavor : productFlavorList) {
+        for (ProductFlavor productFlavor : productFlavorList) {
             variantProviders.add(productFlavors.get(productFlavor.getName()).getMainProvider());
         }
 
@@ -552,13 +553,14 @@
         variantProviders.add(defaultConfigData.getMainProvider());
 
         // Done. Create the variant and get its internal storage object.
-        BaseVariantData<?> variantData = variantFactory.createVariantData(variantConfig,
-                densities, abis, compatibleScreens, taskManager);
+        BaseVariantData<?> variantData =
+                variantFactory.createVariantData(variantConfig, taskManager);
 
         final VariantDependencies variantDep = VariantDependencies.compute(
                 project, variantConfig.getFullName(),
                 isVariantPublished(),
                 variantData.getType(),
+                null,
                 variantProviders.toArray(new ConfigurationProvider[variantProviders.size()]));
         variantData.setVariantDependency(variantDep);
 
@@ -569,11 +571,18 @@
                     variantDep.getPackageConfiguration().getName(), COM_ANDROID_SUPPORT_MULTIDEX);
         }
 
+        final String testedProjectPath = extension instanceof TestAndroidConfig ?
+                ((TestAndroidConfig) extension).getTargetProjectPath() :
+                null;
+
         SpanRecorders.record(project, ExecutionType.RESOLVE_DEPENDENCIES,
                 new Recorder.Block<Void>() {
                     @Override
                     public Void call() {
-                        taskManager.resolveDependencies(variantDep, null);
+                        taskManager.resolveDependencies(
+                                variantDep,
+                                null /*testedVariantDeps*/,
+                                testedProjectPath);
                         return null;
                     }
                 }, new Recorder.Property(SpanRecorders.VARIANT, variantConfig.getFullName()));
@@ -584,7 +593,7 @@
     }
 
     private static void createCompoundSourceSets(
-            @NonNull List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavorList,
+            @NonNull List<? extends ProductFlavor> productFlavorList,
             GradleVariantConfiguration variantConfig,
             NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer) {
         if (!productFlavorList.isEmpty() && !variantConfig.getType().isSingleBuildType()) {
@@ -631,12 +640,12 @@
     public TestVariantData createTestVariantData(
             BaseVariantData testedVariantData,
             VariantType type) {
-        ProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
-        BuildType buildType = testedVariantData.getVariantConfiguration().getBuildType();
+        CoreProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
+        CoreBuildType buildType = testedVariantData.getVariantConfiguration().getBuildType();
         BuildTypeData buildTypeData = buildTypes.get(buildType.getName());
 
         GradleVariantConfiguration testedConfig = testedVariantData.getVariantConfiguration();
-        List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavorList = testedConfig.getProductFlavors();
+        List<? extends CoreProductFlavor> productFlavorList = testedConfig.getProductFlavors();
 
         // handle test variant
         // need a suppress warning because ProductFlavor.getTestSourceSet(type) is annotated
@@ -653,8 +662,8 @@
                 type,
                 signingOverride);
 
-        for (com.android.build.gradle.api.GroupableProductFlavor productFlavor : productFlavorList) {
-            ProductFlavorData<GroupableProductFlavor> data = productFlavors
+        for (CoreProductFlavor productFlavor : productFlavorList) {
+            ProductFlavorData<CoreProductFlavor> data = productFlavors
                     .get(productFlavor.getName());
 
             String dimensionName = productFlavor.getDimension();
@@ -672,7 +681,7 @@
         createCompoundSourceSets(
                 productFlavorList,
                 testVariantConfig,
-                extension.getSourceSetsContainer());
+                extension.getSourceSets());
 
         // create the internal storage for this variant.
         TestVariantData testVariantData = new TestVariantData(
@@ -691,11 +700,11 @@
      * @param productFlavorList the flavor(s) to build.
      */
     private void createVariantDataForProductFlavors(
-            @NonNull List<com.android.build.gradle.api.GroupableProductFlavor> productFlavorList) {
+            @NonNull List<ProductFlavor> productFlavorList) {
 
         BuildTypeData testBuildTypeData = null;
-        if (extension instanceof TestedExtension) {
-            TestedExtension testedExtension = (TestedExtension) extension;
+        if (extension instanceof TestedAndroidConfig) {
+            TestedAndroidConfig testedExtension = (TestedAndroidConfig) extension;
 
             testBuildTypeData = buildTypes.get(testedExtension.getTestBuildType());
             if (testBuildTypeData == null) {
@@ -706,15 +715,16 @@
 
         BaseVariantData variantForAndroidTest = null;
 
-        ProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
+        CoreProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
 
-        Closure<Void> variantFilterClosure = extension.getVariantFilter();
+        Action<com.android.build.gradle.api.VariantFilter> variantFilterAction =
+                extension.getVariantFilter();
 
         for (BuildTypeData buildTypeData : buildTypes.values()) {
             boolean ignore = false;
-            if (variantFilterClosure != null) {
+            if (variantFilterAction != null) {
                 variantFilter.reset(defaultConfig, buildTypeData.getBuildType(), productFlavorList);
-                variantFilterClosure.call(variantFilter);
+                variantFilterAction.execute(variantFilter);
                 ignore = variantFilter.isIgnore();
             }
 
@@ -724,6 +734,30 @@
                         productFlavorList);
                 variantDataList.add(variantData);
 
+                GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
+                ThreadRecorder.get().record(
+                        ExecutionType.VARIANT_CONFIG,
+                        Recorder.EmptyBlock,
+                        new Recorder.Property(
+                                "project",
+                                project.getName()),
+                        new Recorder.Property(
+                                "variant",
+                                variantData.getName()),
+                        new Recorder.Property(
+                                "use_jack",
+                                Boolean.toString(variantConfig.getUseJack())),
+                        new Recorder.Property(
+                                "use_minify",
+                                Boolean.toString(variantConfig.isMinifyEnabled())),
+                        new Recorder.Property(
+                                "use_multi_dex",
+                                Boolean.toString(variantConfig.isMultiDexEnabled())),
+                        new Recorder.Property(
+                                "multi_dex_legacy",
+                                Boolean.toString(variantConfig.isLegacyMultiDexMode())));
+
+
                 if (variantFactory.hasTestScope()) {
                     TestVariantData unitTestVariantData = createTestVariantData(
                             variantData,
@@ -731,8 +765,6 @@
                     variantDataList.add(unitTestVariantData);
 
                     if (buildTypeData == testBuildTypeData) {
-                        GradleVariantConfiguration variantConfig = variantData
-                                .getVariantConfiguration();
                         if (variantConfig.isMinifyEnabled() && variantConfig.getUseJack()) {
                             throw new RuntimeException(
                                     "Cannot test obfuscated variants when compiling with jack.");
@@ -751,59 +783,6 @@
         }
     }
 
-    public void createApiObjects() {
-        for (BaseVariantData<?> variantData : variantDataList) {
-            if (variantData.getType().isForTesting()) {
-                // Testing variants are handled together with their "owners".
-                continue;
-            }
-
-            BaseVariant variantApi =
-                    variantFactory.createVariantApi(variantData, readOnlyObjectProvider);
-
-            if (variantFactory.hasTestScope()) {
-                TestVariantData androidTestVariantData =
-                        ((TestedVariantData) variantData).getTestVariantData(ANDROID_TEST);
-
-                if (androidTestVariantData != null) {
-                    TestVariantImpl androidTestVariant = instantiator.newInstance(
-                            TestVariantImpl.class,
-                            androidTestVariantData,
-                            variantApi,
-                            androidBuilder,
-                            readOnlyObjectProvider);
-
-                    // add the test output.
-                    ApplicationVariantFactory.createApkOutputApiObjects(
-                            instantiator,
-                            androidTestVariantData,
-                            androidTestVariant);
-
-                    ((TestedExtension) extension).addTestVariant(androidTestVariant);
-                    ((TestedVariant) variantApi).setTestVariant(androidTestVariant);
-                }
-
-                TestVariantData unitTestVariantData =
-                        ((TestedVariantData) variantData).getTestVariantData(UNIT_TEST);
-                if (unitTestVariantData != null) {
-                    UnitTestVariantImpl unitTestVariant = instantiator.newInstance(
-                            UnitTestVariantImpl.class,
-                            unitTestVariantData,
-                            variantApi,
-                            androidBuilder,
-                            readOnlyObjectProvider);
-
-                    ((TestedExtension) extension).addUnitTestVariant(unitTestVariant);
-                    ((TestedVariant) variantApi).setUnitTestVariant(unitTestVariant);
-                }
-            }
-
-            // Only add the variant API object to the domain object set once it's been fully
-            // initialized.
-            extension.addVariant(variantApi);
-        }
-    }
-
     private boolean isVariantPublished() {
         return extension.getPublishNonDefault();
     }
@@ -826,21 +805,19 @@
     }
 
     private SigningConfig createSigningOverride() {
-        if (project.hasProperty(PROPERTY_SIGNING_STORE_FILE) &&
-                project.hasProperty(PROPERTY_SIGNING_STORE_PASSWORD) &&
-                project.hasProperty(PROPERTY_SIGNING_KEY_ALIAS) &&
-                project.hasProperty(PROPERTY_SIGNING_KEY_PASSWORD)) {
+        AndroidGradleOptions.SigningOptions signingOptions =
+                AndroidGradleOptions.getSigningOptions(project);
+        if (signingOptions != null) {
+            com.android.build.gradle.internal.dsl.SigningConfig signingConfigDsl =
+                    new com.android.build.gradle.internal.dsl.SigningConfig("externalOverride");
 
-            SigningConfig signingConfigDsl = new SigningConfig("externalOverride");
-            Map<String, ?> props = project.getProperties();
+            signingConfigDsl.setStoreFile(new File(signingOptions.storeFile));
+            signingConfigDsl.setStorePassword(signingOptions.keyPassword);
+            signingConfigDsl.setKeyAlias(signingOptions.keyAlias);
+            signingConfigDsl.setKeyPassword(signingOptions.keyPassword);
 
-            signingConfigDsl.setStoreFile(new File((String) props.get(PROPERTY_SIGNING_STORE_FILE)));
-            signingConfigDsl.setStorePassword((String) props.get(PROPERTY_SIGNING_STORE_PASSWORD));
-            signingConfigDsl.setKeyAlias((String) props.get(PROPERTY_SIGNING_KEY_ALIAS));
-            signingConfigDsl.setKeyPassword((String) props.get(PROPERTY_SIGNING_KEY_PASSWORD));
-
-            if (project.hasProperty(PROPERTY_SIGNING_STORE_TYPE)) {
-                signingConfigDsl.setStoreType((String) props.get(PROPERTY_SIGNING_STORE_TYPE));
+            if (signingOptions.storeType != null) {
+                signingConfigDsl.setStoreType(signingOptions.storeType);
             }
 
             return signingConfigDsl;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java
index acfa596..b4e6ffd 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java
@@ -17,8 +17,7 @@
 package com.android.build.gradle.internal;
 
 import com.android.annotations.NonNull;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
 import com.android.builder.model.SigningConfig;
 
 import java.util.Map;
@@ -29,13 +28,13 @@
 public interface VariantModel {
 
     @NonNull
-    ProductFlavorData<ProductFlavor> getDefaultConfig();
+    ProductFlavorData<CoreProductFlavor> getDefaultConfig();
 
     @NonNull
     Map<String, BuildTypeData> getBuildTypes();
 
     @NonNull
-    Map<String, ProductFlavorData<GroupableProductFlavor>> getProductFlavors();
+    Map<String, ProductFlavorData<CoreProductFlavor>> getProductFlavors();
 
     @NonNull
     Map<String, ? extends SigningConfig> getSigningConfigs();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
index 9c2e4aa..3355eb0 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
@@ -20,7 +20,6 @@
 import com.android.annotations.Nullable;
 import com.android.build.gradle.api.BaseVariant;
 import com.android.build.gradle.api.BaseVariantOutput;
-import com.android.build.gradle.api.GroupableProductFlavor;
 import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.build.gradle.tasks.AidlCompile;
 import com.android.build.gradle.tasks.GenerateBuildConfig;
@@ -35,7 +34,7 @@
 import com.google.common.collect.Lists;
 
 import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
 import org.gradle.api.tasks.compile.AbstractCompile;
 import org.gradle.api.tasks.compile.JavaCompile;
 
@@ -118,7 +117,7 @@
 
     @Override
     @NonNull
-    public List<GroupableProductFlavor> getProductFlavors() {
+    public List<ProductFlavor> getProductFlavors() {
         return new ImmutableFlavorList(
                 getVariantData().getVariantConfiguration().getProductFlavors(),
                 readOnlyObjectProvider);
@@ -184,15 +183,13 @@
     @Override
     @Nullable
     public JavaCompile getJavaCompile() {
-        return getVariantData().javaCompileTask instanceof JavaCompile
-                ? (JavaCompile) getVariantData().javaCompileTask
-                : null;
+        return getVariantData().javacTask;
     }
 
     @NonNull
     @Override
     public AbstractCompile getJavaCompiler() {
-        return getVariantData().javaCompileTask;
+        return getVariantData().javaCompilerTask;
     }
 
     @NonNull
@@ -215,7 +212,7 @@
 
     @Override
     @NonNull
-    public Copy getProcessJavaResources() {
+    public Sync getProcessJavaResources() {
         return getVariantData().processJavaResourcesTask;
     }
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java
index 5f43a96..38bdcc3 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java
@@ -17,7 +17,7 @@
 package com.android.build.gradle.internal.api;
 
 import com.android.annotations.NonNull;
-import com.android.build.gradle.api.GroupableProductFlavor;
+import com.android.builder.model.ProductFlavor;
 
 import java.lang.reflect.Array;
 import java.util.Collection;
@@ -26,19 +26,19 @@
 import java.util.ListIterator;
 
 /**
- * Implementation of List that create read-only GroupableProductFlavor on the fly as they are
+ * Implementation of List that create read-only ProductFlavor on the fly as they are
  * queried. The list itself is immutable.
  */
-public class ImmutableFlavorList implements List<GroupableProductFlavor> {
+public class ImmutableFlavorList implements List<ProductFlavor> {
 
     @NonNull
-    private final List<? extends GroupableProductFlavor> list;
+    private final List<? extends ProductFlavor> list;
 
     @NonNull
     private final ReadOnlyObjectProvider immutableObjectProvider;
 
     ImmutableFlavorList(
-            @NonNull List<? extends GroupableProductFlavor> list,
+            @NonNull List<? extends ProductFlavor> list,
             @NonNull ReadOnlyObjectProvider immutableObjectProvider) {
         this.list = list;
         this.immutableObjectProvider = immutableObjectProvider;
@@ -61,16 +61,16 @@
 
     @NonNull
     @Override
-    public Iterator<GroupableProductFlavor> iterator() {
-        final Iterator<? extends GroupableProductFlavor> baseIterator = list.iterator();
-        return new Iterator<GroupableProductFlavor>() {
+    public Iterator<ProductFlavor> iterator() {
+        final Iterator<? extends ProductFlavor> baseIterator = list.iterator();
+        return new Iterator<ProductFlavor>() {
             @Override
             public boolean hasNext() {
                 return baseIterator.hasNext();
             }
 
             @Override
-            public GroupableProductFlavor next() {
+            public ProductFlavor next() {
                 return immutableObjectProvider.getProductFlavor(baseIterator.next());
             }
 
@@ -116,7 +116,7 @@
     }
 
     @Override
-    public boolean add(GroupableProductFlavor e) {
+    public boolean add(ProductFlavor e) {
         throw new UnsupportedOperationException();
     }
 
@@ -131,12 +131,12 @@
     }
 
     @Override
-    public boolean addAll(@NonNull Collection<? extends GroupableProductFlavor> es) {
+    public boolean addAll(@NonNull Collection<? extends ProductFlavor> es) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public boolean addAll(int i, @NonNull Collection<? extends GroupableProductFlavor> es) {
+    public boolean addAll(int i, @NonNull Collection<? extends ProductFlavor> es) {
         throw new UnsupportedOperationException();
     }
 
@@ -156,23 +156,23 @@
     }
 
     @Override
-    public GroupableProductFlavor get(int i) {
-        GroupableProductFlavor gpf = list.get(i);
+    public ProductFlavor get(int i) {
+        ProductFlavor gpf = list.get(i);
         return immutableObjectProvider.getProductFlavor(gpf);
     }
 
     @Override
-    public GroupableProductFlavor set(int i, GroupableProductFlavor e) {
+    public ProductFlavor set(int i, ProductFlavor e) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void add(int i, GroupableProductFlavor e) {
+    public void add(int i, ProductFlavor e) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public GroupableProductFlavor remove(int i) {
+    public ProductFlavor remove(int i) {
         throw new UnsupportedOperationException();
     }
 
@@ -194,16 +194,16 @@
 
     @NonNull
     @Override
-    public ListIterator<GroupableProductFlavor> listIterator() {
-        final ListIterator<? extends GroupableProductFlavor> baseIterator = list.listIterator();
-        return new ListIterator<GroupableProductFlavor>() {
+    public ListIterator<ProductFlavor> listIterator() {
+        final ListIterator<? extends ProductFlavor> baseIterator = list.listIterator();
+        return new ListIterator<ProductFlavor>() {
             @Override
             public boolean hasNext() {
                 return baseIterator.hasNext();
             }
 
             @Override
-            public GroupableProductFlavor next() {
+            public ProductFlavor next() {
                 return immutableObjectProvider.getProductFlavor(baseIterator.next());
             }
 
@@ -213,7 +213,7 @@
             }
 
             @Override
-            public GroupableProductFlavor previous() {
+            public ProductFlavor previous() {
                 return immutableObjectProvider.getProductFlavor(baseIterator.previous());
             }
 
@@ -233,12 +233,12 @@
             }
 
             @Override
-            public void set(GroupableProductFlavor productFlavor) {
+            public void set(ProductFlavor productFlavor) {
                 throw new UnsupportedOperationException();
             }
 
             @Override
-            public void add(GroupableProductFlavor productFlavor) {
+            public void add(ProductFlavor productFlavor) {
                 throw new UnsupportedOperationException();
             }
         };
@@ -246,16 +246,16 @@
 
     @NonNull
     @Override
-    public ListIterator<GroupableProductFlavor> listIterator(int i) {
-        final ListIterator<? extends GroupableProductFlavor> baseIterator = list.listIterator(i);
-        return new ListIterator<GroupableProductFlavor>() {
+    public ListIterator<ProductFlavor> listIterator(int i) {
+        final ListIterator<? extends ProductFlavor> baseIterator = list.listIterator(i);
+        return new ListIterator<ProductFlavor>() {
             @Override
             public boolean hasNext() {
                 return baseIterator.hasNext();
             }
 
             @Override
-            public GroupableProductFlavor next() {
+            public ProductFlavor next() {
                 return immutableObjectProvider.getProductFlavor(baseIterator.next());
             }
 
@@ -265,7 +265,7 @@
             }
 
             @Override
-            public GroupableProductFlavor previous() {
+            public ProductFlavor previous() {
                 return immutableObjectProvider.getProductFlavor(baseIterator.previous());
             }
 
@@ -285,12 +285,12 @@
             }
 
             @Override
-            public void set(GroupableProductFlavor productFlavor) {
+            public void set(ProductFlavor productFlavor) {
                 throw new UnsupportedOperationException();
             }
 
             @Override
-            public void add(GroupableProductFlavor productFlavor) {
+            public void add(ProductFlavor productFlavor) {
                 throw new UnsupportedOperationException();
             }
         };
@@ -298,7 +298,7 @@
 
     @NonNull
     @Override
-    public List<GroupableProductFlavor> subList(int i, int i2) {
+    public List<ProductFlavor> subList(int i, int i2) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java
index 8032cac..9399907 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java
@@ -21,6 +21,9 @@
 import com.android.builder.model.BuildType;
 import com.android.builder.model.SigningConfig;
 
+import java.io.File;
+import java.util.List;
+
 /**
  * Read-only version of the BuildType wrapping another BuildType.
  *
@@ -112,4 +115,10 @@
     public SigningConfig getSigningConfig() {
         return readOnlyObjectProvider.getSigningConfig(buildType.getSigningConfig());
     }
+
+    @NonNull
+    @Override
+    public List<File> getJarJarRuleFiles() {
+        return buildType.getJarJarRuleFiles();
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyGroupableProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyGroupableProductFlavor.java
deleted file mode 100644
index 1db306d..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyGroupableProductFlavor.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 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.GroupableProductFlavor;
-
-/**
- * Read-only version of the GroupableProductFlavor wrapping another GroupableProductFlavor.
- *
- * In the variant API, it is important that the objects returned by the variants
- * are read-only.
- *
- * However, even though the API is defined to use the base interfaces as return
- * type (which all contain only getters), the dynamics of Groovy makes it easy to
- * actually use the setters of the implementation classes.
- *
- * This wrapper ensures that the returned instance is actually just a strict implementation
- * of the base interface and is read-only.
- */
-// TODO: Remove once GroupableProductFlavor interface is removed.
-@Deprecated
-public class ReadOnlyGroupableProductFlavor extends ReadOnlyProductFlavor implements
-        GroupableProductFlavor {
-
-    ReadOnlyGroupableProductFlavor(
-            @NonNull GroupableProductFlavor productFlavor,
-            @NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
-        super(productFlavor, readOnlyObjectProvider);
-    }
-
-    @Deprecated
-    public String getFlavorDimension() {
-        return getDimension();
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java
index 88138ec..953d3b6 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java
@@ -18,7 +18,6 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
 import com.android.builder.model.BuildType;
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
@@ -27,7 +26,7 @@
 import java.util.Map;
 
 /**
- * Provides read-only versions of BuildType, (Groupable)ProductFlavor and SigningConfig instances
+ * Provides read-only versions of BuildType, ProductFlavor and SigningConfig instances
  * so that they can safely be exposed through the variant API.
  *
  * The class creates them on the fly so that they are only created when a
@@ -47,7 +46,7 @@
      * Map of read-only ProductFlavor. This maps the normal flavor to the read-only version.
      */
     @NonNull
-    private final Map<GroupableProductFlavor, GroupableProductFlavor> readOnlyFlavors = Maps.newIdentityHashMap();
+    private final Map<ProductFlavor, ProductFlavor> readOnlyFlavors = Maps.newIdentityHashMap();
 
     /**
      * Map of read-only SigningConfig. This maps the normal config to the read-only version.
@@ -94,11 +93,11 @@
      * @return an read-only version.
      */
     @NonNull
-    public GroupableProductFlavor getProductFlavor(@NonNull GroupableProductFlavor productFlavor) {
-        GroupableProductFlavor readOnlyProductFlavor = readOnlyFlavors.get(productFlavor);
+    public ProductFlavor getProductFlavor(@NonNull ProductFlavor productFlavor) {
+        ProductFlavor readOnlyProductFlavor = readOnlyFlavors.get(productFlavor);
         if (readOnlyProductFlavor == null) {
             readOnlyFlavors.put(productFlavor,
-                    readOnlyProductFlavor = new ReadOnlyGroupableProductFlavor(
+                    readOnlyProductFlavor = new ReadOnlyProductFlavor(
                             productFlavor, this));
         }
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java
index 5dcf746..2c0a489 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java
@@ -22,7 +22,10 @@
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
 
+import java.io.File;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 /**
  * read-only version of the ProductFlavor wrapping another ProductFlavor.
@@ -119,6 +122,12 @@
         return productFlavor.getTestInstrumentationRunner();
     }
 
+    @NonNull
+    @Override
+    public Map<String, String> getTestInstrumentationRunnerArguments() {
+        return productFlavor.getTestInstrumentationRunnerArguments();
+    }
+
     @Nullable
     @Override
     public Boolean getTestHandleProfiling() {
@@ -148,4 +157,16 @@
     public String getDimension() {
         return productFlavor.getDimension();
     }
+
+    @NonNull
+    @Override
+    public List<File> getJarJarRuleFiles() {
+        return productFlavor.getJarJarRuleFiles();
+    }
+
+    @Nullable
+    @Deprecated
+    public String getFlavorDimension() {
+        return productFlavor.getDimension();
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java
index 6c60ced..c3b9e3f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java
@@ -17,7 +17,7 @@
 package com.android.build.gradle.internal.api;
 
 import com.android.annotations.Nullable;
-import com.android.build.gradle.TestedExtension;
+import com.android.build.gradle.TestedAndroidConfig;
 import com.android.build.gradle.api.TestVariant;
 import com.android.build.gradle.api.UnitTestVariant;
 
@@ -32,7 +32,7 @@
      * Returns the build variant that will test this build variant.
      *
      * <p>The android test variant exists only for one build type, by default "debug". This is
-     * controlled by {@link TestedExtension#testBuildType}.
+     * controlled by {@link TestedAndroidConfig#getTestBuildType}.
      */
     @Nullable
     TestVariant getTestVariant();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java
index a1e16de..015b0be 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java
@@ -18,7 +18,6 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
 import com.android.builder.model.BuildType;
 import com.android.builder.model.ProductFlavor;
 
@@ -26,8 +25,8 @@
 import java.util.List;
 
 /**
- * An object representing a Variant (made of a build type and one or more flavors), and
- * a boolean controlling whether this variant is to be excluded.
+ * Exposes a read-only view of a variant as well as a flag that can be used to signal that the
+ * variant should be ignored.
  */
 public class VariantFilter implements com.android.build.gradle.api.VariantFilter {
 
@@ -38,7 +37,7 @@
 
     private ProductFlavor defaultConfig;
     private BuildType buildType;
-    private List<GroupableProductFlavor> flavors;
+    private List<ProductFlavor> flavors;
 
     public VariantFilter(@NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
         this.readOnlyObjectProvider = readOnlyObjectProvider;
@@ -47,7 +46,7 @@
     public void reset(
             @NonNull ProductFlavor defaultConfig,
             @NonNull BuildType buildType,
-            @Nullable List<GroupableProductFlavor> flavors) {
+            @Nullable List<ProductFlavor> flavors) {
         ignore = false;
         this.defaultConfig = defaultConfig;
         this.buildType = buildType;
@@ -67,8 +66,10 @@
     }
 
     /**
-     * The default config. This is a Read-Only version of the global
-     * <code>android.defaultConfig</code> object.
+     * Returns a read-only ProductFlavor that represents the default config.
+     *
+     * <p>See {@link com.android.build.gradle.internal.dsl.ProductFlavor} for properties present
+     * on the returned object.
      */
     @Override
     @NonNull
@@ -77,8 +78,10 @@
     }
 
     /**
-     * The Build Type used by the variant. This is Read-Only version, as changing this
-     * object would have global impact.
+     * Returns a read-only Build Type.
+     *
+     * <p>See {@link com.android.build.gradle.internal.dsl.BuildType} for properties present
+     * on the returned object.
      */
     @Override
     @NonNull
@@ -87,14 +90,16 @@
     }
 
     /**
-     * The list of Product Flavors for this variant. These are returned as Read-Only versions, as
-     * changing these objects would have global impact.
+     * Returns the list of read-only flavors, or an empty list.
+     *
+     * <p>See {@link com.android.build.gradle.internal.dsl.ProductFlavor} for properties
+     * present on the returned objects.
      */
     @NonNull
     @Override
-    public List<GroupableProductFlavor> getFlavors() {
+    public List<ProductFlavor> getFlavors() {
         return flavors != null ?
                 new ImmutableFlavorList(flavors, readOnlyObjectProvider) :
-                Collections.<GroupableProductFlavor>emptyList();
+                Collections.<ProductFlavor>emptyList();
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Abi.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Abi.java
new file mode 100644
index 0000000..01ca329
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Abi.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 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.core;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+/**
+ * Enum of valid ABI you can specify for NDK.
+ */
+public enum Abi {
+    //          name                           architecture                        gccToolchainPrefix        gccExecutablePrefix       supports64Bits
+    ARMEABI    (SdkConstants.ABI_ARMEABI,      SdkConstants.CPU_ARCH_ARM,          "arm-linux-androideabi",  "arm-linux-androideabi",  false),
+    ARMEABI_V7A(SdkConstants.ABI_ARMEABI_V7A,  SdkConstants.CPU_ARCH_ARM,          "arm-linux-androideabi",  "arm-linux-androideabi",  false),
+    ARM64_V8A  (SdkConstants.ABI_ARM64_V8A,    SdkConstants.CPU_ARCH_ARM64,        "aarch64-linux-android",  "aarch64-linux-android",  true),
+    X86        (SdkConstants.ABI_INTEL_ATOM,   SdkConstants.CPU_ARCH_INTEL_ATOM,   "x86",                    "i686-linux-android",     false),
+    X86_64     (SdkConstants.ABI_INTEL_ATOM64, SdkConstants.CPU_ARCH_INTEL_ATOM64, "x86_64",                 "x86_64-linux-android",   true),
+    MIPS       (SdkConstants.ABI_MIPS,         SdkConstants.CPU_ARCH_MIPS,         "mipsel-linux-android",   "mipsel-linux-android",   false),
+    MIPS64     (SdkConstants.ABI_MIPS64,       SdkConstants.CPU_ARCH_MIPS64,       "mips64el-linux-android", "mips64el-linux-android", true);
+
+
+    @NonNull
+    private String name;
+    @NonNull
+    String architecture;
+    @NonNull
+    private String gccToolchainPrefix;
+    @NonNull
+    private String gccExecutablePrefix;
+    private boolean supports64Bits;
+
+    Abi(@NonNull String name, @NonNull String architecture,
+            @NonNull String gccToolchainPrefix, @NonNull String gccExecutablePrefix, boolean supports64Bits) {
+        this.name = name;
+        this.architecture = architecture;
+        this.gccToolchainPrefix = gccToolchainPrefix;
+        this.gccExecutablePrefix = gccExecutablePrefix;
+        this.supports64Bits = supports64Bits;
+    }
+
+    /**
+     * Returns the ABI Enum with the specified name.
+     */
+    @Nullable
+    public static Abi getByName(@NonNull String name) {
+        for (Abi abi : values()) {
+            if (abi.name.equals(name)) {
+                return abi;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns name of the ABI.
+     */
+    @NonNull
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the CPU architecture.
+     */
+    @NonNull
+    public String getArchitecture() {
+        return architecture;
+    }
+
+    /**
+     * Returns the platform string for locating the toolchains in the NDK.
+     */
+    @NonNull
+    public String getGccToolchainPrefix() {
+        return gccToolchainPrefix;
+    }
+
+    /**
+     * Returns the prefix of GCC tools for the ABI.
+     */
+    @NonNull
+    public String getGccExecutablePrefix() {
+        return gccExecutablePrefix;
+    }
+
+    /**
+     * Returns whether the ABI supports 64-bits.
+     */
+    public boolean supports64Bits() {
+        return supports64Bits;
+    }
+}
+
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java
index 8b7187f..0aa6fe5 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java
@@ -20,14 +20,13 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
 import com.android.builder.core.VariantConfiguration;
 import com.android.builder.core.VariantType;
 import com.android.builder.model.SigningConfig;
 import com.android.builder.model.SourceProvider;
-import com.google.common.base.Preconditions;
 
 import java.util.List;
 import java.util.Set;
@@ -38,7 +37,7 @@
  *
  * It also adds support for Ndk support that is not ready to go in the builder library.
  */
-public class GradleVariantConfiguration extends VariantConfiguration<BuildType, ProductFlavor, GroupableProductFlavor> {
+public class GradleVariantConfiguration extends VariantConfiguration<CoreBuildType, CoreProductFlavor, CoreProductFlavor> {
 
     private final MergedNdkConfig mMergedNdkConfig = new MergedNdkConfig();
 
@@ -46,9 +45,9 @@
      * Creates a {@link GradleVariantConfiguration} for a normal (non-test) variant.
      */
     public GradleVariantConfiguration(
-            @NonNull ProductFlavor defaultConfig,
+            @NonNull CoreProductFlavor defaultConfig,
             @NonNull SourceProvider defaultSourceProvider,
-            @NonNull BuildType buildType,
+            @NonNull CoreBuildType buildType,
             @Nullable SourceProvider buildTypeSourceProvider,
             @NonNull VariantType type,
             @Nullable SigningConfig signingConfigOverride) {
@@ -62,9 +61,9 @@
      */
     public GradleVariantConfiguration(
             @Nullable VariantConfiguration testedConfig,
-            @NonNull ProductFlavor defaultConfig,
+            @NonNull CoreProductFlavor defaultConfig,
             @NonNull SourceProvider defaultSourceProvider,
-            @NonNull BuildType buildType,
+            @NonNull CoreBuildType buildType,
             @Nullable SourceProvider buildTypeSourceProvider,
             @NonNull VariantType type,
             @Nullable SigningConfig signingConfigOverride) {
@@ -76,7 +75,7 @@
     @NonNull
     @Override
     public VariantConfiguration addProductFlavor(
-            @NonNull GroupableProductFlavor productFlavor,
+            @NonNull CoreProductFlavor productFlavor,
             @NonNull SourceProvider sourceProvider,
             @NonNull String dimensionName) {
         checkNotNull(productFlavor);
@@ -88,7 +87,7 @@
     }
 
     @NonNull
-    public NdkConfig getNdkConfig() {
+    public CoreNdkOptions getNdkConfig() {
         return mMergedNdkConfig;
     }
 
@@ -121,7 +120,7 @@
         }
 
         // cant use merge flavor as useJack is not a prop on the base class.
-        for (ProductFlavor productFlavor : getProductFlavors()) {
+        for (CoreProductFlavor productFlavor : getProductFlavors()) {
             value = productFlavor.getUseJack();
             if (value != null) {
                 return value;
@@ -143,9 +142,9 @@
             mMergedNdkConfig.append(getDefaultConfig().getNdkConfig());
         }
 
-        final List<GroupableProductFlavor> flavors = getProductFlavors();
+        final List<CoreProductFlavor> flavors = getProductFlavors();
         for (int i = flavors.size() - 1 ; i >= 0 ; i--) {
-            NdkConfig ndkConfig = flavors.get(i).getNdkConfig();
+            CoreNdkOptions ndkConfig = flavors.get(i).getNdkConfig();
             if (ndkConfig != null) {
                 mMergedNdkConfig.append(ndkConfig);
             }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java
index c19e63e..7ced015 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java
@@ -18,6 +18,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
@@ -27,7 +28,7 @@
 /**
  * Implementation of NdkConfig used to merge multiple configs together.
  */
-public class MergedNdkConfig implements NdkConfig {
+public class MergedNdkConfig implements CoreNdkOptions {
 
     private String moduleName;
     private String cFlags;
@@ -79,7 +80,7 @@
         return jobs;
     }
 
-    public void append(@NonNull NdkConfig ndkConfig) {
+    public void append(@NonNull CoreNdkOptions ndkConfig) {
         // override
         if (ndkConfig.getModuleName() != null) {
             moduleName = ndkConfig.getModuleName();
@@ -97,23 +98,19 @@
         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) {
+        } else if (ndkConfig.getcFlags() != null && !ndkConfig.getcFlags().isEmpty()) {
             cFlags = cFlags + " " + ndkConfig.getcFlags();
         }
 
         if (ndkConfig.getLdLibs() != null) {
             if (ldLibs == null) {
                 ldLibs = Lists.newArrayListWithCapacity(ndkConfig.getLdLibs().size());
-            } else {
-                ldLibs.clear();
             }
             ldLibs.addAll(ndkConfig.getLdLibs());
         }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/NdkConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/NdkConfig.java
deleted file mode 100644
index 06e767c..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/NdkConfig.java
+++ /dev/null
@@ -1,64 +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.core;
-
-import com.android.annotations.Nullable;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Base class for NDK config file.
- */
-public interface NdkConfig {
-
-    /**
-     * The module name
-     */
-    @Nullable
-    String getModuleName();
-
-    /**
-     * The C Flags
-     */
-    @Nullable
-    String getcFlags();
-
-    /**
-     * The LD Libs
-     */
-    @Nullable
-    Collection<String> getLdLibs();
-
-    /**
-     * The ABI Filters
-     */
-    @Nullable
-    Set<String> getAbiFilters();
-
-    /**
-     * The APP_STL value
-     */
-    @Nullable
-    String getStl();
-
-    /**
-     * Number of parallel threads to spawn.
-     */
-    @Nullable
-    Integer getJobs();
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Toolchain.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Toolchain.java
new file mode 100644
index 0000000..6c81e9e
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Toolchain.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.core;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+/**
+ * Enum of valid toolchains you can specify for NDK.
+ */
+public enum Toolchain {
+    GCC("gcc"),
+    CLANG("clang");
+
+    @NonNull
+    private final String name;
+
+    @NonNull
+    public static Toolchain getDefault() {
+        return GCC;
+    }
+
+    @Nullable
+    public static Toolchain getByName(@NonNull String toolchainName) {
+        for (Toolchain toolchain : values()) {
+            if (toolchain.name.equals(toolchainName)) {
+                return toolchain;
+            }
+        }
+        return null;
+    }
+
+    Toolchain(@NonNull String name) {
+        this.name = name;
+    }
+
+    @NonNull
+    public String getName() {
+        return name;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy
index cc37469..8a36055 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy
@@ -16,10 +16,10 @@
 
 package com.android.build.gradle.internal.coverage
 
-import com.android.build.gradle.internal.TaskManager
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
+import com.android.build.gradle.internal.PostCompilationData
 import org.gradle.api.DefaultTask
 import org.gradle.api.file.FileCollection
 import org.gradle.api.tasks.InputDirectory
@@ -64,9 +64,9 @@
 
         VariantScope scope;
 
-        TaskManager.PostCompilationData pcData;
+        PostCompilationData pcData;
 
-        ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+        ConfigAction(VariantScope scope, PostCompilationData pcData) {
             this.scope = scope
             this.pcData = pcData
         }
@@ -88,7 +88,7 @@
                 scope.globalScope.project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
             }
             // can't directly use the existing inputFiles closure as we need the dir instead :\
-            ConventionMappingHelper.map(jacocoTask, "inputDir", pcData.inputDir)
+            ConventionMappingHelper.map(jacocoTask, "inputDir", pcData.inputDirCallable)
             ConventionMappingHelper.map(jacocoTask, "outputDir") {
                 new File("${scope.globalScope.buildDir}/${FD_INTERMEDIATES}/coverage-instrumented-classes/${scope.variantConfiguration.dirName}")
             }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java
index 2911e04..6244654 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java
@@ -18,9 +18,9 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.annotations.concurrency.Immutable;
 import com.android.builder.dependency.LibraryDependency;
 import com.android.builder.model.MavenCoordinates;
+import com.google.common.collect.ImmutableList;
 
 import java.io.File;
 import java.util.Collection;
@@ -28,13 +28,19 @@
 
 /**
  * Version of LibraryDependency that includes transitive jar dependencies as JarInfo.
+ *
+ * This is used as a temporary object as the dependencies are processed. This object contains
+ * more information and can be mutated ({@link #setIsOptional(boolean)}) during the process.
+ *
+ * In the end the object will be converted into an immutable LibraryDependency instance.
  */
-@Immutable
 public class LibInfo extends LibraryDependencyImpl {
 
     @NonNull
     private final Collection<JarInfo> jarDependencies;
 
+    private boolean mutableIsOptional = false;
+
     public LibInfo(@NonNull File bundle,
             @NonNull File explodedBundle,
             @NonNull List<LibraryDependency> dependencies,
@@ -51,13 +57,41 @@
                 variantName,
                 projectPath,
                 requestedCoordinates,
-                resolvedCoordinates);
+                resolvedCoordinates,
+                false);
         this.jarDependencies = jarDependencies;
     }
 
+    /**
+     * Mark the dependency as optional.
+     */
+    public void setIsOptional(boolean isOptional) {
+        this.mutableIsOptional = isOptional;
+        for (LibInfo libInfo : getLibInfoDependencies()) {
+            libInfo.setIsOptional(isOptional);
+        }
+    }
+
+    @Override
+    public boolean isOptional() {
+        return mutableIsOptional;
+    }
+
+    @NonNull
+    public List<LibInfo> getLibInfoDependencies() {
+        ImmutableList.Builder<LibInfo> libInfoBuilder = ImmutableList.builder();
+        for (LibraryDependency libraryDependency : getDependencies()) {
+            if (libraryDependency instanceof LibInfo) {
+                libInfoBuilder.add((LibInfo) libraryDependency);
+            } else {
+                throw new RuntimeException("Mixed LibInfo and LibraryDependencies instances !");
+            }
+        }
+        return libInfoBuilder.build();
+    }
+
     @NonNull
     public Collection<JarInfo> getJarDependencies() {
         return jarDependencies;
     }
-
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java
index 77cf021..4f46a86 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java
@@ -44,6 +44,8 @@
     @Nullable
     private final MavenCoordinates resolvedCoordinates;
 
+    private final boolean isOptional;
+
     public LibraryDependencyImpl(
             @NonNull File bundle,
             @NonNull File explodedBundle,
@@ -52,12 +54,14 @@
             @Nullable String variantName,
             @Nullable String projectPath,
             @Nullable MavenCoordinates requestedCoordinates,
-            @Nullable MavenCoordinates resolvedCoordinates) {
+            @Nullable MavenCoordinates resolvedCoordinates,
+            boolean isOptional) {
         super(bundle, explodedBundle, name, projectPath);
         this.dependencies = dependencies;
         this.variantName = variantName;
         this.requestedCoordinates = requestedCoordinates;
         this.resolvedCoordinates = resolvedCoordinates;
+        this.isOptional = isOptional;
     }
 
     @NonNull
@@ -96,6 +100,11 @@
         return resolvedCoordinates;
     }
 
+    @Override
+    public boolean isOptional() {
+        return isOptional;
+    }
+
     /**
      * Returns a version of the library dependency with the dependencies removed.
      */
@@ -109,7 +118,8 @@
                 variantName,
                 getProject(),
                 requestedCoordinates,
-                resolvedCoordinates);
+                resolvedCoordinates,
+                isOptional);
     }
 
     @Override
@@ -126,12 +136,18 @@
         LibraryDependencyImpl that = (LibraryDependencyImpl) o;
         return Objects.equal(dependencies, that.dependencies) &&
                 Objects.equal(variantName, that.variantName) &&
-                Objects.equal(resolvedCoordinates, that.resolvedCoordinates);
+                Objects.equal(resolvedCoordinates, that.resolvedCoordinates) &&
+                Objects.equal(isOptional, that.isOptional());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(super.hashCode(), dependencies, variantName, resolvedCoordinates);
+        return Objects.hashCode(
+                super.hashCode(),
+                dependencies,
+                variantName,
+                resolvedCoordinates,
+                isOptional);
     }
 
     @Override
@@ -141,6 +157,7 @@
                 .add("variantName", variantName)
                 .add("requestedCoordinates", requestedCoordinates)
                 .add("resolvedCoordinates", resolvedCoordinates)
+                .add("isOptional", isOptional)
                 .add("super", super.toString())
                 .toString();
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java
index b28fce6..6554876 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java
@@ -17,6 +17,7 @@
 package com.android.build.gradle.internal.dependency;
 
 import com.android.annotations.NonNull;
+import com.android.builder.dependency.LibraryDependency;
 import com.android.builder.dependency.SymbolFileProvider;
 
 import org.gradle.api.tasks.InputFile;
@@ -30,10 +31,12 @@
 
     private final File manifest;
     private final File symbolFile;
+    private final boolean isOptional;
 
-    public SymbolFileProviderImpl(@NonNull File manifest, @NonNull File symbolFile) {
-        this.manifest = manifest;
-        this.symbolFile = symbolFile;
+    public SymbolFileProviderImpl(@NonNull LibraryDependency library) {
+        manifest = library.getManifest();
+        symbolFile = library.getSymbolFile();
+        isOptional = library.isOptional();
     }
 
     @InputFile
@@ -49,4 +52,9 @@
     public File getSymbolFile() {
         return symbolFile;
     }
+
+    @Override
+    public boolean isOptional() {
+        return isOptional;
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy
index 888fbf5..68a653a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy
@@ -36,7 +36,7 @@
  *
  * It optionally contains the dependencies for a test config for the given config.
  */
-public class VariantDependencies implements DependencyContainer, ConfigurationProvider {
+public class VariantDependencies implements DependencyContainer {
 
     final String name
 
@@ -69,11 +69,13 @@
 
     DependencyChecker checker
 
-    static VariantDependencies compute(@NonNull Project project,
-                                       @NonNull String name,
-                                                boolean publishVariant,
-                                                VariantType variantType,
-                                       @NonNull ConfigurationProvider... providers) {
+    static VariantDependencies compute(
+            @NonNull Project project,
+            @NonNull String name,
+            boolean publishVariant,
+            @NonNull VariantType variantType,
+            @Nullable VariantDependencies parentVariant,
+            @NonNull ConfigurationProvider... providers) {
         Set<Configuration> compileConfigs = Sets.newHashSetWithExpectedSize(providers.length * 2)
         Set<Configuration> apkConfigs = Sets.newHashSetWithExpectedSize(providers.length)
 
@@ -89,6 +91,11 @@
             }
         }
 
+        if (parentVariant != null) {
+            compileConfigs.add(parentVariant.getCompileConfiguration())
+            apkConfigs.add(parentVariant.getPackageConfiguration())
+        }
+
         Configuration compile = project.configurations.maybeCreate("_${name}Compile")
         compile.visible = false
         compile.description = "## Internal use, do not manually configure ##"
@@ -160,24 +167,16 @@
         return name
     }
 
-    @Override
     @NonNull
     Configuration getCompileConfiguration() {
         return compileConfiguration
     }
 
-    @Override
     @NonNull
     Configuration getPackageConfiguration() {
         return packageConfiguration
     }
 
-    @Override
-    @Nullable
-    Configuration getProvidedConfiguration() {
-        return null
-    }
-
     @Nullable
     Configuration getPublishConfiguration() {
         return publishConfiguration
@@ -233,6 +232,16 @@
         return localJars
     }
 
+    public boolean hasNonOptionalLibraries() {
+        for (LibraryDependency libraryDependency : libraries) {
+            if (!libraryDependency.isOptional()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     @Override
     public String toString() {
         return Objects.toStringHelper(this)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java
index ea4de1c..3c3bdc4 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java
@@ -73,6 +73,8 @@
 
     /**
      * Extensions of files that will not be stored compressed in the APK.
+     *
+     * <p>Equivalent of the -0 flag. See <code>aapt --help</code>
      */
     @Override
     @Optional
@@ -91,7 +93,6 @@
 
     /**
      * Enables or disables PNG crunching.
-     * @param value true to enable, false to disable.
      */
     public void setCruncherEnabled(boolean value) {
         cruncherEnabled = value;
@@ -104,10 +105,9 @@
     public boolean getCruncherEnabled() {
         return cruncherEnabled;
     }
+    
     /**
      * Whether to use the new cruncher.
-     *
-     * <p>TODO: Document.
      */
     @Input
     public boolean getUseNewCruncher() {
@@ -124,6 +124,8 @@
 
     /**
      * Forces aapt to return an error if it fails to find an entry for a configuration.
+     *
+     * <p>See <code>aapt --help</code>
      */
     @Override
     @Input
@@ -131,10 +133,22 @@
         return failOnMissingConfigEntry;
     }
 
+    // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
+
+    /**
+     * Sets extensions of files that will not be stored compressed in the APK.
+     *
+     * <p>Equivalent of the -0 flag. See <code>aapt --help</code>
+     */
     public void noCompress(String noCompress) {
         noCompressList = Collections.singletonList(noCompress);
     }
 
+    /**
+     * Sets extensions of files that will not be stored compressed in the APK.
+     *
+     * <p>Equivalent of the -0 flag. See <code>aapt --help</code>
+     */
     public void noCompress(String... noCompress) {
         noCompressList = Arrays.asList(noCompress);
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java
index 6dbd7bb..c0f6eb3 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java
@@ -16,34 +16,41 @@
 
 package com.android.build.gradle.internal.dsl;
 
-import static com.android.build.OutputFile.NO_FILTER;
-
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.build.OutputFile;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 import java.util.Set;
 
 /**
- * Data for per-ABI splits.
+ * DSL object for configuring per-abi splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
  */
 public class AbiSplitOptions extends SplitOptions {
 
-    private static final String[] ABI_LIST = new String[] {
-            "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64" };
-
     private boolean universalApk = false;
 
     @Override
     protected Set<String> getDefaultValues() {
-        return Sets.newHashSet(ABI_LIST);
+        Set<String> values = Sets.newHashSet();
+        for (Abi abi : NdkHandler.getAbiList()) {
+            values.add(abi.getName());
+        }
+        return values;
     }
 
     @Override
     protected ImmutableSet<String> getAllowedValues() {
-        return ImmutableSet.copyOf(ABI_LIST);
+        ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+        for (Abi abi : NdkHandler.getAbiList()) {
+            builder.add(abi.getName());
+        }
+        return builder.build();
     }
 
     /**
@@ -57,19 +64,6 @@
         this.universalApk = universalApk;
     }
 
-    @NonNull
-    @Override
-    public Set<String> getApplicableFilters() {
-        Set<String> list = super.getApplicableFilters();
-
-        // if universal, and splitting enabled, then add an entry with no filter.
-        if (isEnable() && universalApk) {
-            list.add(NO_FILTER);
-        }
-
-        return list;
-    }
-
     /**
      * Returns the list of actual abi filters, each value of the collection is guaranteed to be non
      * null and of the possible API value.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.groovy
deleted file mode 100644
index 80c7340..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.groovy
+++ /dev/null
@@ -1,231 +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.build.gradle.internal.dsl
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.annotations.VisibleForTesting
-import com.android.build.gradle.internal.core.NdkConfig
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultBuildType
-import com.android.builder.model.BaseConfig
-import com.android.builder.model.ClassField
-import org.gradle.api.Action
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-import org.gradle.internal.reflect.Instantiator
-/**
- * DSL object to configure build types.
- */
-public class BuildType extends DefaultBuildType implements Serializable {
-    private static final long serialVersionUID = 1L
-
-    @NonNull
-    private final Project project
-    @NonNull
-    private final Logger logger
-
-    private final NdkOptions ndkConfig
-
-    private Boolean useJack
-
-    public BuildType(@NonNull String name,
-                        @NonNull Project project,
-                        @NonNull Instantiator instantiator,
-                        @NonNull Logger logger) {
-        super(name)
-        this.project = project
-        this.logger = logger
-        ndkConfig = instantiator.newInstance(NdkOptions.class)
-    }
-
-    @VisibleForTesting
-    BuildType(@NonNull String name,
-                 @NonNull Project project,
-                 @NonNull Logger logger) {
-        super(name)
-        this.project = project
-        this.logger = logger
-        ndkConfig = null
-    }
-
-    @Nullable
-    public NdkConfig getNdkConfig() {
-        return ndkConfig;
-    }
-
-    public void init(SigningConfig debugSigningConfig) {
-        if (BuilderConstants.DEBUG.equals(getName())) {
-            setDebuggable(true)
-            setEmbedMicroApp(false)
-
-            assert debugSigningConfig != null
-            setSigningConfig(debugSigningConfig)
-        } else if (BuilderConstants.RELEASE.equals(getName())) {
-            // no config needed for now.
-        }
-    }
-
-    /** The signing configuration. */
-    @Override
-    SigningConfig getSigningConfig() {
-        return (SigningConfig) super.signingConfig
-    }
-
-    @Override
-    protected void _initWith(@NonNull BaseConfig that) {
-        super._initWith(that)
-        shrinkResources = that.shrinkResources
-        useJack = that.useJack
-    }
-
-    int hashCode() {
-        int result = super.hashCode()
-        result = 31 * result + (useJack != null ? useJack.hashCode() : 0)
-        result = 31 * result + (shrinkResources ? 1 : 0)
-        return result
-    }
-
-    @Override
-    boolean equals(o) {
-        if (this.is(o)) return true
-        if (getClass() != o.class) return false
-        if (!super.equals(o)) return false
-        if (useJack != o.useJack) return false
-        if (shrinkResources != o.shrinkResources) return false
-
-        return true
-    }
-
-    // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
-
-    public void buildConfigField(
-            @NonNull String type,
-            @NonNull String name,
-            @NonNull String value) {
-        ClassField alreadyPresent = getBuildConfigFields().get(name);
-        if (alreadyPresent != null) {
-            logger.info(
-                    "BuildType(${getName()}): buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
-        }
-        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
-    }
-
-    public void resValue(
-            @NonNull String type,
-            @NonNull String name,
-            @NonNull String value) {
-        ClassField alreadyPresent = getResValues().get(name);
-        if (alreadyPresent != null) {
-            logger.info(
-                    "BuildType(${getName()}): resValue '$name' value is being replaced: ${alreadyPresent.value} -> $value");
-        }
-        addResValue(AndroidBuilder.createClassField(type, name, value));
-    }
-
-    /**
-     * Adds a new ProGuard configuration file.
-     *
-     * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
-     *
-     * <p>There are 2 default rules files
-     * <ul>
-     *     <li>proguard-android.txt
-     *     <li>proguard-android-optimize.txt
-     * </ul>
-     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
-     * full path to the files. They are identical except for enabling optimizations.
-     */
-    @NonNull
-    public BuildType proguardFile(Object proguardFile) {
-        proguardFiles.add(project.file(proguardFile));
-        return this;
-    }
-
-    /**
-     * Adds new ProGuard configuration files.
-     */
-    @NonNull
-    public BuildType proguardFiles(Object... proguardFileArray) {
-        proguardFiles.addAll(project.files(proguardFileArray).files);
-        return this;
-    }
-
-    /**
-     * Sets the ProGuard configuration files.
-     */
-    @NonNull
-    public BuildType setProguardFiles(Iterable<?> proguardFileIterable) {
-        proguardFiles.clear();
-        for (Object proguardFile : proguardFileIterable) {
-            proguardFiles.add(project.file(proguardFile));
-        }
-        return this;
-    }
-
-    @NonNull
-    public BuildType testProguardFile(Object proguardFile) {
-        testProguardFiles.add(project.file(proguardFile));
-        return this;
-    }
-
-    /**
-     * Adds new ProGuard configuration files.
-     */
-    @NonNull
-    public BuildType testProguardFiles(Object... proguardFileArray) {
-        testProguardFiles.addAll(project.files(proguardFileArray).files);
-        return this;
-    }
-
-    @NonNull
-    public BuildType consumerProguardFiles(Object... proguardFileArray) {
-        consumerProguardFiles.addAll(project.files(proguardFileArray).files);
-        return this;
-    }
-
-    @NonNull
-    public BuildType setConsumerProguardFiles(Iterable<?> proguardFileIterable) {
-        consumerProguardFiles.clear();
-        for (Object proguardFile : proguardFileIterable) {
-            consumerProguardFiles.add(project.file(proguardFile));
-        }
-        return this;
-    }
-
-    void ndk(Action<NdkOptions> action) {
-        action.execute(ndkConfig)
-    }
-
-    Boolean getUseJack() {
-        return useJack
-    }
-
-    void setUseJack(Boolean useJack) {
-        this.useJack = useJack
-    }
-
-    void useJack(Boolean useJack) {
-        setUseJack(useJack)
-    }
-
-    boolean shrinkResources = false // opt-in for now until we've validated it in the field
-
-    void shrinkResources(boolean flag) {
-        this.shrinkResources = flag
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.java
new file mode 100644
index 0000000..9b7274d
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.java
@@ -0,0 +1,326 @@
+/*
+ * 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.internal.dsl;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.DefaultBuildType;
+import com.android.builder.model.BaseConfig;
+import com.android.builder.model.ClassField;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.io.Serializable;
+
+/**
+ * DSL object to configure build types.
+ */
+public class BuildType extends DefaultBuildType implements CoreBuildType, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @NonNull
+    private final Project project;
+
+    @NonNull
+    private final Logger logger;
+
+    @Nullable
+    private final NdkOptions ndkConfig;
+
+    @Nullable
+    private Boolean useJack;
+
+    private boolean shrinkResources = false; // opt-in for now until we've validated it in the field
+
+    public BuildType(@NonNull String name,
+                     @NonNull Project project,
+                     @NonNull Instantiator instantiator,
+                     @NonNull Logger logger) {
+        super(name);
+        this.project = project;
+        this.logger = logger;
+        ndkConfig = instantiator.newInstance(NdkOptions.class);
+    }
+
+    @VisibleForTesting
+    BuildType(@NonNull String name,
+              @NonNull Project project,
+              @NonNull Logger logger) {
+        super(name);
+        this.project = project;
+        this.logger = logger;
+        ndkConfig = null;
+    }
+
+    @Override
+    @Nullable
+    public CoreNdkOptions getNdkConfig() {
+        return ndkConfig;
+    }
+
+    /**
+     * Initialize the DSL object. Not meant to be used from the build scripts.
+     */
+    public void init(SigningConfig debugSigningConfig) {
+        if (BuilderConstants.DEBUG.equals(getName())) {
+            setDebuggable(true);
+            setEmbedMicroApp(false);
+
+            assert debugSigningConfig != null;
+            setSigningConfig(debugSigningConfig);
+        } else if (BuilderConstants.RELEASE.equals(getName())) {
+            // no config needed for now.
+        }
+    }
+
+    /** The signing configuration. */
+    @Override
+    @Nullable
+    public SigningConfig getSigningConfig() {
+        return (SigningConfig) super.getSigningConfig();
+    }
+
+    @Override
+    protected void _initWith(@NonNull BaseConfig that) {
+        super._initWith(that);
+        BuildType thatBuildType = (BuildType) that;
+        shrinkResources = thatBuildType.isShrinkResources();
+        useJack = thatBuildType.getUseJack();
+    }
+
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (useJack != null ? useJack.hashCode() : 0);
+        result = 31 * result + (shrinkResources ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BuildType)) return false;
+        if (!super.equals(o)) return false;
+        BuildType other = (BuildType) o;
+        if (useJack != other.getUseJack()) return false;
+        if (shrinkResources != other.isShrinkResources()) return false;
+
+        return true;
+    }
+
+    // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
+
+    /**
+     * Adds a new field to the generated BuildConfig class.
+     *
+     * <p>The field is generated as: <code>&lt;type&gt; &lt;name&gt; = &lt;value&gt;;</code>
+     *
+     * <p>This means each of these must have valid Java content. If the type is a String, then the
+     * value should include quotes.
+     *
+     * @param type the type of the field
+     * @param name the name of the field
+     * @param value the value of the field
+     */
+    public void buildConfigField(
+            @NonNull String type,
+            @NonNull String name,
+            @NonNull String value) {
+        ClassField alreadyPresent = getBuildConfigFields().get(name);
+        if (alreadyPresent != null) {
+            logger.info("BuildType({}): buildConfigField '{}' value is being replaced: {} -> {}",
+                    getName(), name, alreadyPresent.getValue(), value);
+        }
+        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
+    }
+
+    /**
+     * Adds a new generated resource.
+     *
+     * @param type the type of the resource
+     * @param name the name of the resource
+     * @param value the value of the resource
+     */
+    public void resValue(
+            @NonNull String type,
+            @NonNull String name,
+            @NonNull String value) {
+        ClassField alreadyPresent = getResValues().get(name);
+        if (alreadyPresent != null) {
+            logger.info("BuildType({}): resValue '{}' value is being replaced: {} -> {}",
+                    getName(), name, alreadyPresent.getValue(), value);
+        }
+        addResValue(AndroidBuilder.createClassField(type, name, value));
+    }
+
+    /**
+     * Adds a new ProGuard configuration file.
+     *
+     * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
+     *
+     * <p>There are 2 default rules files
+     * <ul>
+     *     <li>proguard-android.txt
+     *     <li>proguard-android-optimize.txt
+     * </ul>
+     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code>
+     * will return the full path to the files. They are identical except for enabling optimizations.
+     */
+    @NonNull
+    public BuildType proguardFile(@NonNull Object proguardFile) {
+        getProguardFiles().add(project.file(proguardFile));
+        return this;
+    }
+
+    /**
+     * Adds new ProGuard configuration files.
+     */
+    @NonNull
+    public BuildType proguardFiles(@NonNull Object... proguardFileArray) {
+        getProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+        return this;
+    }
+
+    /**
+     * Sets the ProGuard configuration files.
+     */
+    @NonNull
+    public BuildType setProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+        getProguardFiles().clear();
+        for (Object proguardFile : proguardFileIterable) {
+            getProguardFiles().add(project.file(proguardFile));
+        }
+        return this;
+    }
+
+    /**
+     * Specifies a proguard rule file to be included in the published AAR.
+     *
+     * This proguard rule file will then be used by any application project that consume the AAR
+     * (if proguard is enabled).
+     *
+     * This allows AAR to specify shrinking or obfuscation exclude rules.
+     *
+     * This is only valid for Library project. This is ignored in Application project.
+     */
+    @NonNull
+    public BuildType testProguardFile(@NonNull Object proguardFile) {
+        getTestProguardFiles().add(project.file(proguardFile));
+        return this;
+    }
+
+    /**
+     * Adds new ProGuard configuration files.
+     */
+    @NonNull
+    public BuildType testProguardFiles(@NonNull Object... proguardFileArray) {
+        getTestProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+        return this;
+    }
+
+    @NonNull
+    public BuildType consumerProguardFiles(@NonNull Object... proguardFileArray) {
+        getConsumerProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+        return this;
+    }
+
+    /**
+     * Specifies a proguard rule file to be included in the published AAR.
+     *
+     * This proguard rule file will then be used by any application project that consume the AAR
+     * (if proguard is enabled).
+     *
+     * This allows AAR to specify shrinking or obfuscation exclude rules.
+     *
+     * This is only valid for Library project. This is ignored in Application project.
+     */
+    @NonNull
+    public BuildType setConsumerProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+        getConsumerProguardFiles().clear();
+        for (Object proguardFile : proguardFileIterable) {
+            getConsumerProguardFiles().add(project.file(proguardFile));
+        }
+        return this;
+    }
+
+    public void ndk(@NonNull Action<NdkOptions> action) {
+        action.execute(ndkConfig);
+    }
+
+    /**
+     * Whether the experimental Jack toolchain should be used.
+     */
+    @Override
+    @Nullable
+    public Boolean getUseJack() {
+        return useJack;
+    }
+
+    /**
+     * Whether the experimental Jack toolchain should be used.
+     */
+    public void setUseJack(@Nullable Boolean useJack) {
+        this.useJack = useJack;
+    }
+
+    /**
+     * Whether the experimental Jack toolchain should be used.
+     */
+    public void useJack(@Nullable Boolean useJack) {
+        setUseJack(useJack);
+    }
+
+    /**
+     * Whether shrinking of unused resources is enabled.
+     *
+     * Default is false;
+     */
+    @Override
+    public boolean isShrinkResources() {
+        return shrinkResources;
+    }
+
+    public void setShrinkResources(boolean shrinkResources) {
+        this.shrinkResources = shrinkResources;
+    }
+
+    /**
+     * Whether shrinking of unused resources is enabled.
+     *
+     * Default is false;
+     */
+    public void shrinkResources(boolean flag) {
+        this.shrinkResources = flag;
+    }
+
+    public void jarJarRuleFile(@NonNull Object file) {
+        getJarJarRuleFiles().add(project.file(file));
+    }
+
+    public void jarJarRuleFiles(@NonNull Object... files) {
+        getJarJarRuleFiles().clear();
+        for (Object file : files) {
+            getJarJarRuleFiles().add(project.file(file));
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreBuildType.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreBuildType.java
new file mode 100644
index 0000000..25ff6c5
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreBuildType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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.Nullable;
+import com.android.builder.model.BuildType;
+
+/**
+ * A build type with addition properties for building with Gradle plugin.
+ */
+public interface CoreBuildType extends BuildType {
+
+    @Nullable
+    CoreNdkOptions getNdkConfig();
+
+    @Nullable
+    Boolean getUseJack();
+
+    boolean isShrinkResources();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreNdkOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreNdkOptions.java
new file mode 100644
index 0000000..4302131
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreNdkOptions.java
@@ -0,0 +1,64 @@
+/*
+ * 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.Nullable;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Base class for NDK config file.
+ */
+public interface CoreNdkOptions {
+
+    /**
+     * The module name
+     */
+    @Nullable
+    String getModuleName();
+
+    /**
+     * The C Flags
+     */
+    @Nullable
+    String getcFlags();
+
+    /**
+     * The LD Libs
+     */
+    @Nullable
+    List<String> getLdLibs();
+
+    /**
+     * The ABI Filters
+     */
+    @Nullable
+    Set<String> getAbiFilters();
+
+    /**
+     * The APP_STL value
+     */
+    @Nullable
+    String getStl();
+
+    /**
+     * Number of parallel threads to spawn.
+     */
+    @Nullable
+    Integer getJobs();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreProductFlavor.java
new file mode 100644
index 0000000..2909787
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreProductFlavor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.Nullable;
+import com.android.builder.model.ProductFlavor;
+
+import org.gradle.api.Named;
+
+/**
+ * A product flavor with addition properties for building with Gradle plugin.
+ */
+public interface CoreProductFlavor extends ProductFlavor, Named {
+
+    @Nullable
+    CoreNdkOptions getNdkConfig();
+
+    @Nullable
+    Boolean getUseJack();
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreSigningConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreSigningConfig.java
new file mode 100644
index 0000000..7faa5db
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreSigningConfig.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 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.Nullable;
+
+import java.io.File;
+import com.android.builder.model.SigningConfig;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+
+/**
+ * A SigningConfig with added annotations to be used with @Nested in a Gradle task.
+ */
+public interface CoreSigningConfig extends SigningConfig {
+
+    @Override
+    @Nullable
+    @InputFile @Optional
+    File getStoreFile();
+
+    @Override
+    @Nullable
+    @Input
+    String getStorePassword();
+
+    @Override
+    @Nullable
+    @Input
+    String getKeyAlias();
+
+    @Override
+    @Nullable
+    @Input
+    String getStoreType();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java
index e196bc1..fd14504 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java
@@ -16,8 +16,6 @@
 
 package com.android.build.gradle.internal.dsl;
 
-import static com.android.build.OutputFile.NO_FILTER;
-
 import com.android.annotations.NonNull;
 import com.android.resources.Density;
 import com.google.common.collect.ImmutableSet;
@@ -25,12 +23,13 @@
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
 /**
- * Data for per-density splits.
+ * DSL object for configuring per-density splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
  */
 public class DensitySplitOptions extends SplitOptions {
 
@@ -79,6 +78,11 @@
         compatibleScreens = Sets.newHashSet(sizes);
     }
 
+    /**
+     * Adds a new compatible screen.
+     *
+     * <p>See {@link #getCompatibleScreens()}.
+     */
     public void compatibleScreens(@NonNull String... sizes) {
         if (compatibleScreens == null) {
             compatibleScreens = Sets.newHashSet(sizes);
@@ -89,7 +93,10 @@
     }
 
     /**
-     * TODO: Document.
+     * A list of compatible screens.
+     *
+     * <p>This will inject a matching <code>&lt;compatible-screens&gt;&lt;screen ...&gt;</code>
+     * node in the manifest. This is optional.
      */
     @NonNull
     public Set<String> getCompatibleScreens() {
@@ -99,22 +106,6 @@
         return compatibleScreens;
     }
 
-    @NonNull
-    @Override
-    public Set<String> getApplicableFilters() {
-        // use a LinkedHashSet so iteration order follows insertion order.
-        LinkedHashSet<String> filters = new LinkedHashSet<String>();
-
-        // if splitting enabled, then add an entry with no filter for universal
-        // add it FIRST, since code assume that the first variant output will be the universal one.
-        if (isEnable()) {
-            filters.add(NO_FILTER);
-        }
-        filters.addAll(super.getApplicableFilters());
-        return filters;
-    }
-
-
     /**
      * Sets whether the build system should determine the splits based on the "language-*" folders
      * in the resources. If the auto mode is set to true, the include list will be ignored.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavor.groovy
deleted file mode 100644
index f69545b..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavor.groovy
+++ /dev/null
@@ -1,61 +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.build.gradle.internal.dsl
-import com.android.annotations.NonNull
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.build.gradle.internal.LoggingUtil.displayDeprecationWarning
-
-/**
- * A version of {@link ProductFlavor} that can receive a dimension name.
- */
-// TODO: Remove interface and implementors now ProductFlavor has dimension.
-@Deprecated
-public class GroupableProductFlavor
-        extends ProductFlavor implements com.android.build.gradle.api.GroupableProductFlavor {
-
-    public GroupableProductFlavor(
-            @NonNull String name,
-            @NonNull Project project,
-            @NonNull Instantiator instantiator,
-            @NonNull Logger logger) {
-        super(name, project, instantiator, logger)
-    }
-
-    @Deprecated
-    public void setFlavorDimension(String dimension) {
-        displayDeprecationWarning(logger, project,
-                "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
-                        "it has been replaced by 'dimension'.")
-        setDimension(dimension);
-    }
-
-    /**
-     * Name of the dimension this product flavor belongs to. Has been replaced by
-     * <code>dimension</code>
-     */
-    @Deprecated
-    public String getFlavorDimension() {
-        displayDeprecationWarning(logger, project,
-                "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
-                        "it has been replaced by 'dimension'.")
-        return getDimension();
-    }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
deleted file mode 100644
index 92204e4..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
+++ /dev/null
@@ -1,52 +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.build.gradle.internal.dsl;
-
-import com.android.annotations.NonNull;
-
-import org.gradle.api.NamedDomainObjectFactory;
-import org.gradle.api.Project;
-import org.gradle.api.logging.Logger;
-import org.gradle.internal.reflect.Instantiator;
-
-/**
- * Factory to create GroupableProductFlavor object using an {@link Instantiator} to add
- * the DSL methods.
- */
-public class GroupableProductFlavorFactory implements NamedDomainObjectFactory<GroupableProductFlavor> {
-
-    @NonNull
-    private final Instantiator instantiator;
-    @NonNull
-    private final Project project;
-    @NonNull
-    private final Logger logger;
-
-    public GroupableProductFlavorFactory(@NonNull Instantiator instantiator,
-                                         @NonNull Project project,
-                                         @NonNull Logger logger) {
-        this.instantiator = instantiator;
-        this.project = project;
-        this.logger = logger;
-    }
-
-    @Override
-    public GroupableProductFlavor create(String name) {
-        return instantiator.newInstance(GroupableProductFlavor.class,
-                name, project, instantiator, logger);
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java
index 25d1e08..ce3700a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java
@@ -25,7 +25,9 @@
 import java.util.Set;
 
 /**
- * Data for per-language split.
+ * DSL object for configuring per-language splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
  */
 public class LanguageSplitOptions {
 
@@ -44,6 +46,9 @@
         include = Sets.newHashSet(list);
     }
 
+    /**
+     * Adds an include pattern.
+     */
     public void include(@NonNull String... includes) {
         if (include == null) {
             include = Sets.newHashSet(includes);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java
index d5af4fb..fc10b78 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java
@@ -18,7 +18,6 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.core.NdkConfig;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
@@ -34,7 +33,7 @@
 /**
  * DSL object for NDK-related settings.
  */
-public class NdkOptions implements NdkConfig, Serializable {
+public class NdkOptions implements CoreNdkOptions, Serializable {
     private static final long serialVersionUID = 1L;
 
     private String moduleName;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java
index 94ef560..2b86f65 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java
@@ -30,6 +30,7 @@
 
     private Set<String> excludes = Sets.newHashSet("LICENSE.txt", "LICENSE");
     private Set<String> pickFirsts = Sets.newHashSet();
+    private Set<String> merges = Sets.newHashSet();
 
     /**
      * Returns the list of excluded paths.
@@ -78,4 +79,26 @@
     public void setPickFirsts(Set<String> pickFirsts) {
         this.pickFirsts = Sets.newHashSet(pickFirsts);
     }
+
+    /**
+     * Returns the list of paths where all occurrences are concatenated and packaged in the APK.
+     */
+    @Override
+    @NonNull
+    @Input
+    public Set<String> getMerges() {
+        return Sets.newHashSet(merges);
+    }
+
+    public void setMerges(Set<String> merges) {
+        this.merges = Sets.newHashSet(merges);
+    }
+
+    /**
+     * Adds a merge path.
+     * @param path the path, as packaged in the APK
+     */
+    public void merge(String path) {
+        merges.add(path);
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PreprocessingOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PreprocessingOptions.java
new file mode 100644
index 0000000..72a9211
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PreprocessingOptions.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 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 static com.google.common.base.Preconditions.checkArgument;
+
+import com.android.resources.Density;
+import com.google.common.collect.Sets;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Resource preprocessing options.
+ */
+public class PreprocessingOptions {
+    private EnumSet<Density> densities;
+    private boolean enabled;
+
+    public PreprocessingOptions() {
+        this.enabled = true;
+
+        // TODO: What are the right default values?
+        this.densities = EnumSet.of(
+                Density.MEDIUM,
+                Density.HIGH,
+                Density.XHIGH,
+                Density.XXHIGH);
+    }
+
+    /**
+     * Whether to enable resources pre-processing. This is disabled by default.
+     *
+     * <p>If resources pre-processing is enabled, the build process will create two copies of the
+     * resource tree: one with a merged view of resources for a given variant and one with the
+     * preprocessed files, ready to be packaged. This may slow down your clean builds.
+     */
+    public boolean getEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    /**
+     * Set of screen densities for which PNG files should be generated based on vector drawable
+     * resource files.
+     *
+     * <p>Default to {@code ["mdpi", "hdpi", "xhdpi", "xxhdpi"]}.
+     */
+    public Set<String> getDensities() {
+        Set<String> result = Sets.newHashSet();
+        for (Density density : densities) {
+            result.add(density.getResourceValue());
+        }
+        return result;
+    }
+
+    public void setDensities(Set<String> densities) {
+        EnumSet<Density> newValue = EnumSet.noneOf(Density.class);
+        for (String density : densities) {
+            Density typedValue = Density.getEnum(density);
+            checkArgument(typedValue != null, "Unrecognized density %s", density);
+            newValue.add(typedValue);
+        }
+        this.densities = newValue;
+    }
+
+    /**
+     * Returns the densities to generate as the underlying enum values.
+     *
+     * <p>Not meant to be used in build scripts.
+     */
+    public Set<Density> getTypedDensities() {
+        return densities;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.groovy
deleted file mode 100644
index 72dbac4..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.groovy
+++ /dev/null
@@ -1,293 +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.build.gradle.internal.dsl
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.core.NdkConfig
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultApiVersion
-import com.android.builder.core.DefaultProductFlavor
-import com.android.builder.model.ApiVersion
-import com.android.builder.model.ClassField
-import com.google.common.base.Strings
-import org.gradle.api.Action
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * DSL object used to configure product flavors.
- */
-class ProductFlavor extends DefaultProductFlavor {
-
-    @NonNull
-    protected final Project project
-
-    @NonNull
-    protected final Logger logger
-
-    private final NdkOptions ndkConfig
-
-    private Boolean useJack
-
-    ProductFlavor(@NonNull String name,
-            @NonNull Project project,
-            @NonNull Instantiator instantiator,
-            @NonNull Logger logger) {
-        super(name)
-        this.project = project
-        this.logger = logger
-        ndkConfig = instantiator.newInstance(NdkOptions.class)
-    }
-
-    @Nullable
-    public NdkConfig getNdkConfig() {
-        return ndkConfig;
-    }
-
-    @NonNull
-    public void setMinSdkVersion(int minSdkVersion) {
-        setMinSdkVersion(new DefaultApiVersion(minSdkVersion));
-    }
-
-    /**
-     * Sets minimum SDK version.
-     *
-     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
-     * uses-sdk element documentation</a>.
-     */
-    @NonNull
-    public void minSdkVersion(int minSdkVersion) {
-        setMinSdkVersion(minSdkVersion);
-    }
-
-    @NonNull
-    public void setMinSdkVersion(String minSdkVersion) {
-        setMinSdkVersion(getApiVersion(minSdkVersion))
-    }
-
-    /**
-     * Sets minimum SDK version.
-     *
-     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
-     * uses-sdk element documentation</a>.
-     */
-    @NonNull
-    public void minSdkVersion(String minSdkVersion) {
-        setMinSdkVersion(minSdkVersion);
-    }
-
-    @NonNull
-    public com.android.builder.model.ProductFlavor setTargetSdkVersion(int targetSdkVersion) {
-        setTargetSdkVersion(new DefaultApiVersion(targetSdkVersion));
-        return this;
-    }
-
-    /**
-     * Sets the target SDK version to the given value.
-     *
-     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
-     * uses-sdk element documentation</a>.
-     */
-    @NonNull
-    public void targetSdkVersion(int targetSdkVersion) {
-        setTargetSdkVersion(targetSdkVersion);
-    }
-
-    @NonNull
-    public void setTargetSdkVersion(String targetSdkVersion) {
-        setTargetSdkVersion(getApiVersion(targetSdkVersion))
-    }
-
-    /**
-     * Sets the target SDK version to the given value.
-     *
-     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
-     * uses-sdk element documentation</a>.
-     */
-    @NonNull
-    public void targetSdkVersion(String targetSdkVersion) {
-        setTargetSdkVersion(targetSdkVersion);
-    }
-
-    @NonNull
-    public void maxSdkVersion(int targetSdkVersion) {
-        setMaxSdkVersion(targetSdkVersion);
-    }
-
-    @Nullable
-    private static ApiVersion getApiVersion(@Nullable String value) {
-        if (!Strings.isNullOrEmpty(value)) {
-            if (Character.isDigit(value.charAt(0))) {
-                try {
-                    int apiLevel = Integer.valueOf(value)
-                    return new DefaultApiVersion(apiLevel)
-                } catch (NumberFormatException e) {
-                    throw new RuntimeException("'${value}' is not a valid API level. ", e)
-                }
-            }
-
-            return new DefaultApiVersion(value)
-        }
-
-        return null
-    }
-
-    /**
-     * Signing config used by this product flavor.
-     */
-    @Override
-    SigningConfig getSigningConfig() {
-        return (SigningConfig) super.signingConfig
-    }
-
-// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
-
-    public void buildConfigField(
-            @NonNull String type,
-            @NonNull String name,
-            @NonNull String value) {
-        ClassField alreadyPresent = getBuildConfigFields().get(name);
-        if (alreadyPresent != null) {
-            String flavorName = getName();
-            if (BuilderConstants.MAIN.equals(flavorName)) {
-                logger.info(
-                        "DefaultConfig: buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
-            } else {
-                logger.info(
-                        "ProductFlavor($flavorName): buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
-            }
-        }
-        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
-    }
-
-    public void resValue(
-            @NonNull String type,
-            @NonNull String name,
-            @NonNull String value) {
-        ClassField alreadyPresent = getResValues().get(name);
-        if (alreadyPresent != null) {
-            String flavorName = getName();
-            if (BuilderConstants.MAIN.equals(flavorName)) {
-                logger.info(
-                        "DefaultConfig: resValue '$name' value is being replaced: ${alreadyPresent.value} -> $value");
-            } else {
-                logger.info(
-                        "ProductFlavor($flavorName): resValue '$name' value is being replaced: ${alreadyPresent.value} -> $value");
-            }
-        }
-        addResValue(AndroidBuilder.createClassField(type, name, value));
-    }
-
-    /**
-     * Adds a new ProGuard configuration file.
-     *
-     * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
-     *
-     * <p>There are 2 default rules files
-     * <ul>
-     *     <li>proguard-android.txt
-     *     <li>proguard-android-optimize.txt
-     * </ul>
-     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
-     * full path to the files. They are identical except for enabling optimizations.
-     */
-    @NonNull
-    public void proguardFile(Object proguardFile) {
-        proguardFiles.add(project.file(proguardFile))
-    }
-
-    /**
-     * Adds new ProGuard configuration files.
-     */
-    @NonNull
-    public void proguardFiles(Object... proguardFileArray) {
-        proguardFiles.addAll(project.files(proguardFileArray).files)
-    }
-
-    /**
-     * Sets the ProGuard configuration files.
-     */
-    @NonNull
-    public void setProguardFiles(Iterable<?> proguardFileIterable) {
-        proguardFiles.clear()
-        for (Object proguardFile : proguardFileIterable) {
-            proguardFiles.add(project.file(proguardFile))
-        }
-    }
-
-    @NonNull
-    public void testProguardFile(Object proguardFile) {
-        testProguardFiles.add(project.file(proguardFile));
-    }
-
-    /**
-     * Adds new ProGuard configuration files.
-     */
-    @NonNull
-    public void testProguardFiles(Object... proguardFileArray) {
-        testProguardFiles.addAll(project.files(proguardFileArray).files);
-    }
-
-    @NonNull
-    public void consumerProguardFiles(Object... proguardFileArray) {
-        consumerProguardFiles.addAll(project.files(proguardFileArray).files)
-    }
-
-    @NonNull
-    public void setConsumerProguardFiles(Iterable<?> proguardFileIterable) {
-        consumerProguardFiles.clear()
-        for (Object proguardFile : proguardFileIterable) {
-            consumerProguardFiles.add(project.file(proguardFile))
-        }
-    }
-
-    void ndk(Action<NdkOptions> action) {
-        action.execute(ndkConfig)
-        LoggingUtil.displayDeprecationWarning(
-                logger,
-                project,
-                "Current NDK support is deprecated.  Alternative will be provided in the future.");
-    }
-
-    void resConfig(@NonNull String config) {
-        addResourceConfiguration(config);
-    }
-
-    void resConfigs(@NonNull String... config) {
-        addResourceConfigurations(config);
-    }
-
-    void resConfigs(@NonNull Collection<String> config) {
-        addResourceConfigurations(config);
-    }
-
-    Boolean getUseJack() {
-        return useJack
-    }
-
-    void setUseJack(Boolean useJack) {
-        this.useJack = useJack
-    }
-
-    void useJack(Boolean useJack) {
-        setUseJack(useJack)
-    }
-}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.java
new file mode 100644
index 0000000..5733ced
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.java
@@ -0,0 +1,466 @@
+/*
+ * 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.internal.dsl;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.DefaultApiVersion;
+import com.android.builder.core.DefaultProductFlavor;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.ClassField;
+import com.google.common.base.Strings;
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.build.gradle.tasks.NdkCompile.USE_DEPRECATED_NDK;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * DSL object used to configure product flavors.
+ */
+public class ProductFlavor extends DefaultProductFlavor implements CoreProductFlavor {
+
+    @NonNull
+    protected final Project project;
+
+    @NonNull
+    protected final Logger logger;
+
+    @NonNull
+    private final NdkOptions ndkConfig;
+
+    @Nullable
+    private Boolean useJack;
+
+    public ProductFlavor(@NonNull String name,
+            @NonNull Project project,
+            @NonNull Instantiator instantiator,
+            @NonNull Logger logger) {
+        super(name);
+        this.project = project;
+        this.logger = logger;
+        ndkConfig = instantiator.newInstance(NdkOptions.class);
+    }
+
+    @Override
+    @Nullable
+    public CoreNdkOptions getNdkConfig() {
+        return ndkConfig;
+    }
+
+    public void setMinSdkVersion(int minSdkVersion) {
+        setMinSdkVersion(new DefaultApiVersion(minSdkVersion));
+    }
+
+    /**
+     * Sets minimum SDK version.
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+     * uses-sdk element documentation</a>.
+     */
+    public void minSdkVersion(int minSdkVersion) {
+        setMinSdkVersion(minSdkVersion);
+    }
+
+    public void setMinSdkVersion(@Nullable String minSdkVersion) {
+        setMinSdkVersion(getApiVersion(minSdkVersion));
+    }
+
+    /**
+     * Sets minimum SDK version.
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+     * uses-sdk element documentation</a>.
+     */
+    public void minSdkVersion(@Nullable String minSdkVersion) {
+        setMinSdkVersion(minSdkVersion);
+    }
+
+    @NonNull
+    public com.android.builder.model.ProductFlavor setTargetSdkVersion(int targetSdkVersion) {
+        setTargetSdkVersion(new DefaultApiVersion(targetSdkVersion));
+        return this;
+    }
+
+    /**
+     * Sets the target SDK version to the given value.
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+     * uses-sdk element documentation</a>.
+     */
+    public void targetSdkVersion(int targetSdkVersion) {
+        setTargetSdkVersion(targetSdkVersion);
+    }
+
+    public void setTargetSdkVersion(@Nullable String targetSdkVersion) {
+        setTargetSdkVersion(getApiVersion(targetSdkVersion));
+    }
+
+    /**
+     * Sets the target SDK version to the given value.
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+     * uses-sdk element documentation</a>.
+     */
+    public void targetSdkVersion(@Nullable String targetSdkVersion) {
+        setTargetSdkVersion(targetSdkVersion);
+    }
+
+    /**
+     * Sets the maximum SDK version to the given value.
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+     * uses-sdk element documentation</a>.
+     */
+    public void maxSdkVersion(int targetSdkVersion) {
+        setMaxSdkVersion(targetSdkVersion);
+    }
+
+    @Nullable
+    private static ApiVersion getApiVersion(@Nullable String value) {
+        if (!Strings.isNullOrEmpty(value)) {
+            if (Character.isDigit(value.charAt(0))) {
+                try {
+                    int apiLevel = Integer.valueOf(value);
+                    return new DefaultApiVersion(apiLevel);
+                } catch (NumberFormatException e) {
+                    throw new RuntimeException("'" + value + "' is not a valid API level. ", e);
+                }
+            }
+
+            return new DefaultApiVersion(value);
+        }
+
+        return null;
+    }
+
+    /**
+     * Adds a custom argument to the test instrumentation runner, e.g:
+     *
+     * <p><pre>testInstrumentationRunnerArgument "size", "medium"</pre>
+     *
+     * <p>Test runner arguments can also be specified from the command line:
+     *
+     * <p><pre>
+     * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest
+     * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar
+     * </pre>
+     */
+    public void testInstrumentationRunnerArgument(@NonNull String key, @NonNull String value) {
+        getTestInstrumentationRunnerArguments().put(key, value);
+    }
+
+    /**
+     * Adds custom arguments to the test instrumentation runner, e.g:
+     *
+     * <p><pre>testInstrumentationRunnerArguments(size: "medium", foo: "bar")</pre>
+     *
+     * <p>Test runner arguments can also be specified from the command line:
+     *
+     * <p><pre>
+     * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest
+     * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar
+     * </pre>
+     */
+    public void testInstrumentationRunnerArguments(@NonNull Map<String, String> args) {
+        getTestInstrumentationRunnerArguments().putAll(args);
+    }
+
+    /**
+     * Signing config used by this product flavor.
+     */
+    @Override
+    @Nullable
+    public SigningConfig getSigningConfig() {
+        return (SigningConfig) super.getSigningConfig();
+    }
+
+// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
+
+    /**
+     * Adds a new field to the generated BuildConfig class.
+     *
+     * The field is generated as:
+     *
+     * <type> <name> = <value>;
+     *
+     * This means each of these must have valid Java content. If the type is a String, then the
+     * value should include quotes.
+     *
+     * @param type the type of the field
+     * @param name the name of the field
+     * @param value the value of the field
+     */
+    public void buildConfigField(
+            @NonNull String type,
+            @NonNull String name,
+            @NonNull String value) {
+        ClassField alreadyPresent = getBuildConfigFields().get(name);
+        if (alreadyPresent != null) {
+            String flavorName = getName();
+            if (BuilderConstants.MAIN.equals(flavorName)) {
+                logger.info(
+                        "DefaultConfig: buildConfigField '{}' value is being replaced: {} -> {}",
+                        name, alreadyPresent.getValue(), value);
+            } else {
+                logger.info(
+                        "ProductFlavor({}): buildConfigField '{}' "
+                                + "value is being replaced: {} -> {}",
+                        flavorName, name, alreadyPresent.getValue(), value);
+            }
+        }
+        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
+    }
+
+    /**
+     * Adds a new generated resource.
+     *
+     * <p>This is equivalent to specifying a resource in res/values.
+     *
+     * <p>See <a href="http://developer.android.com/guide/topics/resources/available-resources.html">Resource Types</a>.
+     *
+     * @param type the type of the resource
+     * @param name the name of the resource
+     * @param value the value of the resource
+     */
+    public void resValue(
+            @NonNull String type,
+            @NonNull String name,
+            @NonNull String value) {
+        ClassField alreadyPresent = getResValues().get(name);
+        if (alreadyPresent != null) {
+            String flavorName = getName();
+            if (BuilderConstants.MAIN.equals(flavorName)) {
+                logger.info(
+                        "DefaultConfig: resValue '{}' value is being replaced: {} -> {}",
+                        name, alreadyPresent.getValue(), value);
+            } else {
+                logger.info(
+                        "ProductFlavor({}): resValue '{}' value is being replaced: {} -> {}",
+                        flavorName, name, alreadyPresent.getValue(), value);
+            }
+        }
+        addResValue(AndroidBuilder.createClassField(type, name, value));
+    }
+
+    /**
+     * Adds a new ProGuard configuration file.
+     *
+     * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
+     *
+     * <p>There are 2 default rules files
+     * <ul>
+     *     <li>proguard-android.txt
+     *     <li>proguard-android-optimize.txt
+     * </ul>
+     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+     * full path to the files. They are identical except for enabling optimizations.
+     */
+    public void proguardFile(@NonNull Object proguardFile) {
+        getProguardFiles().add(project.file(proguardFile));
+    }
+
+    /**
+     * Adds new ProGuard configuration files.
+     *
+     * <p>There are 2 default rules files
+     * <ul>
+     *     <li>proguard-android.txt
+     *     <li>proguard-android-optimize.txt
+     * </ul>
+     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+     * full path to the files. They are identical except for enabling optimizations.
+     */
+    public void proguardFiles(@NonNull Object... proguardFileArray) {
+        getProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+    }
+
+    /**
+     * Sets the ProGuard configuration files.
+     *
+     * <p>There are 2 default rules files
+     * <ul>
+     *     <li>proguard-android.txt
+     *     <li>proguard-android-optimize.txt
+     * </ul>
+     * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+     * full path to the files. They are identical except for enabling optimizations.
+     */
+    public void setProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+        getProguardFiles().clear();
+        for (Object proguardFile : proguardFileIterable) {
+            getProguardFiles().add(project.file(proguardFile));
+        }
+    }
+
+    /**
+     * Specifies a proguard rule file to be included in the published AAR.
+     *
+     * <p>This proguard rule file will then be used by any application project that consume the AAR
+     * (if proguard is enabled).
+     *
+     * <p>This allows AAR to specify shrinking or obfuscation exclude rules.
+     *
+     * <p>This is only valid for Library project. This is ignored in Application project.
+     */
+    public void testProguardFile(@NonNull Object proguardFile) {
+        getTestProguardFiles().add(project.file(proguardFile));
+    }
+
+    /**
+     * Adds new ProGuard configuration files.
+     */
+    public void testProguardFiles(Object... proguardFileArray) {
+        getTestProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+    }
+
+    public void consumerProguardFiles(Object... proguardFileArray) {
+        getConsumerProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+    }
+
+    /**
+     * Specifies a proguard rule file to be included in the published AAR.
+     *
+     * <p>This proguard rule file will then be used by any application project that consume the AAR
+     * (if proguard is enabled).
+     *
+     * <p>This allows AAR to specify shrinking or obfuscation exclude rules.
+     *
+     * <p>This is only valid for Library project. This is ignored in Application project.
+     */
+    public void setConsumerProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+        getConsumerProguardFiles().clear();
+        for (Object proguardFile : proguardFileIterable) {
+            getConsumerProguardFiles().add(project.file(proguardFile));
+        }
+    }
+
+    public void ndk(Action<NdkOptions> action) {
+        action.execute(ndkConfig);
+        if (!project.hasProperty(USE_DEPRECATED_NDK)) {
+            throw new RuntimeException(
+                    "Error: NDK integration is deprecated in the current plugin.  Consider trying " +
+                            "the new experimental plugin.  For details, see " +
+                            "http://tools.android.com/tech-docs/new-build-system/gradle-experimental.  " +
+                            "Set \"" + USE_DEPRECATED_NDK + "=true\" in gradle.properties to " +
+                            "continue using the current NDK integration.");
+        }
+    }
+
+    /**
+     * Adds a resource configuration filter.
+     *
+     * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type
+     * but of different value will be ignored from the final packaging of the APK.
+     *
+     * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc...
+     */
+    public void resConfig(@NonNull String config) {
+        addResourceConfiguration(config);
+    }
+
+    /**
+     * Adds several resource configuration filters.
+     *
+     * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type
+     * but of different value will be ignored from the final packaging of the APK.
+     *
+     * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc...
+     */
+    public void resConfigs(@NonNull String... config) {
+        addResourceConfigurations(config);
+    }
+
+    /**
+     * Adds several resource configuration filters.
+     *
+     * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type
+     * but of different value will be ignored from the final packaging of the APK.
+     *
+     * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc...
+     */
+    public void resConfigs(@NonNull Collection<String> config) {
+        addResourceConfigurations(config);
+    }
+
+    /**
+     * Whether the experimental Jack toolchain should be used.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a>
+     */
+    @Override
+    @Nullable
+    public Boolean getUseJack() {
+        return useJack;
+    }
+
+    /**
+     * Whether the experimental Jack toolchain should be used.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a>
+     */
+    public void setUseJack(Boolean useJack) {
+        this.useJack = useJack;
+    }
+
+    /**
+     * Whether the experimental Jack toolchain should be used.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a>
+     */
+    public void useJack(Boolean useJack) {
+        setUseJack(useJack);
+    }
+
+    @Deprecated
+    public void setFlavorDimension(String dimension) {
+        LoggingUtil.displayDeprecationWarning(logger, project,
+                "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
+                        "it has been replaced by 'dimension'.");
+        setDimension(dimension);
+    }
+
+    /**
+     * Name of the dimension this product flavor belongs to. Has been replaced by
+     * <code>dimension</code>
+     */
+    @Deprecated
+    public String getFlavorDimension() {
+        LoggingUtil.displayDeprecationWarning(logger, project,
+                "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
+                        "it has been replaced by 'dimension'.");
+        return getDimension();
+    }
+
+    public void jarJarRuleFile(Object file) {
+        getJarJarRuleFiles().add(project.file(file));
+    }
+
+    public void jarJarRuleFiles(Object ...files) {
+        getJarJarRuleFiles().clear();
+        for (Object file : files) {
+            getJarJarRuleFiles().add(project.file(file));
+        }
+    }
+}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java
index 4df150c..ad97b94 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java
@@ -34,7 +34,8 @@
 /**
  * DSL object for configuring signing configs.
  */
-public class SigningConfig extends DefaultSigningConfig implements Serializable, Named {
+public class SigningConfig extends DefaultSigningConfig implements Serializable, Named,
+        CoreSigningConfig {
     private static final long serialVersionUID = 1L;
 
     /**
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.groovy
deleted file mode 100644
index 7d587b9..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.groovy
+++ /dev/null
@@ -1,36 +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.build.gradle.internal.dsl
-
-import org.gradle.api.NamedDomainObjectFactory
-import org.gradle.internal.reflect.Instantiator
-/**
- * Factory to create SigningConfig object using an {@ling Instantiator} to add the DSL methods.
- */
-class SigningConfigFactory implements NamedDomainObjectFactory<SigningConfig> {
-
-    final Instantiator instantiator
-
-    public SigningConfigFactory(Instantiator instantiator) {
-        this.instantiator = instantiator
-    }
-
-    @Override
-    SigningConfig create(String name) {
-        return instantiator.newInstance(SigningConfig.class, name)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.java
new file mode 100644
index 0000000..49ce997
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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.internal.dsl;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.internal.reflect.Instantiator;
+/**
+ * Factory to create SigningConfig object using an {@link Instantiator} to add the DSL methods.
+ */
+public class SigningConfigFactory implements NamedDomainObjectFactory<SigningConfig> {
+
+    private final Instantiator instantiator;
+
+    public SigningConfigFactory(Instantiator instantiator) {
+        this.instantiator = instantiator;
+    }
+
+    @Override
+    @NonNull
+    public SigningConfig create(@NonNull String name) {
+        return instantiator.newInstance(SigningConfig.class, name);
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java
index 11c8707..c9d356b 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java
@@ -22,7 +22,6 @@
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -74,7 +73,7 @@
     /**
      * Resets the list of included split configuration.
      *
-     * Use this before calling include, in order to manually configure the list of configuration
+     * <p>Use this before calling include, in order to manually configure the list of configuration
      * to split on, rather than excluding from the default list.
      */
     public void reset() {
@@ -84,14 +83,14 @@
     /**
      * Returns a list of all applicable filters for this dimension.
      *
-     * The list can return null, indicating that the no-filter option must also be used.
+     * <p>The list can return null, indicating that the no-filter option must also be used.
      *
      * @return the filters to use.
      */
     @NonNull
     public Set<String> getApplicableFilters() {
         if (!enable) {
-            return Collections.singleton(null);
+            return Collections.emptySet();
         }
 
         Set<String> results = Sets.newHashSetWithExpectedSize(values.size());
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java
index 97c49b2..cd86795 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java
@@ -17,9 +17,6 @@
 package com.android.build.gradle.internal.dsl;
 
 import com.android.annotations.NonNull;
-import com.android.resources.Density;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 
 import org.gradle.api.Action;
 import org.gradle.internal.reflect.Instantiator;
@@ -27,7 +24,9 @@
 import java.util.Set;
 
 /**
- * Main entry point for all splits related information.
+ * DSL object for configuring APK Splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
  */
 public class Splits {
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.groovy
deleted file mode 100644
index 2753515..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.groovy
+++ /dev/null
@@ -1,109 +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.dsl
-
-import groovy.transform.CompileStatic
-import org.gradle.api.DomainObjectSet
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.tasks.testing.Test
-import org.gradle.util.ConfigureUtil
-/**
- * Options for running tests.
- */
-@CompileStatic
-class TestOptions {
-    /** Name of the results directory. */
-    String resultsDir
-
-    /** Name of the reports directory. */
-    String reportDir
-
-    /**
-     * Options for controlling unit tests execution.
-     *
-     * @since 1.1
-     */
-    UnitTestOptions unitTests = new UnitTestOptions()
-
-    /**
-     * Configures unit test options.
-     *
-     * @since 1.2
-     */
-    def unitTests(Closure closure) {
-        ConfigureUtil.configure(closure, unitTests);
-    }
-
-    /**
-     * Options for controlling unit tests execution.
-     */
-    static class UnitTestOptions {
-        private DomainObjectSet<Test> testTasks = new DefaultDomainObjectSet<Test>(Test)
-
-        /**
-         * Whether unmocked methods from android.jar should throw exceptions or return default
-         * values (i.e. zero or null).
-         *
-         * <p>See <a href="http://tools.android.com/tech-docs/unit-testing-support">Unit testing support</a> for details.
-         *
-         * @since 1.1
-         */
-        boolean returnDefaultValues
-
-        /**
-         * Configures all unit testing tasks.
-         *
-         * <p>See {@link Test} for available options.
-         *
-         * <p>Inside the closure you can check the name of the task to configure only some test
-         * tasks, e.g.
-         *
-         * <pre>
-         * android {
-         *     testOptions {
-         *         unitTests.all {
-         *             if (it.name == 'testDebug') {
-         *                 systemProperty 'debug', 'true'
-         *             }
-         *         }
-         *     }
-         * }
-         * </pre>
-         *
-         * @since 1.2
-         */
-        def all(Closure configClosure) {
-            testTasks.all { Test testTask ->
-                ConfigureUtil.configure(configClosure, testTask)
-            }
-        }
-
-        /**
-         * Configures a given test task. The configuration closures that were passed to
-         * {@link #all(Closure)} will be applied to it.
-         *
-         * <p>Not meant to be called from build scripts. The reason it exists is that tasks
-         * are created after the build scripts are evaluated, so users have to "register" their
-         * configuration closures first and we can only apply them later.
-         *
-         * @since 1.2
-         */
-        public applyConfiguration(Test task) {
-            this.testTasks.add(task)
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.java
new file mode 100644
index 0000000..a1af3e3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.java
@@ -0,0 +1,156 @@
+/*
+ * 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 org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.util.ConfigureUtil;
+
+import groovy.lang.Closure;
+
+/**
+ * Options for running tests.
+ */
+public class TestOptions {
+    @Nullable
+    private String resultsDir;
+
+    @Nullable
+    private String reportDir;
+
+    /**
+     * Options for controlling unit tests execution.
+     *
+     * @since 1.1
+     */
+    @NonNull
+    private final UnitTestOptions unitTests = new UnitTestOptions();
+
+    /**
+     * Configures unit test options.
+     *
+     * @since 1.2
+     */
+    public void unitTests(Closure closure) {
+        ConfigureUtil.configure(closure, unitTests);
+    }
+
+    /**
+     * Configures unit test options.
+     *
+     * @since 1.2
+     */
+    @NonNull
+    public UnitTestOptions getUnitTests() {
+        return unitTests;
+    }
+
+    /** Name of the results directory. */
+    @Nullable
+    public String getResultsDir() {
+        return resultsDir;
+    }
+
+    public void setResultsDir(@Nullable String resultsDir) {
+        this.resultsDir = resultsDir;
+    }
+
+    /** Name of the reports directory. */
+    @Nullable
+    public String getReportDir() {
+        return reportDir;
+    }
+
+    public void setReportDir(@Nullable String reportDir) {
+        this.reportDir = reportDir;
+    }
+
+    /**
+     * Options for controlling unit tests execution.
+     */
+    public static class UnitTestOptions {
+        private DomainObjectSet<Test> testTasks = new DefaultDomainObjectSet<Test>(Test.class);
+        private boolean returnDefaultValues;
+
+        /**
+         * Whether unmocked methods from android.jar should throw exceptions or return default
+         * values (i.e. zero or null).
+         *
+         * <p>See <a href="http://tools.android.com/tech-docs/unit-testing-support">Unit testing support</a> for details.
+         *
+         * @since 1.1
+         */
+        public boolean isReturnDefaultValues() {
+            return returnDefaultValues;
+        }
+
+        public void setReturnDefaultValues(boolean returnDefaultValues) {
+            this.returnDefaultValues = returnDefaultValues;
+        }
+
+        /**
+         * Configures all unit testing tasks.
+         *
+         * <p>See {@link Test} for available options.
+         *
+         * <p>Inside the closure you can check the name of the task to configure only some test
+         * tasks, e.g.
+         *
+         * <pre>
+         * android {
+         *     testOptions {
+         *         unitTests.all {
+         *             if (it.name == 'testDebug') {
+         *                 systemProperty 'debug', 'true'
+         *             }
+         *         }
+         *     }
+         * }
+         * </pre>
+         *
+         * @since 1.2
+         */
+        public void all(final Closure<Test> configClosure) {
+            testTasks.all(new Action<Test>() {
+                @Override
+                public void execute(Test testTask) {
+                    ConfigureUtil.configure(configClosure, testTask);
+                }
+            });
+        }
+
+
+        /**
+         * Configures a given test task. The configuration closures that were passed to
+         * {@link #all(Closure)} will be applied to it.
+         *
+         * <p>Not meant to be called from build scripts. The reason it exists is that tasks
+         * are created after the build scripts are evaluated, so users have to "register" their
+         * configuration closures first and we can only apply them later.
+         *
+         * @since 1.2
+         */
+        public void applyConfiguration(@NonNull Test task) {
+            this.testTasks.add(task);
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
index d13cc8a..e1ca92e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
@@ -22,6 +22,7 @@
 import com.android.builder.model.AndroidArtifactOutput;
 import com.android.builder.model.ClassField;
 import com.android.builder.model.Dependencies;
+import com.android.builder.model.NativeLibrary;
 import com.android.builder.model.SourceProvider;
 import com.google.common.collect.Sets;
 
@@ -53,6 +54,8 @@
     @Nullable
     private final Set<String> abiFilters;
     @NonNull
+    private final Collection<NativeLibrary> nativeLibraries;
+    @NonNull
     private final Map<String, ClassField> buildConfigFields;
     @NonNull
     private final Map<String, ClassField> resValues;
@@ -74,6 +77,7 @@
             @Nullable SourceProvider variantSourceProvider,
             @Nullable SourceProvider multiFlavorSourceProviders,
             @Nullable Set<String> abiFilters,
+            @NonNull Collection<NativeLibrary> nativeLibraries,
             @NonNull Map<String,ClassField> buildConfigFields,
             @NonNull Map<String,ClassField> resValues) {
         super(name, assembleTaskName, compileTaskName, classesFolder, javaResourcesFolder,
@@ -87,6 +91,7 @@
         this.sourceGenTaskName = sourceGenTaskName;
         this.generatedResourceFolders = generatedResourceFolders;
         this.abiFilters = abiFilters;
+        this.nativeLibraries = nativeLibraries;
         this.buildConfigFields = buildConfigFields;
         this.resValues = resValues;
     }
@@ -140,6 +145,12 @@
 
     @NonNull
     @Override
+    public Collection<NativeLibrary> getNativeLibraries() {
+        return nativeLibraries;
+    }
+
+    @NonNull
+    @Override
     public Map<String, ClassField> getBuildConfigFields() {
         return buildConfigFields;
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
index 62a1906..c928bf9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
@@ -65,6 +65,7 @@
     private final File publicResources;
     @NonNull
     private final List<AndroidLibrary> dependencies;
+    private final boolean isOptional;
 
     AndroidLibraryImpl(
             @NonNull LibraryDependency libraryDependency,
@@ -90,6 +91,7 @@
         lintJar = libraryDependency.getLintJar();
         annotations = libraryDependency.getExternalAnnotations();
         publicResources = libraryDependency.getPublicResources();
+        isOptional = libraryDependency.isOptional();
 
         this.project = project;
         this.variant = variant;
@@ -196,4 +198,9 @@
     public File getPublicResources() {
         return publicResources;
     }
+
+    @Override
+    public boolean isOptional() {
+        return isOptional;
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java
index a5015ec..b57c225 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java
@@ -48,6 +48,8 @@
     private File mMultiDexKeepFile;
     @Nullable
     private File mMultiDexKeepProguard;
+    @Nullable
+    private List<File> mJarJarRuleFiles;
 
     protected BaseConfigImpl(@NonNull BaseConfig baseConfig) {
         mManifestPlaceholders = ImmutableMap.copyOf(baseConfig.getManifestPlaceholders());
@@ -56,6 +58,7 @@
         mMultiDexEnabled = baseConfig.getMultiDexEnabled();
         mMultiDexKeepFile = baseConfig.getMultiDexKeepFile();
         mMultiDexKeepProguard = baseConfig.getMultiDexKeepProguard();
+        mJarJarRuleFiles = baseConfig.getJarJarRuleFiles();
     }
 
     @NonNull
@@ -112,6 +115,12 @@
         return mMultiDexKeepProguard;
     }
 
+    @NonNull
+    @Override
+    public List<File> getJarJarRuleFiles() {
+        return mJarJarRuleFiles;
+    }
+
     @Override
     public String toString() {
         return "BaseConfigImpl{" +
@@ -119,6 +128,7 @@
                 ", mBuildConfigFields=" + mBuildConfigFields +
                 ", mResValues=" + mResValues +
                 ", mMultiDexEnabled=" + mMultiDexEnabled +
+                ", mJarJarRuleFiles=" + mJarJarRuleFiles +
                 '}';
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
index 3168120..4528821 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
@@ -28,6 +28,7 @@
 import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SigningConfig;
 import com.android.builder.model.SyncIssue;
+import com.android.builder.model.NativeToolchain;
 import com.android.builder.model.Variant;
 import com.google.common.collect.Lists;
 
@@ -69,6 +70,8 @@
     private final File buildFolder;
     @Nullable
     private final String resourcePrefix;
+    @NonNull
+    private final Collection<NativeToolchain> nativeToolchains;
     private final boolean isLibrary;
     private final int apiVersion;
 
@@ -97,6 +100,7 @@
             @NonNull LintOptions lintOptions,
             @NonNull File buildFolder,
             @Nullable String resourcePrefix,
+            @NonNull Collection<NativeToolchain> nativeToolchains,
             boolean isLibrary,
             int apiVersion) {
         this.modelVersion = modelVersion;
@@ -116,6 +120,7 @@
         this.resourcePrefix = resourcePrefix;
         this.isLibrary = isLibrary;
         this.apiVersion = apiVersion;
+        this.nativeToolchains = nativeToolchains;
     }
 
     @NonNull
@@ -267,4 +272,9 @@
         return resourcePrefix;
     }
 
+    @NonNull
+    @Override
+    public Collection<NativeToolchain> getNativeToolchains() {
+        return nativeToolchains;
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java
index f4cd045..f4c37df 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java
@@ -17,6 +17,7 @@
 package com.android.build.gradle.internal.model;
 
 import static com.android.SdkConstants.DOT_JAR;
+import static com.android.SdkConstants.FD_JARS;
 
 import com.android.annotations.NonNull;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
@@ -222,6 +223,8 @@
         if (aarFile.isFile()) {
             List<File> jarList = Lists.newArrayList();
 
+            File jarsFolder = new File(explodedFolder, FD_JARS);
+
             ZipFile zipFile = null;
             try {
                 //noinspection IOResourceOpenedButNotSafelyClosed
@@ -231,7 +234,7 @@
                     ZipEntry zipEntry = e.nextElement();
                     String name = zipEntry.getName();
                     if (name.startsWith("libs/") && name.endsWith(DOT_JAR)) {
-                        jarList.add(new File(explodedFolder, name.replace('/', File.separatorChar)));
+                        jarList.add(new File(jarsFolder, name.replace('/', File.separatorChar)));
                     }
                 }
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
index 7f05729..160b9a7 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
@@ -36,6 +36,9 @@
 
     private final Set<String> ideSetupTaskNames;
 
+    @Nullable
+    private final File mockablePlatformJar;
+
     public static JavaArtifactImpl clone(@NonNull JavaArtifact javaArtifact) {
         SourceProvider variantSP = javaArtifact.getVariantSourceProvider();
         SourceProvider flavorsSP = javaArtifact.getMultiFlavorSourceProvider();
@@ -48,6 +51,7 @@
                 javaArtifact.getGeneratedSourceFolders(),
                 javaArtifact.getClassesFolder(),
                 javaArtifact.getJavaResourcesFolder(),
+                javaArtifact.getMockablePlatformJar(),
                 DependenciesImpl.cloneDependenciesForJavaArtifacts(javaArtifact.getDependencies()),
                 variantSP != null ? SourceProviderImpl.cloneProvider(variantSP) : null,
                 flavorsSP != null ? SourceProviderImpl.cloneProvider(flavorsSP) : null);
@@ -60,6 +64,7 @@
                             @NonNull Collection<File> generatedSourceFolders,
                             @NonNull File classesFolder,
                             @NonNull File javaResourcesFolder,
+                            @Nullable File mockablePlatformJar,
                             @NonNull Dependencies dependencies,
                             @Nullable SourceProvider variantSourceProvider,
                             @Nullable SourceProvider multiFlavorSourceProviders) {
@@ -67,6 +72,7 @@
                 dependencies,
                 variantSourceProvider, multiFlavorSourceProviders, generatedSourceFolders);
         this.ideSetupTaskNames = Sets.newHashSet(ideSetupTaskNames);
+        this.mockablePlatformJar = mockablePlatformJar;
     }
 
     @NonNull
@@ -74,4 +80,10 @@
     public Set<String> getIdeSetupTaskNames() {
         return ideSetupTaskNames;
     }
+
+    @Override
+    @Nullable
+    public File getMockablePlatformJar() {
+        return mockablePlatformJar;
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java
index 6bb260d..d40955c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java
@@ -48,4 +48,12 @@
     public List<? extends JavaLibrary> getDependencies() {
         return Collections.emptyList();
     }
+
+    @Override
+    public String toString() {
+        final StringBuffer sb = new StringBuffer("JavaLibraryImpl{");
+        sb.append("jarFile=").append(jarFile);
+        sb.append('}');
+        return sb.toString();
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
index 838ad3e..b6e18ee 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
@@ -15,20 +15,24 @@
  */
 
 package com.android.build.gradle.internal.model;
+
 import static com.android.builder.model.AndroidProject.ARTIFACT_MAIN;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.api.ApkOutputFile;
 import com.android.build.gradle.internal.BuildTypeData;
 import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.NdkHandler;
 import com.android.build.gradle.internal.ProductFlavorData;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
 import com.android.build.gradle.internal.scope.VariantScope;
 import com.android.build.gradle.internal.variant.ApkVariantOutputData;
 import com.android.build.gradle.internal.variant.BaseVariantData;
@@ -37,7 +41,6 @@
 import com.android.build.gradle.internal.variant.TestedVariantData;
 import com.android.builder.Version;
 import com.android.builder.core.AndroidBuilder;
-import com.android.builder.core.DefaultProductFlavor;
 import com.android.builder.core.VariantType;
 import com.android.builder.model.AaptOptions;
 import com.android.builder.model.AndroidArtifact;
@@ -47,18 +50,23 @@
 import com.android.builder.model.ArtifactMetaData;
 import com.android.builder.model.JavaArtifact;
 import com.android.builder.model.LintOptions;
+import com.android.builder.model.NativeLibrary;
+import com.android.builder.model.NativeToolchain;
+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.model.SyncIssue;
 import com.android.sdklib.AndroidVersion;
 import com.android.sdklib.IAndroidTarget;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.api.Project;
 import org.gradle.tooling.provider.model.ToolingModelBuilder;
 
@@ -66,6 +74,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Builder for the custom Android model.
@@ -75,13 +84,19 @@
     @NonNull
     private final AndroidBuilder androidBuilder;
     @NonNull
-    private final BaseExtension extension;
+    private final AndroidConfig config;
     @NonNull
     private final ExtraModelInfo extraModelInfo;
     @NonNull
     private final VariantManager variantManager;
     @NonNull
     private final TaskManager taskManager;
+    @NonNull
+    private final NdkHandler ndkHandler;
+    @NonNull
+    private Map<Abi, NativeToolchain> toolchains;
+    @NonNull
+    private NativeLibraryFactory nativeLibFactory;
 
     private final boolean isLibrary;
 
@@ -89,14 +104,18 @@
             @NonNull AndroidBuilder androidBuilder,
             @NonNull VariantManager variantManager,
             @NonNull TaskManager taskManager,
-            @NonNull BaseExtension extension,
+            @NonNull AndroidConfig config,
             @NonNull ExtraModelInfo extraModelInfo,
+            @NonNull NdkHandler ndkHandler,
+            @NonNull NativeLibraryFactory nativeLibraryFactory,
             boolean isLibrary) {
         this.androidBuilder = androidBuilder;
-        this.extension = extension;
+        this.config = config;
         this.extraModelInfo = extraModelInfo;
         this.variantManager = variantManager;
         this.taskManager = taskManager;
+        this.ndkHandler = ndkHandler;
+        this.nativeLibFactory = nativeLibraryFactory;
         this.isLibrary = isLibrary;
     }
 
@@ -112,11 +131,7 @@
 
     @Override
     public Object buildAll(String modelName, Project project) {
-        NamedDomainObjectContainer<SigningConfig> signingConfigs;
-
-        // Cast is needed due to covariance issues.
-        //noinspection unchecked
-        signingConfigs = (NamedDomainObjectContainer<SigningConfig>) (NamedDomainObjectContainer<?>) extension.getSigningConfigs();
+        Collection<? extends SigningConfig> signingConfigs = config.getSigningConfigs();
 
         // Get the boot classpath. This will ensure the target is configured.
         List<String> bootClasspath = androidBuilder.getBootClasspathAsStrings();
@@ -135,14 +150,16 @@
         }
 
         LintOptions lintOptions = com.android.build.gradle.internal.dsl.LintOptions.create(
-                extension.getLintOptions());
+                config.getLintOptions());
 
-        AaptOptions aaptOptions = AaptOptionsImpl.create(extension.getAaptOptions());
+        AaptOptions aaptOptions = AaptOptionsImpl.create(config.getAaptOptions());
 
         List<SyncIssue> syncIssues = Lists.newArrayList(extraModelInfo.getSyncIssues().values());
 
-        List<String> flavorDimensionList = (extension.getFlavorDimensionList() != null ?
-                extension.getFlavorDimensionList() : Lists.<String>newArrayList());
+        List<String> flavorDimensionList = (config.getFlavorDimensionList() != null ?
+                config.getFlavorDimensionList() : Lists.<String>newArrayList());
+
+        toolchains = createNativeToolchainModelMap(ndkHandler);
 
         DefaultAndroidProject androidProject = new DefaultAndroidProject(
                 Version.ANDROID_GRADLE_PLUGIN_VERSION,
@@ -151,15 +168,16 @@
                 androidBuilder.getTarget() != null ? androidBuilder.getTarget().hashString() : "",
                 bootClasspath,
                 frameworkSource,
-                cloneSigningConfigs(signingConfigs),
+                cloneSigningConfigs(config.getSigningConfigs()),
                 aaptOptions,
                 artifactMetaDataList,
                 findUnresolvedDependencies(syncIssues),
                 syncIssues,
-                extension.getCompileOptions(),
+                config.getCompileOptions(),
                 lintOptions,
                 project.getBuildDir(),
-                extension.getResourcePrefix(),
+                config.getResourcePrefix(),
+                ImmutableList.copyOf(toolchains.values()),
                 isLibrary,
                 Version.BUILDER_MODEL_API_VERSION);
 
@@ -188,6 +206,28 @@
         return androidProject;
     }
 
+    /**
+     * Create a map of ABI to NativeToolchain
+     */
+    public static Map<Abi, NativeToolchain> createNativeToolchainModelMap(
+            @NonNull NdkHandler ndkHandler) {
+        if (!ndkHandler.isNdkDirConfigured()) {
+            return ImmutableMap.of();
+        }
+
+        Map<Abi, NativeToolchain> toolchains = Maps.newHashMap();
+
+        for (Abi abi : ndkHandler.getSupportedAbis()) {
+            toolchains.put(
+                    abi,
+                    new NativeToolchainImpl(
+                            ndkHandler.getToolchain().getName() + "-" + abi.getName(),
+                            ndkHandler.getCCompiler(abi),
+                            ndkHandler.getCppCompiler(abi)));
+        }
+        return toolchains;
+    }
+
     @NonNull
     private VariantImpl createVariant(
             @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
@@ -200,8 +240,10 @@
         List<AndroidArtifact> extraAndroidArtifacts = Lists.newArrayList(
                 extraModelInfo.getExtraAndroidArtifacts(variantName));
         // Make sure all extra artifacts are serializable.
-        Collection<JavaArtifact> extraJavaArtifacts = extraModelInfo.getExtraJavaArtifacts(variantName);
-        List<JavaArtifact> clonedExtraJavaArtifacts = Lists.newArrayListWithCapacity(extraJavaArtifacts.size());
+        Collection<JavaArtifact> extraJavaArtifacts = extraModelInfo.getExtraJavaArtifacts(
+                variantName);
+        List<JavaArtifact> clonedExtraJavaArtifacts = Lists.newArrayListWithCapacity(
+                extraJavaArtifacts.size());
         for (JavaArtifact javaArtifact : extraJavaArtifacts) {
             clonedExtraJavaArtifacts.add(JavaArtifactImpl.clone(javaArtifact));
         }
@@ -265,13 +307,6 @@
         DependenciesImpl dependencies = DependenciesImpl.cloneDependencies(variantData,
                 androidBuilder);
 
-        // Add the mockable JAR path. It will be created before tests are actually run from the IDE.
-        dependencies.getJavaLibraries().add(
-                new JavaLibraryImpl(
-                        taskManager.createMockableJar.getOutputFile(),
-                        null,
-                        null));
-
         List<File> extraGeneratedSourceFolders = variantData.getExtraGeneratedSourceFolders();
         return new JavaArtifactImpl(
                 variantType.getArtifactName(),
@@ -280,18 +315,40 @@
                 Sets.newHashSet(variantData.prepareDependenciesTask.getName(),
                         taskManager.createMockableJar.getName()),
                 extraGeneratedSourceFolders != null ? extraGeneratedSourceFolders : Collections.<File>emptyList(),
-                (variantData.javaCompileTask != null) ?
-                        variantData.javaCompileTask.getDestinationDir() :
+                (variantData.javacTask != null) ?
+                        variantData.javacTask.getDestinationDir() :
                         variantData.getScope().getJavaOutputDir(),
-                variantData.processJavaResourcesTask.getDestinationDir(),
+                variantData.getScope().getJavaResourcesDestinationDir(),
+                taskManager.createMockableJar.getOutputFile(),
                 dependencies,
                 sourceProviders.variantSourceProvider,
                 sourceProviders.multiFlavorSourceProvider);
     }
 
+    /**
+     * Create a NativeLibrary for each ABI.
+     */
+    private Collection<NativeLibrary> createNativeLibraries(
+            @NonNull Collection<Abi> abis,
+            @NonNull VariantScope scope) {
+        Collection<NativeLibrary> nativeLibraries = Lists.newArrayListWithCapacity(abis.size());
+        for (Abi abi : abis) {
+            NativeToolchain toolchain = toolchains.get(abi);
+            if (toolchain == null) {
+                continue;
+            }
+            Optional<NativeLibrary> lib = nativeLibFactory.create(scope, toolchain.getName(), abi);
+            if (lib.isPresent()) {
+                nativeLibraries.add(lib.get());
+            }
+        }
+        return nativeLibraries;
+    }
+
     private AndroidArtifact createAndroidArtifact(
             @NonNull String name,
             @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
+        VariantScope scope = variantData.getScope();
         GradleVariantConfiguration variantConfiguration = variantData.getVariantConfiguration();
 
         SigningConfig signingConfig = variantConfiguration.getSigningConfig();
@@ -306,6 +363,28 @@
         List<? extends BaseVariantOutputData> variantOutputs = variantData.getOutputs();
         List<AndroidArtifactOutput> outputs = Lists.newArrayListWithCapacity(variantOutputs.size());
 
+        CoreNdkOptions ndkConfig = variantData.getVariantConfiguration().getNdkConfig();
+        Collection<NativeLibrary> nativeLibraries = ImmutableList.of();
+        if (ndkHandler.getNdkDirectory() != null) {
+            if (config.getSplits().getAbi().isEnable()) {
+                nativeLibraries = createNativeLibraries(
+                        config.getSplits().getAbi().isUniversalApk()
+                                ? ndkHandler.getSupportedAbis()
+                                : createAbiList(config.getSplits().getAbiFilters()),
+                        scope);
+            } else {
+                if (ndkConfig.getAbiFilters() == null || ndkConfig.getAbiFilters().isEmpty()) {
+                    nativeLibraries = createNativeLibraries(
+                            ndkHandler.getSupportedAbis(),
+                            scope);
+                } else {
+                    nativeLibraries = createNativeLibraries(
+                            createAbiList(ndkConfig.getAbiFilters()),
+                            scope);
+                }
+            }
+        }
+
         for (BaseVariantOutputData variantOutputData : variantOutputs) {
             int intVersionCode;
             if (variantOutputData instanceof ApkVariantOutputData) {
@@ -333,35 +412,45 @@
             // add the main APK.
             outputs.add(new AndroidArtifactOutputImpl(
                     outputFiles.build(),
-                    variantOutputData.assembleTask.getName(),
+                    "assemble" + variantOutputData.getFullName(),
                     variantOutputData.getScope().getManifestOutputFile(),
                     intVersionCode));
         }
 
-        VariantScope scope = variantData.getScope();
         return new AndroidArtifactImpl(
                 name,
                 outputs,
-                variantData.assembleVariantTask.getName(),
+                variantData.assembleVariantTask == null ? scope.getTaskName("assemble") : variantData.assembleVariantTask.getName(),
                 variantConfiguration.isSigningReady() || variantData.outputsAreSigned,
                 signingConfigName,
                 variantConfiguration.getApplicationId(),
-                scope.getSourceGenTask().getName(),
-                scope.getCompileTask().getName(),
+                // TODO: Need to determine the tasks' name when the tasks may not be created
+                // in component plugin.
+                scope.getSourceGenTask() == null ? scope.getTaskName("generate", "Sources") : scope.getSourceGenTask().getName(),
+                scope.getCompileTask() == null ? scope.getTaskName("compile", "Sources") : scope.getCompileTask().getName(),
                 getGeneratedSourceFolders(variantData),
                 getGeneratedResourceFolders(variantData),
-                (variantData.javaCompileTask != null) ?
-                        variantData.javaCompileTask.getDestinationDir() :
+                (variantData.javacTask != null) ?
+                        variantData.javacTask.getDestinationDir() :
                         scope.getJavaOutputDir(),
                 scope.getJavaResourcesDestinationDir(),
                 DependenciesImpl.cloneDependencies(variantData, androidBuilder),
                 sourceProviders.variantSourceProvider,
                 sourceProviders.multiFlavorSourceProvider,
                 variantConfiguration.getSupportedAbis(),
+                nativeLibraries,
                 variantConfiguration.getMergedBuildConfigFields(),
                 variantConfiguration.getMergedResValues());
     }
 
+    private static Collection<Abi> createAbiList(Collection<String> abiNames) {
+        ImmutableList.Builder<Abi> builder = ImmutableList.builder();
+        for (String abiName : abiNames) {
+            builder.add(Abi.getByName(abiName));
+        }
+        return builder.build();
+    }
+
     private static SourceProviders determineSourceProviders(
             @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
         SourceProvider variantSourceProvider =
@@ -381,12 +470,12 @@
     @NonNull
     private static List<String> getProductFlavorNames(
             @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
-        List<GroupableProductFlavor> productFlavors = variantData.getVariantConfiguration()
+        List<CoreProductFlavor> productFlavors = variantData.getVariantConfiguration()
                 .getProductFlavors();
 
         List<String> flavorNames = Lists.newArrayListWithCapacity(productFlavors.size());
 
-        for (DefaultProductFlavor flavor : productFlavors) {
+        for (ProductFlavor flavor : productFlavors) {
             flavorNames.add(flavor.getName());
         }
 
@@ -453,7 +542,7 @@
 
     @NonNull
     private static Collection<SigningConfig> cloneSigningConfigs(
-            @NonNull Collection<SigningConfig> signingConfigs) {
+            @NonNull Collection<? extends SigningConfig> signingConfigs) {
         Collection<SigningConfig> results = Lists.newArrayListWithCapacity(signingConfigs.size());
 
         for (SigningConfig signingConfig : signingConfigs) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryFactory.java
new file mode 100644
index 0000000..8879e6c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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.core.Abi;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.builder.model.NativeLibrary;
+import com.google.common.base.Optional;
+
+/**
+ * Factory for creating NativeLibrary.
+ */
+public interface NativeLibraryFactory {
+    @NonNull
+    Optional<NativeLibrary> create(
+            @NonNull VariantScope scope,
+            @NonNull String toolchainName,
+            @NonNull Abi abi);
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryImpl.java
new file mode 100644
index 0000000..f40ae89
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryImpl.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 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.NativeLibrary;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Implementation of NativeLibrary that is serializable.
+ */
+public class NativeLibraryImpl implements NativeLibrary, Serializable{
+    private static final long serialVersionUID = 1L;
+
+    @NonNull
+    String name;
+    @NonNull
+    String toolchainName;
+    @NonNull
+    String abi;
+    @NonNull
+    List<File> cIncludeDirs;
+    @NonNull
+    List<File> cppIncludeDirs;
+    @NonNull
+    List<File> cSystemIncludeDirs;
+    @NonNull
+    List<File> cppSystemIncludeDirs;
+    @NonNull
+    List<String> cDefines;
+    @NonNull
+    List<String> cppDefines;
+    @NonNull
+    List<String> cCompilerFlags;
+    @NonNull
+    List<String> cppCompilerFlags;
+    @NonNull
+    List<File> debuggableLibraryFolders;
+
+    public NativeLibraryImpl(
+            @NonNull String name,
+            @NonNull String toolchainName,
+            @NonNull String abi,
+            @NonNull List<File> cIncludeDirs,
+            @NonNull List<File> cppIncludeDirs,
+            @NonNull List<File> cSystemIncludeDirs,
+            @NonNull List<File> cppSystemIncludeDirs,
+            @NonNull List<String> cDefines,
+            @NonNull List<String> cppDefines,
+            @NonNull List<String> cCompilerFlags,
+            @NonNull List<String> cppCompilerFlags,
+            @NonNull List<File> debuggableLibraryFolders) {
+        this.name = name;
+        this.toolchainName = toolchainName;
+        this.abi = abi;
+        this.cIncludeDirs = cIncludeDirs;
+        this.cppIncludeDirs = cppIncludeDirs;
+        this.cSystemIncludeDirs = cSystemIncludeDirs;
+        this.cppSystemIncludeDirs = cppSystemIncludeDirs;
+        this.cDefines = cDefines;
+        this.cppDefines = cppDefines;
+        this.cCompilerFlags = cCompilerFlags;
+        this.cppCompilerFlags = cppCompilerFlags;
+        this.debuggableLibraryFolders = debuggableLibraryFolders;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @NonNull
+    @Override
+    public String getToolchainName() {
+        return toolchainName;
+    }
+
+    @NonNull
+    @Override
+    public String getAbi() {
+        return abi;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getCIncludeDirs() {
+        return cIncludeDirs;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getCppIncludeDirs() {
+        return cppIncludeDirs;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getCSystemIncludeDirs() {
+        return cSystemIncludeDirs;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getCppSystemIncludeDirs() {
+        return cppSystemIncludeDirs;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getCDefines() {
+        return cDefines;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getCppDefines() {
+        return cppDefines;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getCCompilerFlags() {
+        return cCompilerFlags;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getCppCompilerFlags() {
+        return cppCompilerFlags;
+    }
+
+    @NonNull
+    @Override
+    public List<File> getDebuggableLibraryFolders() {
+        return debuggableLibraryFolders;
+    }
+
+    @Override
+    public String toString() {
+        return "NativeLibraryImpl{" +
+                "name='" + name + '\'' +
+                ", toolchainName='" + toolchainName + '\'' +
+                ", cIncludeDirs=" + cIncludeDirs +
+                ", cppIncludeDirs=" + cppIncludeDirs +
+                ", cDefines=" + cDefines +
+                ", cppDefines=" + cppDefines +
+                ", cCompilerFlags=" + cCompilerFlags +
+                ", cppCompilerFlags=" + cppCompilerFlags +
+                ", solibSearchPaths=" + debuggableLibraryFolders +
+                '}';
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeToolchainImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeToolchainImpl.java
new file mode 100644
index 0000000..5e7be5e
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeToolchainImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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.NativeToolchain;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * Implementation of NativeToolchain that is serializable.
+ */
+public class NativeToolchainImpl implements NativeToolchain, Serializable {
+
+    @NonNull
+    String name;
+
+    @NonNull
+    File cCompilerExecutable;
+
+    @NonNull
+    File cppCompilerExecutable;
+
+    public NativeToolchainImpl(@NonNull String name, @NonNull File cCompilerExecutable,
+            @NonNull File cppCompilerExecutable) {
+        this.name = name;
+        this.cCompilerExecutable = cCompilerExecutable;
+        this.cppCompilerExecutable = cppCompilerExecutable;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @NonNull
+    @Override
+    public File getCCompilerExecutable() {
+        return cCompilerExecutable;
+    }
+
+    @NonNull
+    @Override
+    public File getCppCompilerExecutable() {
+        return cppCompilerExecutable;
+    }
+
+    @Override
+    public String toString() {
+        return "ToolchainImpl{" +
+                "name='" + name + '\'' +
+                ", cCompilerExecutable=" + cCompilerExecutable +
+                ", cppCompilerExecutable=" + cppCompilerExecutable +
+                '}';
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
index c7da888..da1648c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
@@ -22,9 +22,11 @@
 import com.android.builder.model.ProductFlavor;
 import com.android.builder.model.SigningConfig;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 
 import java.io.Serializable;
 import java.util.Collection;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -47,6 +49,7 @@
     private String mApplicationId = null;
     private String mTestApplicationId = null;
     private String mTestInstrumentationRunner = null;
+    private Map<String, String> mTestInstrumentationRunnerArguments = Maps.newHashMap();
     private Boolean mTestHandleProfiling = null;
     private Boolean mTestFunctionalTest = null;
     private Set<String> mResourceConfigurations = null;
@@ -86,6 +89,9 @@
         clonedFlavor.mResourceConfigurations = ImmutableSet.copyOf(
                 productFlavor.getResourceConfigurations());
 
+        clonedFlavor.mTestInstrumentationRunnerArguments = Maps.newHashMap(
+                productFlavor.getTestInstrumentationRunnerArguments());
+
         return clonedFlavor;
     }
 
@@ -163,6 +169,12 @@
         return mTestInstrumentationRunner;
     }
 
+    @NonNull
+    @Override
+    public Map<String, String> getTestInstrumentationRunnerArguments() {
+        return mTestInstrumentationRunnerArguments;
+    }
+
     @Nullable
     @Override
     public Boolean getTestHandleProfiling() {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.groovy
deleted file mode 100644
index 51c84b9..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.groovy
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 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.process
-import com.android.annotations.NonNull
-import com.android.ide.common.process.JavaProcessExecutor
-import com.android.ide.common.process.JavaProcessInfo
-import com.android.ide.common.process.ProcessOutput
-import com.android.ide.common.process.ProcessOutputHandler
-import com.android.ide.common.process.ProcessResult
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.process.ExecResult
-import org.gradle.process.JavaExecSpec
-
-/**
- * Implementation of JavaProcessExecutor that uses Gradle's mechanism to execute external java
- * processes.
- */
-@CompileStatic
-public class GradleJavaProcessExecutor implements JavaProcessExecutor {
-
-    @NonNull
-    private final Project project;
-
-    public GradleJavaProcessExecutor(@NonNull Project project) {
-        this.project = project
-    }
-
-    @NonNull
-    @Override
-    public ProcessResult execute(
-            @NonNull JavaProcessInfo javaProcessInfo,
-            @NonNull ProcessOutputHandler processOutputHandler) {
-        ProcessOutput output = processOutputHandler.createOutput()
-
-        ExecResult result = project.javaexec(getJavaExecClosure(javaProcessInfo, output))
-
-        processOutputHandler.handleOutput(output);
-
-        return new GradleProcessResult(result)
-    }
-
-    @NonNull
-    private static Closure getJavaExecClosure(
-            @NonNull final JavaProcessInfo javaProcessInfo,
-            @NonNull final ProcessOutput processOutput) {
-        return { JavaExecSpec javaExecSpec ->
-            javaExecSpec.classpath(new File(javaProcessInfo.getClasspath()))
-            javaExecSpec.setMain(javaProcessInfo.getMainClass())
-            javaExecSpec.args(javaProcessInfo.getArgs())
-            javaExecSpec.jvmArgs(javaProcessInfo.getJvmArgs())
-            javaExecSpec.environment(javaProcessInfo.getEnvironment())
-            javaExecSpec.setStandardOutput(processOutput.getStandardOutput())
-            javaExecSpec.setErrorOutput(processOutput.getErrorOutput())
-
-            // we want the caller to be able to do its own thing.
-            javaExecSpec.setIgnoreExitValue(true)
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.java
new file mode 100644
index 0000000..fff68d8
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 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.process;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.JavaProcessExecutor;
+import com.android.ide.common.process.JavaProcessInfo;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.ide.common.process.ProcessResult;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.process.ExecResult;
+import org.gradle.process.JavaExecSpec;
+
+import java.io.File;
+
+/**
+ * Implementation of JavaProcessExecutor that uses Gradle's mechanism to execute external java
+ * processes.
+ */
+public class GradleJavaProcessExecutor implements JavaProcessExecutor {
+
+    @NonNull
+    private final Project project;
+
+    public GradleJavaProcessExecutor(@NonNull Project project) {
+        this.project = project;
+    }
+
+    @NonNull
+    @Override
+    public ProcessResult execute(
+            @NonNull JavaProcessInfo javaProcessInfo,
+            @NonNull ProcessOutputHandler processOutputHandler) {
+        ProcessOutput output = processOutputHandler.createOutput();
+
+        ExecResult result = project.javaexec(new ExecAction(javaProcessInfo, output));
+
+        try {
+            processOutputHandler.handleOutput(output);
+        } catch (ProcessException e) {
+            return new OutputHandlerFailedGradleProcessResult(e);
+        }
+
+        return new GradleProcessResult(result);
+    }
+
+    private static class ExecAction implements Action<JavaExecSpec> {
+
+        @NonNull
+        private final JavaProcessInfo javaProcessInfo;
+
+        @NonNull
+        private final ProcessOutput processOutput;
+
+        private ExecAction(@NonNull JavaProcessInfo javaProcessInfo,
+                @NonNull ProcessOutput processOutput) {
+            this.javaProcessInfo = javaProcessInfo;
+            this.processOutput = processOutput;
+        }
+
+        @Override
+        public void execute(JavaExecSpec javaExecSpec) {
+            javaExecSpec.classpath(new File(javaProcessInfo.getClasspath()));
+            javaExecSpec.setMain(javaProcessInfo.getMainClass());
+            javaExecSpec.args(javaProcessInfo.getArgs());
+            javaExecSpec.jvmArgs(javaProcessInfo.getJvmArgs());
+            javaExecSpec.environment(javaProcessInfo.getEnvironment());
+            javaExecSpec.setStandardOutput(processOutput.getStandardOutput());
+            javaExecSpec.setErrorOutput(processOutput.getErrorOutput());
+
+            // we want the caller to be able to do its own thing.
+            javaExecSpec.setIgnoreExitValue(true);
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.groovy
deleted file mode 100644
index 0e2d2de..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.groovy
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2015 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.process
-import com.android.annotations.NonNull
-import com.android.ide.common.process.ProcessExecutor
-import com.android.ide.common.process.ProcessInfo
-import com.android.ide.common.process.ProcessOutput
-import com.android.ide.common.process.ProcessOutputHandler
-import com.android.ide.common.process.ProcessResult
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.process.ExecResult
-import org.gradle.process.ExecSpec
-
-/**
- * Implementation of ProcessExecutor that uses Gradle's mechanism to execute external processes.
- */
-@CompileStatic
-public class GradleProcessExecutor implements ProcessExecutor {
-
-    @NonNull
-    private final Project project;
-
-    public GradleProcessExecutor(@NonNull Project project) {
-        this.project = project
-    }
-
-    @NonNull
-    @Override
-    public ProcessResult execute(
-            @NonNull ProcessInfo processInfo,
-            @NonNull ProcessOutputHandler processOutputHandler) {
-        ProcessOutput output = processOutputHandler.createOutput()
-
-        ExecResult result = project.exec(getExecClosure(processInfo, output))
-
-        processOutputHandler.handleOutput(output);
-
-        return new GradleProcessResult(result)
-    }
-
-    @NonNull
-    private static Closure getExecClosure(
-            @NonNull final ProcessInfo processInfo,
-            @NonNull final ProcessOutput processOutput) {
-        return { ExecSpec execSpec ->
-
-            execSpec.setExecutable(processInfo.executable)
-            execSpec.args(processInfo.getArgs())
-            execSpec.environment(processInfo.getEnvironment())
-            execSpec.setStandardOutput(processOutput.getStandardOutput())
-            execSpec.setErrorOutput(processOutput.getErrorOutput())
-
-            // we want the caller to be able to do its own thing.
-            execSpec.setIgnoreExitValue(true)
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.java
new file mode 100644
index 0000000..57642aa
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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.process;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessExecutor;
+import com.android.ide.common.process.ProcessInfo;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.ide.common.process.ProcessResult;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+
+/**
+ * Implementation of ProcessExecutor that uses Gradle's mechanism to execute external processes.
+ */
+public class GradleProcessExecutor implements ProcessExecutor {
+
+    @NonNull
+    private final Project project;
+
+    public GradleProcessExecutor(@NonNull Project project) {
+        this.project = project;
+    }
+
+    @NonNull
+    @Override
+    public ProcessResult execute(
+            @NonNull ProcessInfo processInfo,
+            @NonNull ProcessOutputHandler processOutputHandler) {
+        ProcessOutput output = processOutputHandler.createOutput();
+
+        final ExecResult result = project.exec(new ExecAction(processInfo, output));
+
+        try {
+            processOutputHandler.handleOutput(output);
+        } catch (final ProcessException e) {
+            return new OutputHandlerFailedGradleProcessResult(e);
+        }
+
+        return new GradleProcessResult(result);
+    }
+
+    private static class ExecAction implements Action<ExecSpec> {
+
+        @NonNull
+        private final ProcessInfo processInfo;
+
+        @NonNull
+        private final ProcessOutput processOutput;
+
+        ExecAction(@NonNull final ProcessInfo processInfo,
+                @NonNull final ProcessOutput processOutput) {
+            this.processInfo = processInfo;
+            this.processOutput = processOutput;
+        }
+
+        @Override
+        public void execute(ExecSpec execSpec) {
+            execSpec.setExecutable(processInfo.getExecutable());
+            execSpec.args(processInfo.getArgs());
+            execSpec.environment(processInfo.getEnvironment());
+            execSpec.setStandardOutput(processOutput.getStandardOutput());
+            execSpec.setErrorOutput(processOutput.getErrorOutput());
+
+            // we want the caller to be able to do its own thing.
+            execSpec.setIgnoreExitValue(true);
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/OutputHandlerFailedGradleProcessResult.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/OutputHandlerFailedGradleProcessResult.java
new file mode 100644
index 0000000..575402c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/OutputHandlerFailedGradleProcessResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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.process;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessResult;
+
+public class OutputHandlerFailedGradleProcessResult implements ProcessResult {
+    @NonNull
+    private final ProcessException failure;
+
+    OutputHandlerFailedGradleProcessResult(@NonNull ProcessException failure) {
+        this.failure = failure;
+    }
+
+    @Override
+    public ProcessResult assertNormalExitValue() throws ProcessException {
+        throw failure;
+    }
+
+    @Override
+    public int getExitValue() {
+        return -1;
+    }
+
+    @Override
+    public ProcessResult rethrowFailure() throws ProcessException {
+        throw failure;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.groovy
deleted file mode 100644
index 0e86e12..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 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.profile
-
-import com.android.builder.profile.ExecutionRecord
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.Recorder
-import com.google.common.base.CaseFormat
-import org.gradle.api.Task
-import org.gradle.api.execution.TaskExecutionListener
-import org.gradle.api.tasks.TaskState
-
-import java.util.concurrent.ConcurrentHashMap
-
-/**
- * Implementation of the {@link TaskExecutionListener} that records the execution span of
- * tasks execution and records such spans using the {@link Recorder} facilities.
- */
-class RecordingBuildListener implements TaskExecutionListener {
-
-    private final class TaskRecord {
-
-        private final long recordId;
-        private final long startTime;
-
-        TaskRecord(long recordId, long startTime) {
-            this.startTime = startTime
-            this.recordId = recordId
-        }
-    }
-
-    private final Recorder mRecorder;
-
-    RecordingBuildListener(Recorder recorder) {
-        mRecorder = recorder
-    }
-
-    // map of outstanding tasks executing, keyed by their name.
-    final Map<String, TaskRecord> taskRecords = new ConcurrentHashMap<>();
-
-    @Override
-    void beforeExecute(Task task) {
-        taskRecords.put(task.getName(), new TaskRecord(
-                mRecorder.allocationRecordId(), System.currentTimeMillis()))
-    }
-
-    @Override
-    void afterExecute(Task task, TaskState taskState) {
-
-        // find the right ExecutionType.
-        String taskImpl = task.getClass().getSimpleName();
-        if (taskImpl.endsWith("_Decorated")) {
-            taskImpl = taskImpl.substring(0, taskImpl.length() - "_Decorated".length());
-        }
-        String potentialExecutionTypeName = "TASK_" +
-                CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.UPPER_UNDERSCORE).
-                        convert(taskImpl);
-        ExecutionType executionType;
-        try {
-            executionType = ExecutionType.valueOf(potentialExecutionTypeName)
-        } catch (IllegalArgumentException ignored) {
-            executionType = ExecutionType.GENERIC_TASK_EXECUTION
-        }
-
-        List< Recorder.Property> properties = new ArrayList<>();
-        properties.add(new Recorder.Property("project", task.getProject().getName()));
-        properties.add(new Recorder.Property("task", task.getName()));
-        TaskRecord taskRecord = taskRecords.get(task.getName());
-        mRecorder.closeRecord(new ExecutionRecord(
-                taskRecord.recordId,
-                0 /* parentId */,
-                taskRecord.startTime,
-                System.currentTimeMillis() - taskRecord.startTime,
-                executionType,
-                properties))
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.java
new file mode 100644
index 0000000..4195665
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.java
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright (C) 2015 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.profile;
+
+import com.android.build.gradle.internal.tasks.DefaultAndroidTask;
+import com.android.builder.profile.ExecutionRecord;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.google.common.base.CaseFormat;
+import org.gradle.api.Task;
+import org.gradle.api.execution.TaskExecutionListener;
+import org.gradle.api.tasks.TaskState;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of the {@link TaskExecutionListener} that records the execution span of
+ * tasks execution and records such spans using the {@link Recorder} facilities.
+ */
+public class RecordingBuildListener implements TaskExecutionListener {
+
+    private static final class TaskRecord {
+
+        private final long recordId;
+        private final long startTime;
+
+        TaskRecord(long recordId, long startTime) {
+            this.startTime = startTime;
+            this.recordId = recordId;
+        }
+    }
+
+    private final Recorder mRecorder;
+
+    public RecordingBuildListener(Recorder recorder) {
+        mRecorder = recorder;
+    }
+
+    // map of outstanding tasks executing, keyed by their name.
+    final Map<String, TaskRecord> taskRecords = new ConcurrentHashMap<String, TaskRecord>();
+
+    @Override
+    public void beforeExecute(Task task) {
+        taskRecords.put(task.getName(), new TaskRecord(
+                mRecorder.allocationRecordId(), System.currentTimeMillis()));
+    }
+
+    @Override
+    public void afterExecute(Task task, TaskState taskState) {
+
+        // find the right ExecutionType.
+        String taskImpl = task.getClass().getSimpleName();
+        if (taskImpl.endsWith("_Decorated")) {
+            taskImpl = taskImpl.substring(0, taskImpl.length() - "_Decorated".length());
+        }
+        String potentialExecutionTypeName = "TASK_" +
+                CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.UPPER_UNDERSCORE).
+                        convert(taskImpl);
+        ExecutionType executionType;
+        try {
+            executionType = ExecutionType.valueOf(potentialExecutionTypeName);
+        } catch (IllegalArgumentException ignored) {
+            executionType = ExecutionType.GENERIC_TASK_EXECUTION;
+        }
+
+        List<Recorder.Property> properties = new ArrayList<Recorder.Property>();
+        properties.add(new Recorder.Property("project", task.getProject().getName()));
+        properties.add(new Recorder.Property("task", task.getName()));
+
+        if (task instanceof DefaultAndroidTask) {
+            String variantName = ((DefaultAndroidTask) task).getVariantName();
+            if (variantName == null) {
+                throw new IllegalStateException("Task with type " + task.getClass().getName() +
+                        " does not include a variantName");
+            }
+            if (!variantName.isEmpty()) {
+                properties.add(new Recorder.Property("variant", variantName));
+            }
+        }
+
+        TaskRecord taskRecord = taskRecords.get(task.getName());
+        mRecorder.closeRecord(new ExecutionRecord(
+                taskRecord.recordId,
+                0 /* parentId */,
+                taskRecord.startTime,
+                System.currentTimeMillis() - taskRecord.startTime,
+                executionType,
+                properties));
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.groovy
deleted file mode 100644
index 497b12f..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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.profile
-
-import com.android.annotations.NonNull
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.Recorder
-import com.android.builder.profile.ThreadRecorder
-import org.gradle.api.Project
-
-/**
- * Groovy language helper to record execution spans.
- */
-class SpanRecorders {
-
-    public final static String PROJECT = "project";
-    public final static String VARIANT = "variant";
-
-    static <T> T record(@NonNull ExecutionType executionType, @NonNull Closure<T> closure) {
-        // have to explicitly cast as groovy does not support inner classes with generics...
-        return (T) ThreadRecorder.get().record(executionType, new Recorder.Block() {
-
-            @Override
-            Object call() throws Exception {
-                return closure.call()
-            }
-        })
-    }
-
-    static <T> T record(@NonNull Project project,
-            @NonNull ExecutionType executionType,
-            @NonNull Closure<T> closure) {
-        // have to explicitly cast as groovy does not support inner classes with generics...
-       return (T) ThreadRecorder.get().record(executionType, new Recorder.Block() {
-
-            @Override
-            Object call() throws Exception {
-                return closure.call()
-            }
-        }, new Recorder.Property(PROJECT, project.getName()))
-    }
-
-    /**
-     * Records an execution span, using a Java {@link Recorder.Block}
-     */
-    static <T> T record(@NonNull Project project,
-            @NonNull ExecutionType executionType,
-            @NonNull Recorder.Block<T> block,
-            Recorder.Property... properties) {
-        List<Recorder.Property> mergedProperties = new ArrayList<>(properties.length + 1);
-        mergedProperties.addAll(properties);
-        mergedProperties.add(new Recorder.Property(PROJECT, project.getName()))
-        return (T) ThreadRecorder.get().record(
-                executionType, block,
-                mergedProperties.toArray(new Recorder.Property[mergedProperties.size()]))
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.java
new file mode 100644
index 0000000..a319ede
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.profile;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.concurrency.Immutable;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.Project;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Helper to record execution spans.
+ */
+public class SpanRecorders {
+
+    public static final String PROJECT = "project";
+    public static final String VARIANT = "variant";
+
+
+    /**
+     * Records an execution span, using a Java {@link Recorder.Block}
+     */
+    public static <T> T record(@NonNull Project project,
+            @NonNull ExecutionType executionType,
+            @NonNull Recorder.Block<T> block,
+            Recorder.Property... properties) {
+        Recorder.Property[] mergedProperties = new Recorder.Property[properties.length + 1];
+        mergedProperties[0] = new Recorder.Property(PROJECT, project.getName());
+        System.arraycopy(properties, 0, mergedProperties, 1, properties.length);
+        return (T) ThreadRecorder.get().record(executionType, block, mergedProperties);
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java
index 008de57..db175a6 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java
@@ -28,7 +28,7 @@
 
 /**
  * Handle for a {@link Task} that may not yet been created.
- * For tasks created using CollectionBuilder<Task>, they are usually not actually created until
+ * For tasks created using ModelMap<Task>, they are usually not actually created until
  * Gradle decides those tasks needs to be executed.  This class contains information about those
  * tasks and allow dependencies to be specified.
  */
@@ -37,8 +37,6 @@
     private String name;
     @NonNull
     private final Class<T> taskType;
-    @Nullable
-    private T task;
     @NonNull
     private final List<AndroidTask<? extends Task>> upstreamTasks;
     @NonNull
@@ -154,11 +152,22 @@
     }
 
     /**
-     * Add a configration action for this task.
+     * Add a configuration action for this task.
      * @param taskFactory TaskFactory used to configure the task.
      * @param configAction An Action to be executed.
      */
     public void configure(TaskFactory taskFactory, Action<? super Task> configAction) {
         taskFactory.named(name, configAction);
     }
+
+    /**
+     * Potentially instantiates and return the task. Should only be called once the task is
+     * configured.
+     * @param taskFactory the factory for tasks
+     * @return the task instance.
+     */
+    @SuppressWarnings("unchecked")
+    public T get(TaskFactory taskFactory) {
+        return (T) taskFactory.named(name);
+    }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java
index d0b001a..9e5ac12 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java
@@ -16,16 +16,17 @@
 
 package com.android.build.gradle.internal.scope;
 
+import static com.android.builder.core.BuilderConstants.FD_REPORTS;
 import static com.android.builder.model.AndroidProject.FD_GENERATED;
 import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
 import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
-import static com.android.builder.model.AndroidProject.PROPERTY_APK_LOCATION;
 
 import com.android.annotations.NonNull;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.AndroidGradleOptions;
 import com.android.build.gradle.internal.SdkHandler;
 import com.android.builder.core.AndroidBuilder;
-import com.android.builder.model.AndroidProject;
+import com.google.common.base.Objects;
 
 import org.gradle.api.Project;
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
@@ -43,7 +44,7 @@
     @NonNull
     private String projectBaseName;
     @NonNull
-    private BaseExtension extension;
+    private AndroidConfig extension;
     @NonNull
     private SdkHandler sdkHandler;
     @NonNull
@@ -53,12 +54,16 @@
     private final File intermediatesDir;
     @NonNull
     private final File generatedDir;
+    @NonNull
+    private final File reportsDir;
+    @NonNull
+    private final File outputsDir;
 
     public GlobalScope(
             @NonNull Project project,
             @NonNull AndroidBuilder androidBuilder,
             @NonNull String projectBaseName,
-            @NonNull BaseExtension extension,
+            @NonNull AndroidConfig extension,
             @NonNull SdkHandler sdkHandler,
             @NonNull ToolingModelBuilderRegistry toolingRegistry) {
         this.project = project;
@@ -69,6 +74,8 @@
         this.toolingRegistry = toolingRegistry;
         intermediatesDir = new File(getBuildDir(), FD_INTERMEDIATES);
         generatedDir = new File(getBuildDir(), FD_GENERATED);
+        reportsDir = new File(getBuildDir(), FD_REPORTS);
+        outputsDir = new File(getBuildDir(), FD_OUTPUTS);
     }
 
     @NonNull
@@ -77,7 +84,7 @@
     }
 
     @NonNull
-    public BaseExtension getExtension() {
+    public AndroidConfig getExtension() {
         return extension;
     }
 
@@ -117,16 +124,24 @@
     }
 
     @NonNull
+    public File getReportsDir() {
+        return reportsDir;
+    }
+
+    @NonNull
+    public File getOutputsDir() {
+        return outputsDir;
+    }
+
+    @NonNull
     public String getDefaultApkLocation() {
         return getBuildDir() + "/" + FD_OUTPUTS + "/apk";
     }
 
     @NonNull
     public String getApkLocation() {
-        String apkLocation = getDefaultApkLocation();
-        if (project.hasProperty(PROPERTY_APK_LOCATION)) {
-            apkLocation = (String) project.getProperties().get(PROPERTY_APK_LOCATION);
-        }
-        return apkLocation;
+        return Objects.firstNonNull(
+                AndroidGradleOptions.getApkLocation(project),
+                getDefaultApkLocation());
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java
index 8534920..dd897be 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java
@@ -16,30 +16,17 @@
 
 package com.android.build.gradle.internal.scope;
 
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
-
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.TaskFactory;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.variant.ApkVariantData;
 import com.android.build.gradle.internal.variant.BaseVariantOutputData;
 import com.android.build.gradle.tasks.CompatibleScreensManifest;
 import com.android.build.gradle.tasks.ManifestProcessorTask;
-import com.android.build.gradle.tasks.PackageApplication;
-import com.android.build.gradle.tasks.PackageSplitAbi;
-import com.android.build.gradle.tasks.PackageSplitRes;
 import com.android.build.gradle.tasks.ProcessAndroidResources;
-import com.android.build.gradle.tasks.ZipAlign;
-import com.android.builder.model.AndroidProject;
 import com.android.utils.StringHelper;
-import com.android.xml.AndroidManifest;
-import com.google.common.collect.ImmutableMap;
-
-import org.gradle.api.Task;
 
 import java.io.File;
-import java.util.Map;
 
 /**
  * A scope containing data for a specific variant.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java
index 05d00df..5bcc4f8 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java
@@ -16,15 +16,18 @@
 
 package com.android.build.gradle.internal.scope;
 
+import static com.android.build.gradle.internal.TaskManager.DIR_BUNDLES;
 import static com.android.builder.model.AndroidProject.FD_GENERATED;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.core.Abi;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.internal.coverage.JacocoInstrumentTask;
 import com.android.build.gradle.internal.tasks.CheckManifest;
 import com.android.build.gradle.internal.tasks.FileSupplier;
+import com.android.build.gradle.internal.tasks.MergeJavaResourcesTask;
 import com.android.build.gradle.internal.tasks.PrepareDependenciesTask;
 import com.android.build.gradle.internal.variant.ApkVariantData;
 import com.android.build.gradle.internal.variant.BaseVariantData;
@@ -36,22 +39,27 @@
 import com.android.build.gradle.tasks.Dex;
 import com.android.build.gradle.tasks.GenerateBuildConfig;
 import com.android.build.gradle.tasks.GenerateResValues;
+import com.android.build.gradle.tasks.JackTask;
+import com.android.build.gradle.tasks.JavaResourcesProvider;
 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.PreprocessResourcesTask;
 import com.android.build.gradle.tasks.ProcessAndroidResources;
 import com.android.build.gradle.tasks.RenderscriptCompile;
 import com.android.builder.core.VariantConfiguration;
 import com.android.builder.core.VariantType;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.utils.FileUtils;
 import com.android.utils.StringHelper;
+import com.google.common.base.Objects;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
 import org.gradle.api.Task;
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
 import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
 import java.util.Collection;
@@ -71,7 +79,11 @@
     @Nullable
     private Collection<Object> ndkBuildable;
     @Nullable
-    private Collection<File> ndkOutputDirectories;
+    private Collection<File> ndkSoFolder;
+    @Nullable
+    private File ndkObjFolder;
+    @NonNull
+    private Map<Abi, File> ndkDebuggableLibraryFolders = Maps.newHashMap();
 
     @Nullable
     private File mergeResourceOutputDir;
@@ -95,23 +107,24 @@
     private AndroidTask<GenerateBuildConfig> generateBuildConfigTask;
     private AndroidTask<GenerateResValues> generateResValuesTask;
 
-    /**
-     * Anchor task for post-processing the merged resources to backport some features to earlier
-     * API versions, e.g. generate PNGs from vector drawables (vector drawables were added in 21).
-     */
     @Nullable
     private AndroidTask<Dex> dexTask;
     @Nullable
     private AndroidTask jacocoIntrumentTask;
-    @Nullable
-    private AndroidTask<PreprocessResourcesTask> preprocessResourcesTask;
 
-    private AndroidTask<Copy> processJavaResourcesTask;
+    private AndroidTask<Sync> processJavaResourcesTask;
+    private AndroidTask<MergeJavaResourcesTask> mergeJavaResourcesTask;
+    private JavaResourcesProvider javaResourcesProvider;
     private AndroidTask<NdkCompile> ndkCompileTask;
 
-    // can be JavaCompile or JackTask depending on user's settings.
+    /** @see BaseVariantData#javaCompilerTask */
     @Nullable
-    private AndroidTask<? extends AbstractCompile> javaCompileTask;
+    private AndroidTask<? extends AbstractCompile> javaCompilerTask;
+    @Nullable
+    private AndroidTask<JavaCompile> javacTask;
+    @Nullable
+    private AndroidTask<JackTask> jackTask;
+
     // empty anchor compile task to set all compilations tasks as dependents.
     private AndroidTask<Task> compileTask;
     private AndroidTask<JacocoInstrumentTask> jacocoInstrumentTask;
@@ -122,6 +135,8 @@
     // TODO : why is Jack not registered as the obfuscationTask ???
     private AndroidTask<? extends Task> obfuscationTask;
 
+    private File resourceOutputDir;
+
 
     public VariantScope(
             @NonNull GlobalScope globalScope,
@@ -165,23 +180,44 @@
     }
 
     @Nullable
-    public Collection<File> getNdkOutputDirectories() {
-        return ndkOutputDirectories;
+    public Collection<File> getNdkSoFolder() {
+        return ndkSoFolder;
     }
 
-    public void setNdkOutputDirectories(@NonNull Collection<File> ndkOutputDirectories) {
-        this.ndkOutputDirectories = ndkOutputDirectories;
+    public void setNdkSoFolder(@NonNull Collection<File> ndkSoFolder) {
+        this.ndkSoFolder = ndkSoFolder;
+    }
+
+    @Nullable
+    public File getNdkObjFolder() {
+        return ndkObjFolder;
+    }
+
+    public void setNdkObjFolder(@NonNull File ndkObjFolder) {
+        this.ndkObjFolder = ndkObjFolder;
+    }
+
+    /**
+     * Return the folder containing the shared object with debugging symbol for the specified ABI.
+     */
+    @Nullable
+    public File getNdkDebuggableLibraryFolders(@NonNull Abi abi) {
+        return ndkDebuggableLibraryFolders.get(abi);
+    }
+
+    public void addNdkDebuggableLibraryFolders(@NonNull Abi abi, @NonNull File searchPath) {
+        this.ndkDebuggableLibraryFolders.put(abi, searchPath);
     }
 
     @NonNull
     public Set<File> getJniFolders() {
-        assert getNdkOutputDirectories() != null;
+        assert getNdkSoFolder() != null;
 
         VariantConfiguration config = getVariantConfiguration();
         ApkVariantData apkVariantData = (ApkVariantData) variantData;
         // for now only the project's compilation output.
         Set<File> set = Sets.newHashSet();
-        set.addAll(getNdkOutputDirectories());
+        set.addAll(getNdkSoFolder());
         set.add(getRenderscriptLibOutputDir());
         set.addAll(config.getLibraryJniFolders());
         set.addAll(config.getJniLibsList());
@@ -240,7 +276,7 @@
     public File getProguardOutputFile() {
         return (variantData instanceof LibraryVariantData) ?
                 new File(globalScope.getIntermediatesDir(),
-                        TaskManager.DIR_BUNDLES + "/" + getVariantConfiguration().getDirName()
+                        DIR_BUNDLES + "/" + getVariantConfiguration().getDirName()
                                 + "/classes.jar") :
                 new File(globalScope.getIntermediatesDir(),
                         "/classes-proguard/" + getVariantConfiguration().getDirName()
@@ -290,6 +326,15 @@
     }
 
     @NonNull
+    public File getFinalResourcesDir() {
+        return Objects.firstNonNull(resourceOutputDir, getDefaultMergeResourcesOutputDir());
+    }
+
+    public void setResourceOutputDir(@NonNull File resourceOutputDir) {
+        this.resourceOutputDir = resourceOutputDir;
+    }
+
+    @NonNull
     public File getDefaultMergeResourcesOutputDir() {
         return new File(globalScope.getIntermediatesDir(),
                 "/res/merged/" + getVariantConfiguration().getDirName());
@@ -311,7 +356,7 @@
     public File getMergeAssetsOutputDir() {
         return getVariantConfiguration().getType() == VariantType.LIBRARY ?
                 new File(globalScope.getIntermediatesDir(),
-                        TaskManager.DIR_BUNDLES + "/" + getVariantConfiguration().getDirName() +
+                        DIR_BUNDLES + "/" + getVariantConfiguration().getDirName() +
                                 "/assets") :
                 new File(globalScope.getIntermediatesDir(),
                         "/assets/" + getVariantConfiguration().getDirName());
@@ -324,15 +369,39 @@
     }
 
     @NonNull
+    public File getGeneratedResourcesDir(String name) {
+        return FileUtils.join(
+                globalScope.getGeneratedDir(),
+                "res",
+                name,
+                getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
     public File getGeneratedResOutputDir() {
-        return new File(globalScope.getGeneratedDir(),
-                "res/resValues/" + getVariantConfiguration().getDirName());
+        return getGeneratedResourcesDir("resValues");
+    }
+
+    @NonNull
+    public File getGeneratedPngsOutputDir() {
+        return getGeneratedResourcesDir("pngs");
     }
 
     @NonNull
     public File getRenderscriptResOutputDir() {
-        return new File(globalScope.getGeneratedDir(),
-                "res/rs/" + getVariantConfiguration().getDirName());
+        return getGeneratedResourcesDir("rs");
+    }
+
+    @NonNull
+    public File getPackagedJarsJavaResDestinationDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "packagedJarsJavaResources/" + getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
+    public File getSourceFoldersJavaResDestinationDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "sourceFolderJavaResources/" + getVariantConfiguration().getDirName());
     }
 
     @NonNull
@@ -353,6 +422,76 @@
                 "source/aidl/" + getVariantConfiguration().getDirName());
     }
 
+    @NonNull
+    public File getAidlIncrementalDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "incremental/aidl/" + getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
+    public File getAidlParcelableDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                DIR_BUNDLES + "/" + getVariantConfiguration().getDirName() + "/aidl");
+    }
+
+    /**
+     * Returns the location of an intermediate directory that can be used by the Jack toolchain
+     * to store states necessary to support incremental compilation.
+     * @return a variant specific directory.
+     */
+    @NonNull
+    public File getJackIncrementalDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "incremental/jack/" + getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
+    public File getJackTempDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "tmp/jack/" + getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
+    public File getJillPackagedLibrariesDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "jill/" + getVariantConfiguration().getDirName() + "/packaged");
+    }
+
+    @NonNull
+    public File getJillRuntimeLibrariesDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "jill/" + getVariantConfiguration().getDirName() + "/runtime");
+    }
+
+    @NonNull
+    public File getJackDestinationDir() {
+        return new File(globalScope.getIntermediatesDir(),
+                "dex/" + getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
+    public File getJackClassesZip() {
+        return new File(globalScope.getIntermediatesDir(),
+                "packaged/" + getVariantConfiguration().getDirName() + "/classes.zip");
+    }
+
+    @NonNull
+    public File getProguardOutputFolder() {
+        return new File(globalScope.getBuildDir(), "/" + FD_OUTPUTS + "/mapping/" +
+                getVariantConfiguration().getDirName());
+    }
+
+    @NonNull
+    public File getProcessAndroidResourcesProguardOutputFile() {
+        return new File(globalScope.getIntermediatesDir(),
+                "/proguard-rules/" + getVariantConfiguration().getDirName() + "/aapt_rules.txt");
+    }
+
+    public File getMappingFile() {
+        return new File(globalScope.getOutputsDir(),
+                "/mapping/" + getVariantConfiguration().getDirName() + "/mapping.txt");
+    }
+
     // Tasks getters/setters.
 
     public AndroidTask<Task> getPreBuildTask() {
@@ -483,33 +622,85 @@
         this.dexTask = dexTask;
     }
 
-    @Nullable
-    public AndroidTask<PreprocessResourcesTask> getPreprocessResourcesTask() {
-        return preprocessResourcesTask;
-    }
-
-    public void setPreprocessResourcesTask(
-            @Nullable AndroidTask<PreprocessResourcesTask> preprocessResourcesTask) {
-        this.preprocessResourcesTask = preprocessResourcesTask;
-    }
-
-    public AndroidTask<Copy> getProcessJavaResourcesTask() {
+    public AndroidTask<Sync> getProcessJavaResourcesTask() {
         return processJavaResourcesTask;
     }
 
     public void setProcessJavaResourcesTask(
-            AndroidTask<Copy> processJavaResourcesTask) {
+            AndroidTask<Sync> processJavaResourcesTask) {
         this.processJavaResourcesTask = processJavaResourcesTask;
     }
 
-    @Nullable
-    public AndroidTask<? extends AbstractCompile> getJavaCompileTask() {
-        return javaCompileTask;
+    SignedJarBuilder.IZipEntryFilter packagingOptionsFilter;
+
+    public void setPackagingOptionsFilter(SignedJarBuilder.IZipEntryFilter filter) {
+        this.packagingOptionsFilter = filter;
     }
 
-    public void setJavaCompileTask(
-            @Nullable AndroidTask<? extends AbstractCompile> javaCompileTask) {
-        this.javaCompileTask = javaCompileTask;
+    /**
+     * Returns the {@link SignedJarBuilder.IZipEntryFilter} instance
+     * that manages all resources inclusion in the final APK following the rules defined in
+     * {@link com.android.builder.model.PackagingOptions} settings.
+     */
+    public SignedJarBuilder.IZipEntryFilter getPackagingOptionsFilter() {
+        return packagingOptionsFilter;
+    }
+
+    public void setMergeJavaResourcesTask(AndroidTask<MergeJavaResourcesTask> mergeJavaResourcesTask) {
+        this.mergeJavaResourcesTask = mergeJavaResourcesTask;
+    }
+
+    /**
+     * Returns the task extracting java resources from libraries and merging those with java
+     * resources coming from the variant's source folders.
+     * @return the task merging resources.
+     */
+    public AndroidTask<MergeJavaResourcesTask> getMergeJavaResourcesTask() {
+        return mergeJavaResourcesTask;
+    }
+
+    public void setJavaResourcesProvider(JavaResourcesProvider javaResourcesProvider) {
+        this.javaResourcesProvider = javaResourcesProvider;
+    }
+
+    /**
+     * Returns the {@link JavaResourcesProvider} responsible for providing final merged and possibly
+     * obfuscated java resources for inclusion in the final APK. The provider might change during
+     * the variant build process.
+     * @return the java resources provider.
+     */
+    public JavaResourcesProvider getJavaResourcesProvider() {
+        return javaResourcesProvider;
+    }
+
+    @Nullable
+    public AndroidTask<? extends AbstractCompile> getJavaCompilerTask() {
+        return javaCompilerTask;
+    }
+
+    @Nullable
+    public AndroidTask<JackTask> getJackTask() {
+        return jackTask;
+    }
+
+    public void setJackTask(
+            @Nullable AndroidTask<JackTask> jackTask) {
+        this.jackTask = jackTask;
+    }
+
+    @Nullable
+    public AndroidTask<JavaCompile> getJavacTask() {
+        return javacTask;
+    }
+
+    public void setJavacTask(
+            @Nullable AndroidTask<JavaCompile> javacTask) {
+        this.javacTask = javacTask;
+    }
+
+    public void setJavaCompilerTask(
+            @NonNull AndroidTask<? extends AbstractCompile> javaCompileTask) {
+        this.javaCompilerTask = javaCompileTask;
     }
 
     public AndroidTask<Task> getCompileTask() {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java
index 718561e..38bb68c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java
@@ -21,6 +21,7 @@
 import com.android.annotations.NonNull;
 import com.android.build.gradle.internal.test.report.ReportType;
 import com.android.build.gradle.internal.test.report.TestReport;
+import com.android.utils.FileUtils;
 import com.google.common.collect.Lists;
 
 import org.gradle.api.GradleException;
@@ -126,8 +127,8 @@
         File reportOutDir = getReportsDir();
 
         // empty the folders
-        emptyFolder(resultsOutDir);
-        emptyFolder(reportOutDir);
+        FileUtils.emptyFolder(resultsOutDir);
+        FileUtils.emptyFolder(reportOutDir);
 
         // do the copy.
         copyResults(resultsOutDir);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java
index 494753f..b64ddd4 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java
@@ -18,9 +18,11 @@
 import com.android.annotations.Nullable;
 import com.android.build.gradle.internal.LoggerWrapper;
 import com.android.builder.core.AndroidBuilder;
+import com.android.builder.sdk.TargetInfo;
 import com.android.sdklib.BuildToolInfo;
 import com.android.utils.FileUtils;
 import com.android.utils.ILogger;
+import com.google.common.base.Preconditions;
 
 import java.io.File;
 
@@ -32,8 +34,14 @@
     @Nullable
     private ILogger iLogger;
 
-    @Nullable
+    /**
+     * Returns the androidBuilder.
+     * @throws IllegalStateException if androidBuilder has not been set,
+     */
+    @NonNull
     protected AndroidBuilder getBuilder() {
+        Preconditions.checkState(androidBuilder != null,
+                "androidBuilder required for task '%s'.", getName());
         return androidBuilder;
     }
 
@@ -45,13 +53,16 @@
         return iLogger;
     }
 
-    protected void emptyFolder(File folder) {
-        getLogger().info("deleteDir(" + folder + ") returned: " + FileUtils.deleteFolder(folder));
-        folder.mkdirs();
-    }
-
+    /**
+     * Returns the BuildToolInfo.
+     * @throws IllegalStateException if androidBuilder.targetInfo has not been set,
+     */
+    @NonNull
     protected BuildToolInfo getBuildTools() {
-        return androidBuilder.getTargetInfo().getBuildTools();
+        TargetInfo targetInfo = getBuilder().getTargetInfo();
+        Preconditions.checkState(targetInfo != null,
+                "androidBuilder.targetInfo required for task '%s'.", getName());
+        return targetInfo.getBuildTools();
     }
 
     public void setAndroidBuilder(@NonNull AndroidBuilder androidBuilder) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java
index e08a73b..49c9953 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java
@@ -16,12 +16,26 @@
 
 package com.android.build.gradle.internal.tasks;
 
+import com.android.annotations.Nullable;
 import com.android.builder.Version;
 
 import org.gradle.api.DefaultTask;
 import org.gradle.api.tasks.Input;
 
 public class DefaultAndroidTask extends DefaultTask {
+
+    @Nullable
+    private String variantName;
+
+    @Nullable
+    public String getVariantName() {
+        return variantName;
+    }
+
+    public void setVariantName(@Nullable String variantName) {
+        this.variantName = variantName;
+    }
+
     /**
      * Force tasks to be re-run if the Android plugin version changes.
      * @return the plugin version, of the form "x.y.z"
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java
index 68fec17..efcf048 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java
@@ -20,6 +20,7 @@
 import com.android.build.gradle.internal.AndroidAsciiReportRenderer;
 import com.android.build.gradle.internal.variant.BaseVariantData;
 
+import org.gradle.api.DefaultTask;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.logging.StyledTextOutputFactory;
 
@@ -31,7 +32,7 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-public class DependencyReportTask extends DefaultAndroidTask {
+public class DependencyReportTask extends DefaultTask {
 
     private AndroidAsciiReportRenderer renderer = new AndroidAsciiReportRenderer();
 
@@ -70,7 +71,7 @@
      *
      * @param variants the variants. Must not be null.
      */
-    public void setVariants(@NonNull Collection<BaseVariantData> variants) {
+    public void setVariants(@NonNull Collection<? extends BaseVariantData> variants) {
         this.variants.addAll(variants);
     }
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestLibraryTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestLibraryTask.groovy
deleted file mode 100644
index 44498c4..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestLibraryTask.groovy
+++ /dev/null
@@ -1,24 +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.tasks
-
-/**
- * class to test library project. Exactly the same as DeviceProviderInstrumentTestTask but
- * is needed to be gathered by the reporting plugin.
- */
-class DeviceProviderInstrumentTestLibraryTask extends DeviceProviderInstrumentTestTask {
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.groovy
deleted file mode 100644
index a7bab3a..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.groovy
+++ /dev/null
@@ -1,131 +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.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.test.report.ReportType
-import com.android.build.gradle.internal.test.report.TestReport
-import com.android.builder.internal.testing.SimpleTestCallable
-import com.android.builder.testing.SimpleTestRunner
-import com.android.builder.testing.TestData
-import com.android.builder.testing.TestRunner
-import com.android.builder.testing.api.DeviceProvider
-import com.android.ide.common.process.ProcessExecutor
-import com.google.common.collect.ImmutableList
-import org.gradle.api.GradleException
-import org.gradle.api.Nullable
-import org.gradle.api.tasks.TaskAction
-import org.gradle.logging.ConsoleRenderer
-/**
- * Run instrumentation tests for a given variant
- */
-public class DeviceProviderInstrumentTestTask extends BaseTask implements AndroidTestTask {
-
-    File reportsDir
-    File resultsDir
-    File coverageDir
-
-    String flavorName
-
-    @Nullable
-    Collection<String> installOptions;
-
-    DeviceProvider deviceProvider
-    TestData testData;
-
-    File adbExec;
-    @Nullable
-    File splitSelectExec;
-    ProcessExecutor processExecutor;
-
-    boolean ignoreFailures
-    boolean testFailed
-
-    @TaskAction
-    protected void runTests() {
-
-        File resultsOutDir = getResultsDir()
-        emptyFolder(resultsOutDir)
-
-        File coverageOutDir = getCoverageDir()
-        emptyFolder(coverageOutDir)
-
-        boolean success = false;
-        // If there are tests to run, and the test runner returns with no results, we fail (since
-        // this is most likely a problem with the device setup). If no, the task will succeed.
-        if (!testsFound()) {
-            logger.info("No tests found, nothing to do.")
-            // If we don't create the coverage file, createXxxCoverageReport task will fail.
-            File emptyCoverageFile = new File(coverageOutDir, SimpleTestCallable.FILE_COVERAGE_EC)
-            emptyCoverageFile.createNewFile()
-            success = true;
-        } else {
-            File testApk = testData.getTestApk();
-            String flavor = getFlavorName()
-            TestRunner testRunner = new SimpleTestRunner(getAdbExec(), getSplitSelectExec(),
-                    getProcessExecutor());
-            deviceProvider.init();
-
-            Collection<String> extraArgs = installOptions == null || installOptions.isEmpty() ?
-                    ImmutableList.of() : installOptions;
-            try {
-                success = testRunner.runTests(project.name, flavor,
-                        testApk, testData,
-                        deviceProvider.devices,
-                        deviceProvider.getMaxThreads(),
-                        deviceProvider.getTimeoutInMs(),
-                        extraArgs,
-                        resultsOutDir, coverageOutDir, getILogger());
-            } finally {
-                deviceProvider.terminate();
-            }
-
-        }
-
-        // run the report from the results.
-        File reportOutDir = getReportsDir()
-        emptyFolder(reportOutDir)
-
-        TestReport report = new TestReport(ReportType.SINGLE_FLAVOR, resultsOutDir, reportOutDir)
-        report.generateReport()
-
-        if (!success) {
-            testFailed = true
-            String reportUrl = new ConsoleRenderer().asClickableFileUrl(
-                    new File(reportOutDir, "index.html"));
-            String message = "There were failing tests. See the report at: " + reportUrl;
-            if (getIgnoreFailures()) {
-                logger.warn(message)
-                return
-            } else {
-                throw new GradleException(message)
-            }
-        }
-
-        testFailed = false
-    }
-
-    /**
-     * Determines if there are any tests to run.
-     *
-     * @return true if there are some tests to run, false otherwise
-     */
-    private boolean testsFound() {
-        // For now we check if there are any test sources. We could inspect the test classes and
-        // apply JUnit logic to see if there's something to run, but that would not catch the case
-        // where user makes a typo in a test name or forgets to inherit from a JUnit class
-        !project.files(testData.getTestDirectories()).asFileTree.empty
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.java
new file mode 100644
index 0000000..fcb4291
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.java
@@ -0,0 +1,376 @@
+/*
+ * 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.internal.tasks;
+
+import static com.android.builder.core.BuilderConstants.CONNECTED;
+import static com.android.builder.core.BuilderConstants.DEVICE;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS;
+import static com.android.builder.core.BuilderConstants.FD_FLAVORS;
+import static com.android.builder.core.BuilderConstants.FD_REPORTS;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
+import static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT;
+
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.test.report.ReportType;
+import com.android.build.gradle.internal.test.report.TestReport;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.builder.internal.testing.SimpleTestCallable;
+import com.android.builder.sdk.SdkInfo;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.testing.ConnectedDeviceProvider;
+import com.android.builder.testing.SimpleTestRunner;
+import com.android.builder.testing.TestData;
+import com.android.builder.testing.TestRunner;
+import com.android.builder.testing.api.DeviceException;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestException;
+import com.android.ide.common.process.ProcessExecutor;
+import com.android.utils.FileUtils;
+import com.android.utils.StringHelper;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Nullable;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.logging.ConsoleRenderer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+/**
+ * Run instrumentation tests for a given variant
+ */
+public class DeviceProviderInstrumentTestTask extends BaseTask implements AndroidTestTask {
+
+    private File reportsDir;
+    private File resultsDir;
+    private File coverageDir;
+
+    private String flavorName;
+
+    @Nullable
+    private Collection<String> installOptions;
+
+    private DeviceProvider deviceProvider;
+    private TestData testData;
+
+    private File adbExec;
+    @Nullable
+    private File splitSelectExec;
+    private ProcessExecutor processExecutor;
+
+    private boolean ignoreFailures;
+    private boolean testFailed;
+
+    @TaskAction
+    protected void runTests() throws DeviceException, IOException, InterruptedException,
+            TestRunner.NoAuthorizedDeviceFoundException, TestException {
+
+        File resultsOutDir = getResultsDir();
+        FileUtils.emptyFolder(resultsOutDir);
+
+        File coverageOutDir = getCoverageDir();
+        FileUtils.emptyFolder(coverageOutDir);
+
+        boolean success = false;
+        // If there are tests to run, and the test runner returns with no results, we fail (since
+        // this is most likely a problem with the device setup). If no, the task will succeed.
+        if (!testsFound()) {
+            getLogger().info("No tests found, nothing to do.");
+            // If we don't create the coverage file, createXxxCoverageReport task will fail.
+            File emptyCoverageFile = new File(coverageOutDir, SimpleTestCallable.FILE_COVERAGE_EC);
+            emptyCoverageFile.createNewFile();
+            success = true;
+        } else {
+            File testApk = testData.getTestApk();
+            String flavor = getFlavorName();
+            TestRunner testRunner = new SimpleTestRunner(
+                    getSplitSelectExec(),
+                    getProcessExecutor());
+            deviceProvider.init();
+
+            Collection<String> extraArgs = installOptions == null || installOptions.isEmpty()
+                    ? ImmutableList.<String>of() : installOptions;
+            try {
+                success = testRunner.runTests(getProject().getName(), flavor,
+                        testApk,
+                        testData,
+                        deviceProvider.getDevices(),
+                        deviceProvider.getMaxThreads(),
+                        deviceProvider.getTimeoutInMs(),
+                        extraArgs,
+                        resultsOutDir,
+                        coverageOutDir,
+                        getILogger());
+            } finally {
+                deviceProvider.terminate();
+            }
+
+        }
+
+        // run the report from the results.
+        File reportOutDir = getReportsDir();
+        FileUtils.emptyFolder(reportOutDir);
+
+        TestReport report = new TestReport(ReportType.SINGLE_FLAVOR, resultsOutDir, reportOutDir);
+        report.generateReport();
+
+        if (!success) {
+            testFailed = true;
+            String reportUrl = new ConsoleRenderer().asClickableFileUrl(
+                    new File(reportOutDir, "index.html"));
+            String message = "There were failing tests. See the report at: " + reportUrl;
+            if (getIgnoreFailures()) {
+                getLogger().warn(message);
+                return;
+
+            } else {
+                throw new GradleException(message);
+            }
+        }
+
+        testFailed = false;
+    }
+
+    /**
+     * Determines if there are any tests to run.
+     *
+     * @return true if there are some tests to run, false otherwise
+     */
+    private boolean testsFound() {
+        // For now we check if there are any test sources. We could inspect the test classes and
+        // apply JUnit logic to see if there's something to run, but that would not catch the case
+        // where user makes a typo in a test name or forgets to inherit from a JUnit class
+        return !getProject().files(testData.getTestDirectories()).getAsFileTree().isEmpty();
+    }
+
+    public File getReportsDir() {
+        return reportsDir;
+    }
+
+    public void setReportsDir(File reportsDir) {
+        this.reportsDir = reportsDir;
+    }
+
+    @Override
+    public File getResultsDir() {
+        return resultsDir;
+    }
+
+    public void setResultsDir(File resultsDir) {
+        this.resultsDir = resultsDir;
+    }
+
+    public File getCoverageDir() {
+        return coverageDir;
+    }
+
+    public void setCoverageDir(File coverageDir) {
+        this.coverageDir = coverageDir;
+    }
+
+    public String getFlavorName() {
+        return flavorName;
+    }
+
+    public void setFlavorName(String flavorName) {
+        this.flavorName = flavorName;
+    }
+
+    public Collection<String> getInstallOptions() {
+        return installOptions;
+    }
+
+    public void setInstallOptions(Collection<String> installOptions) {
+        this.installOptions = installOptions;
+    }
+
+    public DeviceProvider getDeviceProvider() {
+        return deviceProvider;
+    }
+
+    public void setDeviceProvider(DeviceProvider deviceProvider) {
+        this.deviceProvider = deviceProvider;
+    }
+
+    public TestData getTestData() {
+        return testData;
+    }
+
+    public void setTestData(TestData testData) {
+        this.testData = testData;
+    }
+
+    public File getAdbExec() {
+        return adbExec;
+    }
+
+    public void setAdbExec(File adbExec) {
+        this.adbExec = adbExec;
+    }
+
+    public File getSplitSelectExec() {
+        return splitSelectExec;
+    }
+
+    public void setSplitSelectExec(File splitSelectExec) {
+        this.splitSelectExec = splitSelectExec;
+    }
+
+    public ProcessExecutor getProcessExecutor() {
+        return processExecutor;
+    }
+
+    public void setProcessExecutor(ProcessExecutor processExecutor) {
+        this.processExecutor = processExecutor;
+    }
+
+    @Override
+    public boolean getIgnoreFailures() {
+        return ignoreFailures;
+    }
+
+    @Override
+    public void setIgnoreFailures(boolean ignoreFailures) {
+        this.ignoreFailures = ignoreFailures;
+    }
+
+    @Override
+    public boolean getTestFailed() {
+        return testFailed;
+    }
+
+
+    public static class ConfigAction implements TaskConfigAction<DeviceProviderInstrumentTestTask> {
+
+        private final VariantScope scope;
+        private final DeviceProvider deviceProvider;
+        private final TestData testData;
+
+        public ConfigAction(VariantScope scope, DeviceProvider deviceProvider, TestData testData) {
+            this.scope = scope;
+            this.deviceProvider = deviceProvider;
+            this.testData = testData;
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName(deviceProvider.getName());
+        }
+
+        @Override
+        public Class<DeviceProviderInstrumentTestTask> getType() {
+            return DeviceProviderInstrumentTestTask.class;
+        }
+
+        @Override
+        public void execute(DeviceProviderInstrumentTestTask task) {
+            final boolean connected = deviceProvider instanceof ConnectedDeviceProvider;
+            String variantName = scope.getTestedVariantData() != null ?
+                    scope.getTestedVariantData().getName() : scope.getVariantData().getName();
+            if (connected) {
+                task.setDescription("Installs and runs the tests for " + variantName +
+                        " on connected devices.");
+            } else {
+                task.setDescription("Installs and runs the tests for " + variantName +
+                        " using provider: " + StringHelper.capitalize(deviceProvider.getName()));
+
+            }
+            task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+            task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            task.setVariantName(variantName);
+            task.setTestData(testData);
+            task.setFlavorName(testData.getFlavorName());
+            task.setDeviceProvider(deviceProvider);
+            task.setInstallOptions(scope.getGlobalScope().getExtension().getAdbOptions().getInstallOptions());
+            task.setProcessExecutor(scope.getGlobalScope().getAndroidBuilder().getProcessExecutor());
+
+            String flavorFolder = testData.getFlavorName();
+            if (!flavorFolder.isEmpty()) {
+                flavorFolder = FD_FLAVORS + "/" + flavorFolder;
+            }
+            String providerFolder = connected ? CONNECTED : DEVICE + "/" + deviceProvider.getName();
+            final String subFolder = "/" + providerFolder + "/" + flavorFolder;
+
+            ConventionMappingHelper.map(task, "adbExec", new Callable<File>() {
+                @Override
+                public File call() {
+                    final SdkInfo info = scope.getGlobalScope().getSdkHandler()
+                            .getSdkInfo();
+                    return (info == null ? null : info.getAdb());
+                }
+            });
+            ConventionMappingHelper.map(task, "splitSelectExec", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    final TargetInfo info = scope.getGlobalScope().getAndroidBuilder().getTargetInfo();
+                    String path = info == null ? null : info.getBuildTools().getPath(SPLIT_SELECT);
+                    if (path != null) {
+                        File splitSelectExe = new File(path);
+                        return splitSelectExe.exists() ? splitSelectExe : null;
+                    } else {
+                        return null;
+                    }
+                }
+            });
+
+            ConventionMappingHelper.map(task, "resultsDir", new Callable<File>() {
+                @Override
+                public File call() {
+                    String rootLocation = scope.getGlobalScope().getExtension().getTestOptions().getResultsDir();
+                    if (rootLocation == null) {
+                        rootLocation = scope.getGlobalScope().getBuildDir() + "/" +
+                                FD_OUTPUTS + "/" + FD_ANDROID_RESULTS;
+                    }
+                    return scope.getGlobalScope().getProject().file(rootLocation + subFolder);
+                }
+            });
+
+            ConventionMappingHelper.map(task, "reportsDir", new Callable<File>() {
+                @Override
+                public File call() {
+                    String rootLocation = scope.getGlobalScope().getExtension().getTestOptions().getReportDir();
+                    if (rootLocation == null) {
+                        rootLocation = scope.getGlobalScope().getBuildDir() + "/" +
+                                FD_REPORTS + "/" + FD_ANDROID_TESTS;
+                    }
+                    return scope.getGlobalScope().getProject().file(rootLocation + subFolder);
+                }
+            });
+
+            String rootLocation = scope.getGlobalScope().getBuildDir() + "/" +
+                                FD_OUTPUTS + "/code-coverage";
+            task.setCoverageDir(scope.getGlobalScope().getProject().file(rootLocation + subFolder));
+
+            if (scope.getVariantData() instanceof TestVariantData) {
+                TestVariantData testVariantData = (TestVariantData) scope.getVariantData();
+                if (connected) {
+                    testVariantData.connectedTestTask = task;
+                } else {
+                    testVariantData.providerTestTaskList.add(task);
+                }
+            }
+
+            task.setEnabled(deviceProvider.isConfigured());
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ExtractJavaResourcesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ExtractJavaResourcesTask.java
new file mode 100644
index 0000000..9e4bc57
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ExtractJavaResourcesTask.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015 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.annotations.NonNull;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.ide.common.packaging.PackagingUtils;
+import com.android.utils.FileUtils;
+import com.google.common.io.ByteStreams;
+
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * Extract all packaged jar files java resources into a directory. Each jar file will be extracted
+ * in a jar specific folder, and only java resources are extracted.
+ */
+public class ExtractJavaResourcesTask extends DefaultAndroidTask {
+
+    // the fact we use a SET is not right, we should have an ordered list of jars...
+    // VariantConfiguration.getPackaged|ProvidedJars should use List<>
+    @InputFiles
+    public Set<File> jarInputFiles;
+
+    @OutputDirectory
+    public File outputDir;
+
+    @InputFiles
+    public Set<File> getJarInputFiles() {
+        return jarInputFiles;
+    }
+
+    @TaskAction
+    public void extractJavaResources(final IncrementalTaskInputs incrementalTaskInputs) {
+
+        incrementalTaskInputs.outOfDate(new org.gradle.api.Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails inputFileDetails) {
+                File inputJar = inputFileDetails.getFile();
+                String folderName = inputJar.getName() +
+                        inputJar.getPath().hashCode();
+
+                File outputFolder = new File(outputDir, folderName);
+                if (outputFolder.exists()) {
+                    try {
+                        FileUtils.deleteFolder(outputFolder);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                if (!outputFolder.mkdirs()) {
+                    throw new RuntimeException(
+                            "Cannot create folder to extract java resources in for "
+                                    + inputJar.getAbsolutePath());
+                }
+
+                // create the jar file visitor that will check for out-dated resources.
+
+                JarFile jarFile = null;
+                try {
+                    jarFile = new JarFile(inputJar);
+                    Enumeration<JarEntry> entries = jarFile.entries();
+                    while (entries.hasMoreElements()) {
+                        JarEntry jarEntry = entries.nextElement();
+                        if (!jarEntry.isDirectory()) {
+                            processJarEntry(jarFile, jarEntry, outputFolder);
+                        }
+                    }
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                } finally {
+                    if (jarFile != null) {
+                        try {
+                            jarFile.close();
+                        } catch (IOException e) {
+                            // ignore.
+                        }
+                    }
+                }
+            }
+        });
+
+        incrementalTaskInputs.removed(new org.gradle.api.Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails inputFileDetails) {
+                File deletedJar = inputFileDetails.getFile();
+                String folderName = deletedJar.getName() +
+                        deletedJar.getPath().hashCode();
+                File outputFolder = new File(outputDir, folderName);
+                if (outputFolder.exists()) {
+                    try {
+                        FileUtils.deleteFolder(outputFolder);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * process one jar entry in an input jar file and optionally stores the entry in the output
+     * folder.
+     * @param jarFile the input jar file
+     * @param jarEntry the jar entry in the jarFile to process
+     * @param outputDir the output folder to use to copy/merge the entry in.
+     * @throws IOException
+     */
+    private static void processJarEntry(JarFile jarFile, JarEntry jarEntry, File outputDir) throws IOException {
+        File outputFile = new File(outputDir, jarEntry.getName());
+        Action action = getAction(jarEntry.getName());
+        if (action == Action.COPY) {
+            if (!outputFile.getParentFile().exists() &&
+                    !outputFile.getParentFile().mkdirs()) {
+                throw new RuntimeException("Cannot create directory " + outputFile.getParent());
+            }
+            if (!outputFile.exists() || outputFile.lastModified()
+                    < jarEntry.getTime()) {
+                InputStream inputStream = null;
+                OutputStream outputStream = null;
+                try {
+                    inputStream = jarFile.getInputStream(jarEntry);
+                    if (inputStream != null) {
+                        outputStream = new BufferedOutputStream(
+                                new FileOutputStream(outputFile));
+                        ByteStreams.copy(inputStream, outputStream);
+                        outputStream.flush();
+                    } else {
+                        throw new RuntimeException("Cannot copy " + jarEntry.getName());
+                    }
+                } finally {
+                    try {
+                        if (outputStream != null) {
+                            outputStream.close();
+                        }
+                    } finally {
+                        if (inputStream != null) {
+                            inputStream.close();
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    /**
+     * Define all possible actions for a Jar file entry.
+     */
+    enum Action {
+        /**
+         * Copy the file to the output destination.
+         */
+        COPY,
+        /**
+         * Ignore the file.
+         */
+        IGNORE
+    }
+
+    /**
+     * Provides an {@link Action} for the archive entry.
+     * @param archivePath the archive entry path in the archive.
+     * @return the action to implement.
+     */
+    @NonNull
+    public static Action getAction(@NonNull String archivePath) {
+        // Manifest files are never merged.
+        if (JarFile.MANIFEST_NAME.equals(archivePath)) {
+            return Action.IGNORE;
+        }
+
+        // split the path into segments.
+        String[] segments = archivePath.split("/");
+
+        // empty path? skip to next entry.
+        if (segments.length == 0) {
+            return Action.IGNORE;
+        }
+
+        // Check each folders to make sure they should be included.
+        // Folders like CVS, .svn, etc.. should already have been excluded from the
+        // jar file, but we need to exclude some other folder (like /META-INF) so
+        // we check anyway.
+        for (int i = 0 ; i < segments.length - 1; i++) {
+            if (!PackagingUtils.checkFolderForPackaging(segments[i])) {
+                return Action.IGNORE;
+            }
+        }
+
+        // get the file name from the path
+        String fileName = segments[segments.length-1];
+
+        return PackagingUtils.checkFileForPackaging(fileName)
+                ? Action.COPY
+                : Action.IGNORE;
+    }
+
+    public static class Config implements TaskConfigAction<ExtractJavaResourcesTask> {
+
+        private final VariantScope scope;
+
+        public Config(VariantScope scope) {
+            this.scope = scope;
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName("extract", "PackagedLibrariesJavaResources");
+        }
+
+        @Override
+        public Class<ExtractJavaResourcesTask> getType() {
+            return ExtractJavaResourcesTask.class;
+        }
+
+        @Override
+        public void execute(ExtractJavaResourcesTask extractJavaResourcesTask) {
+            ConventionMappingHelper.map(extractJavaResourcesTask, "jarInputFiles",
+                    new Callable<Set<File>>() {
+
+                    @Override
+                    public Set<File> call() throws Exception {
+                        return scope.getVariantConfiguration().getPackagedJars();
+                    }
+                });
+            extractJavaResourcesTask.outputDir = scope.getPackagedJarsJavaResDestinationDir();
+            extractJavaResourcesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java
index 573b8f1..2fb3017 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java
@@ -21,18 +21,15 @@
 import static com.android.SdkConstants.FN_ANDROID_MANIFEST_XML;
 import static com.android.builder.core.BuilderConstants.ANDROID_WEAR_MICRO_APK;
 
-import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.build.gradle.internal.scope.ConventionMappingHelper;
 import com.android.build.gradle.internal.scope.TaskConfigAction;
 import com.android.build.gradle.internal.scope.VariantScope;
 import com.android.build.gradle.internal.variant.ApkVariantData;
-import com.android.build.gradle.tasks.AidlCompile;
 import com.android.builder.core.AndroidBuilder;
-import com.android.builder.core.VariantConfiguration;
 import com.android.ide.common.internal.LoggedErrorException;
 import com.android.ide.common.process.ProcessException;
-import com.android.utils.StringHelper;
+import com.android.utils.FileUtils;
 import com.google.common.io.Files;
 
 import org.gradle.api.artifacts.Configuration;
@@ -73,7 +70,7 @@
             InterruptedException {
         // always empty output dir.
         File outDir = getResOutputDir();
-        emptyFolder(outDir);
+        FileUtils.emptyFolder(outDir);
 
         File apk = getApkFile();
         // copy the file into the destination, by sanitizing the name first.
@@ -177,6 +174,7 @@
             variantData.generateApkDataTask = task;
 
             task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            task.setVariantName(scope.getVariantConfiguration().getFullName());
             ConventionMappingHelper.map(task, "resOutputDir", new Callable<File>() {
                 @Override
                 public File call() throws Exception {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java
index e16a9ac..f28be32 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java
@@ -27,6 +27,7 @@
 import org.gradle.api.tasks.incremental.InputFileDetails;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -57,7 +58,7 @@
      * {@link #isIncremental()} returns false.
      *
      */
-    protected abstract void doFullTaskAction();
+    protected abstract void doFullTaskAction() throws IOException;
 
     /**
      * Optional incremental task action.
@@ -65,7 +66,7 @@
      *
      * @param changedInputs the changed input files.
      */
-    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
+    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws IOException {
         // do nothing.
     }
 
@@ -74,7 +75,7 @@
      * Calls out to the doTaskAction as needed.
      */
     @TaskAction
-    void taskAction(IncrementalTaskInputs inputs) {
+    void taskAction(IncrementalTaskInputs inputs) throws IOException {
         if (!isIncremental()) {
             doFullTaskAction();
             return;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.groovy
deleted file mode 100644
index 8a2086e..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.groovy
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2014 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.build.gradle.internal.LoggerWrapper
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.internal.InstallUtils
-import com.android.builder.sdk.SdkInfo
-import com.android.builder.sdk.TargetInfo
-import com.android.builder.testing.ConnectedDeviceProvider
-import com.android.builder.testing.api.DeviceConfigProviderImpl
-import com.android.builder.testing.api.DeviceConnector
-import com.android.builder.testing.api.DeviceProvider
-import com.android.ide.common.build.SplitOutputMatcher
-import com.android.ide.common.process.ProcessExecutor
-import com.android.utils.ILogger
-import com.google.common.base.Joiner
-import com.google.common.collect.ImmutableList
-import org.gradle.api.Action
-import org.gradle.api.GradleException
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-
-import java.util.concurrent.Callable
-
-import static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT
-
-/**
- * Task installing an app variant. It looks at connected device and install the best matching
- * variant output on each device.
- */
-public class InstallVariantTask extends BaseTask {
-
-    @InputFile
-    File adbExe
-
-    @InputFile
-    @Optional
-    File splitSelectExe
-
-    ProcessExecutor processExecutor;
-
-    String projectName
-
-    @Input
-    int timeOutInMs = 0
-
-    @Input @Optional
-    Collection<String> installOptions;
-
-    BaseVariantData<? extends BaseVariantOutputData> variantData
-
-    InstallVariantTask() {
-        this.getOutputs().upToDateWhen {
-            logger.debug("Install task is always run.");
-            false;
-        }
-    }
-
-    @TaskAction
-    void install() {
-        final ILogger iLogger = new LoggerWrapper(getLogger(), LogLevel.LIFECYCLE)
-        DeviceProvider deviceProvider = new ConnectedDeviceProvider(getAdbExe(), iLogger)
-        deviceProvider.init()
-
-        VariantConfiguration variantConfig = variantData.variantConfiguration
-        String variantName = variantConfig.fullName
-
-        int successfulInstallCount = 0;
-
-        for (DeviceConnector device : deviceProvider.getDevices()) {
-            if (InstallUtils.checkDeviceApiLevel(
-                    device, variantConfig.minSdkVersion, iLogger, projectName, variantName)) {
-                // When InstallUtils.checkDeviceApiLevel returns false, it logs the reason.
-                List<File> apkFiles = SplitOutputMatcher.computeBestOutput(processExecutor,
-                        getSplitSelectExe(),
-                        new DeviceConfigProviderImpl(device),
-                        variantData.outputs,
-                        variantData.variantConfiguration.getSupportedAbis())
-
-                if (apkFiles.isEmpty()) {
-                    logger.lifecycle(
-                            "Skipping device '${device.getName()}' for " +
-                                    "'${projectName}:${variantName}': " +
-                                    "Could not find build of variant which supports " +
-                                    "density " + "${device.getDensity()} " +
-                                    "and an ABI in " + Joiner.on(", ").join(device.getAbis()));
-                } else {
-                    logger.lifecycle(
-                            "Installing APK '${Joiner.on(", ").join(apkFiles*.getName())}'" +
-                                    " on '${device.getName()}'")
-
-                    List<String> extraArgs = installOptions == null ? ImmutableList.of() :
-                            installOptions;
-                    if (apkFiles.size() > 1 || device.getApiLevel() >= 21) {
-                        device.installPackages(apkFiles, extraArgs, getTimeOutInMs(), getILogger());
-                        successfulInstallCount++
-                    } else {
-                        device.installPackage(apkFiles.get(0), extraArgs, getTimeOutInMs(),
-                                getILogger())
-                        successfulInstallCount++
-                    }
-                }
-            }
-        }
-
-        if (successfulInstallCount == 0) {
-            throw new GradleException("Failed to install on any devices.")
-        } else {
-            logger.quiet("Installed on ${successfulInstallCount} " +
-                    "${successfulInstallCount==1?'device':'devicess'}.");
-        }
-    }
-
-    public static class ConfigAction implements TaskConfigAction<InstallVariantTask> {
-
-        private final VariantScope scope;
-
-        public ConfigAction(VariantScope scope) {
-            this.scope = scope;
-        }
-
-        @Override
-        String getName() {
-            return "install${scope.getVariantConfiguration().fullName.capitalize()}";
-        }
-
-        @Override
-        Class<InstallVariantTask> getType() {
-            return InstallVariantTask.class
-        }
-
-        @Override
-        public void execute(InstallVariantTask installTask) {
-            installTask.setDescription("Installs the " + scope.getVariantData().getDescription() + ".");
-            installTask.setGroup(TaskManager.INSTALL_GROUP);
-            installTask.setProjectName(scope.getGlobalScope().getProject().getName());
-            installTask.setVariantData(scope.getVariantData());
-            installTask.setTimeOutInMs(scope.getGlobalScope().getExtension().getAdbOptions().getTimeOutInMs());
-            installTask.setInstallOptions(scope.getGlobalScope().getExtension().getAdbOptions().getInstallOptions());
-            installTask.setProcessExecutor(scope.getGlobalScope().getAndroidBuilder().getProcessExecutor());
-            ConventionMappingHelper.map(installTask, "adbExe", new Closure<File>(this, this) {
-                public File doCall(Object it) {
-                    final SdkInfo info = scope.getGlobalScope().getSdkHandler().getSdkInfo();
-                    return (info == null ? null : info.getAdb());
-                }
-
-                public File doCall() {
-                    return doCall(null);
-                }
-
-            });
-            ConventionMappingHelper.map(installTask, "splitSelectExe", new Callable<File>() {
-                @Override
-                public File call() throws Exception {
-                    final TargetInfo info = scope.getGlobalScope().getAndroidBuilder().getTargetInfo();
-                    String path = info == null ? null : info.getBuildTools().getPath(SPLIT_SELECT);
-                    if (path != null) {
-                        File splitSelectExe = new File(path);
-                        return splitSelectExe.exists() ? splitSelectExe : null;
-                    } else {
-                        return null;
-                    }
-                }
-            });
-            ((ApkVariantData) scope.getVariantData()).installTask = installTask;
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.java
new file mode 100644
index 0000000..8e44598
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2014 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 static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT;
+
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.ApkVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.internal.InstallUtils;
+import com.android.builder.sdk.SdkInfo;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.testing.ConnectedDeviceProvider;
+import com.android.builder.testing.api.DeviceConfigProviderImpl;
+import com.android.builder.testing.api.DeviceConnector;
+import com.android.builder.testing.api.DeviceException;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.ide.common.build.SplitOutputMatcher;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessExecutor;
+import com.android.utils.FileUtils;
+import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Task installing an app variant. It looks at connected device and install the best matching
+ * variant output on each device.
+ */
+public class InstallVariantTask extends BaseTask {
+
+    private File adbExe;
+
+    private File splitSelectExe;
+
+    private ProcessExecutor processExecutor;
+
+    private String projectName;
+
+    private int timeOutInMs = 0;
+
+    private Collection<String> installOptions;
+
+    private BaseVariantData<? extends BaseVariantOutputData> variantData;
+
+    public InstallVariantTask() {
+        this.getOutputs().upToDateWhen(new Spec<Task>() {
+            @Override
+            public boolean isSatisfiedBy(Task task) {
+                getLogger().debug("Install task is always run.");
+                return false;
+            }
+        });
+    }
+
+    @TaskAction
+    public void install() throws DeviceException, ProcessException, InterruptedException {
+        final ILogger iLogger = new LoggerWrapper(getLogger(), LogLevel.LIFECYCLE);
+        DeviceProvider deviceProvider = new ConnectedDeviceProvider(getAdbExe(), iLogger);
+        deviceProvider.init();
+
+        VariantConfiguration variantConfig = variantData.getVariantConfiguration();
+        String variantName = variantConfig.getFullName();
+
+        int successfulInstallCount = 0;
+        List<? extends DeviceConnector> devices = deviceProvider.getDevices();
+        for (final DeviceConnector device : devices) {
+            if (InstallUtils.checkDeviceApiLevel(
+                    device, variantConfig.getMinSdkVersion(), iLogger, projectName, variantName)) {
+                // When InstallUtils.checkDeviceApiLevel returns false, it logs the reason.
+                final List<File> apkFiles = SplitOutputMatcher.computeBestOutput(processExecutor,
+                        getSplitSelectExe(),
+                        new DeviceConfigProviderImpl(device),
+                        variantData.getOutputs(),
+                        variantData.getVariantConfiguration().getSupportedAbis());
+
+                if (apkFiles.isEmpty()) {
+                    getLogger().lifecycle(
+                            "Skipping device '{}' for '{}:{}': Could not find build of variant " +
+                                    "which supports density {} and an ABI in {}",
+                            device.getName(), projectName, variantName,
+                            device.getDensity(), Joiner.on(", ").join(device.getAbis()));
+                } else {
+                    getLogger().lifecycle(
+                            "Installing APK '{}' on '{}' for {}:{}",
+                            FileUtils.getNamesAsCommaSeparatedList(apkFiles),
+                            device.getName(),
+                            projectName,
+                            variantName);
+
+                    final Collection<String> extraArgs =
+                            Objects.firstNonNull(installOptions, ImmutableList.<String>of());
+
+                    if (apkFiles.size() > 1 || device.getApiLevel() >= 21) {
+                        device.installPackages(apkFiles, extraArgs,
+                                getTimeOutInMs(), getILogger());
+                        successfulInstallCount++;
+                    } else {
+                        device.installPackage(apkFiles.get(0), extraArgs,
+                                getTimeOutInMs(),
+                                getILogger());
+                        successfulInstallCount++;
+                    }
+                }
+            }
+        }
+
+        if (successfulInstallCount == 0) {
+            throw new GradleException("Failed to install on any devices.");
+        } else {
+            getLogger().quiet("Installed on {} {}.",
+                    successfulInstallCount,
+                    successfulInstallCount==1 ? "device" : "devices");
+        }
+    }
+
+    @InputFile
+    public File getAdbExe() {
+        return adbExe;
+    }
+
+    public void setAdbExe(File adbExe) {
+        this.adbExe = adbExe;
+    }
+
+    @InputFile
+    @Optional
+    public File getSplitSelectExe() {
+        return splitSelectExe;
+    }
+
+    public void setSplitSelectExe(File splitSelectExe) {
+        this.splitSelectExe = splitSelectExe;
+    }
+
+    public ProcessExecutor getProcessExecutor() {
+        return processExecutor;
+    }
+
+    public void setProcessExecutor(ProcessExecutor processExecutor) {
+        this.processExecutor = processExecutor;
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+    @Input
+    public int getTimeOutInMs() {
+        return timeOutInMs;
+    }
+
+    public void setTimeOutInMs(int timeOutInMs) {
+        this.timeOutInMs = timeOutInMs;
+    }
+
+    @Input
+    @Optional
+    public Collection<String> getInstallOptions() {
+        return installOptions;
+    }
+
+    public void setInstallOptions(Collection<String> installOptions) {
+        this.installOptions = installOptions;
+    }
+
+    public BaseVariantData<? extends BaseVariantOutputData> getVariantData() {
+        return variantData;
+    }
+
+    public void setVariantData(
+            BaseVariantData<? extends BaseVariantOutputData> variantData) {
+        this.variantData = variantData;
+    }
+
+    public static class ConfigAction implements TaskConfigAction<InstallVariantTask> {
+
+        private final VariantScope scope;
+
+        public ConfigAction(VariantScope scope) {
+            this.scope = scope;
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName("install");
+        }
+
+        @Override
+        public Class<InstallVariantTask> getType() {
+            return InstallVariantTask.class;
+        }
+
+        @Override
+        public void execute(InstallVariantTask installTask) {
+            installTask.setDescription(
+                    "Installs the " + scope.getVariantData().getDescription() + ".");
+            installTask.setVariantName(scope.getVariantConfiguration().getFullName());
+            installTask.setGroup(TaskManager.INSTALL_GROUP);
+            installTask.setProjectName(scope.getGlobalScope().getProject().getName());
+            installTask.setVariantData(scope.getVariantData());
+            installTask.setTimeOutInMs(
+                    scope.getGlobalScope().getExtension().getAdbOptions().getTimeOutInMs());
+            installTask.setInstallOptions(
+                    scope.getGlobalScope().getExtension().getAdbOptions().getInstallOptions());
+            installTask.setProcessExecutor(
+                    scope.getGlobalScope().getAndroidBuilder().getProcessExecutor());
+            ConventionMappingHelper.map(installTask, "adbExe", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    final SdkInfo info = scope.getGlobalScope().getSdkHandler().getSdkInfo();
+                    return (info == null ? null : info.getAdb());
+                }
+            });
+            ConventionMappingHelper.map(installTask, "splitSelectExe", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    final TargetInfo info =
+                            scope.getGlobalScope().getAndroidBuilder().getTargetInfo();
+                    String path = info == null ? null : info.getBuildTools().getPath(SPLIT_SELECT);
+                    if (path != null) {
+                        File splitSelectExe = new File(path);
+                        return splitSelectExe.exists() ? splitSelectExe : null;
+                    } else {
+                        return null;
+                    }
+                }
+            });
+            ((ApkVariantData) scope.getVariantData()).installTask = installTask;
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTask.java
new file mode 100644
index 0000000..b0aa93f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTask.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2015 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.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.tasks.JavaResourcesProvider;
+import com.android.builder.model.PackagingOptions;
+import com.android.builder.signing.SignedJarBuilder;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Files;
+
+import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * Merges java resources from temporary expansion folders (created from the packaged jars
+ * resources and source folder java resources) into a single output directory that can be used
+ * during obfuscation and packaging.
+ *
+ * This task is the default {@link JavaResourcesProvider} to provide merged java resources to
+ * the final variant packaging step. However, if the variant obfuscation is turned on, some of
+ * these resources packages might need to be adapted to match the obfuscated code. In such
+ * a scenario, the {@link JavaResourcesProvider} will become the task responsible for obfuscation.
+ */
+@ParallelizableTask
+public class MergeJavaResourcesTask extends DefaultAndroidTask implements JavaResourcesProvider {
+
+    @Nested
+    @Optional
+    @Nullable
+    public PackagingOptions packagingOptions;
+
+    @InputDirectory
+    @Optional
+    @Nullable
+    public File getSourceJavaResourcesFolder() {
+        return sourceJavaResourcesFolder;
+    }
+
+    @InputDirectory
+    @Optional
+    @Nullable
+    public File getPackagedJarsJavaResourcesFolder() {
+        return packagedJarsJavaResourcesFolder;
+    }
+
+    @Nullable
+    private FileFilter packagingOptionsFilter;
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    @Nullable
+    private File sourceJavaResourcesFolder;
+    @SuppressWarnings({"UnusedDeclaration"})
+    @Nullable
+    private File packagedJarsJavaResourcesFolder;
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    @Nullable
+    private File outputDir;
+
+    @OutputDirectory
+    @Nullable
+    public File getOutputDir() {
+        return outputDir;
+    }
+
+    public List<File> getExpandedFolders() {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        if (getSourceJavaResourcesFolder() != null) {
+            builder.add(getSourceJavaResourcesFolder());
+        }
+        if (getPackagedJarsJavaResourcesFolder() != null) {
+            builder.add(getPackagedJarsJavaResourcesFolder());
+        }
+        return builder.build();
+    }
+
+    @TaskAction
+    void extractJavaResources(IncrementalTaskInputs incrementalTaskInputs) {
+
+        if (packagingOptionsFilter == null || getOutputDir() == null) {
+            throw new RuntimeException(
+                    "Internal error, packagingOptionsFilter or outputDir is null");
+        }
+        incrementalTaskInputs.outOfDate(new org.gradle.api.Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails inputFileDetails) {
+                try {
+                    packagingOptionsFilter.handleChanged(
+                            getOutputDir(), inputFileDetails.getFile());
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
+        incrementalTaskInputs.removed(new org.gradle.api.Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails inputFileDetails) {
+                try {
+                    packagingOptionsFilter.handleRemoved
+                            (getOutputDir(), inputFileDetails.getFile());
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
+    }
+
+    /**
+     * Defines a file filter contract which will use {@link PackagingOptions} to take appropriate
+     * action.
+     */
+    @VisibleForTesting
+    static final class FileFilter implements SignedJarBuilder.IZipEntryFilter {
+
+        /**
+         * User's setting for a particular archive entry. This is expressed in the build.gradle
+         * DSL and used by this filter to determine file merging behaviors.
+         */
+        private enum PackagingOption {
+            /**
+             * no action was described for archive entry.
+             */
+            NONE,
+            /**
+             * merge all archive entries with the same archive path.
+             */
+            MERGE,
+            /**
+             * pick to first archive entry with that archive path (not stable).
+             */
+            PICK_FIRST,
+            /**
+             * exclude all archive entries with that archive path.
+             */
+            EXCLUDE
+        }
+
+        @Nullable
+        private final PackagingOptions packagingOptions;
+        @NonNull
+        private final Set<String> excludes;
+        @NonNull
+        private final Set<String> pickFirsts;
+        @NonNull
+        private final MergeJavaResourcesTask owner;
+
+        public FileFilter(@NonNull MergeJavaResourcesTask owner,
+                @Nullable PackagingOptions packagingOptions) {
+            this.owner = owner;
+            this.packagingOptions = packagingOptions;
+            excludes = this.packagingOptions != null ? this.packagingOptions.getExcludes() :
+                    Collections.<String>emptySet();
+            pickFirsts = this.packagingOptions != null ? this.packagingOptions.getPickFirsts() :
+                    Collections.<String>emptySet();
+        }
+
+        /**
+         * Implementation of the {@link SignedJarBuilder.IZipEntryFilter} contract which only
+         * cares about copying or ignoring files since merging is handled differently.
+         * @param archivePath the archive file path of the entry
+         * @return true if the archive entry satisfies the filter, false otherwise.
+         * @throws ZipAbortException
+         */
+        @Override
+        public boolean checkEntry(@NonNull String archivePath)
+                throws ZipAbortException {
+            PackagingOption packagingOption = getPackagingAction(archivePath);
+            switch(packagingOption) {
+                case EXCLUDE:
+                    return false;
+                case PICK_FIRST:
+                    List<File> allFiles = getAllFiles(archivePath);
+                    return allFiles.isEmpty();
+                case MERGE:
+                case NONE:
+                    return true;
+                default:
+                    throw new RuntimeException("Unhandled PackagingOption " + packagingOption);
+            }
+        }
+
+        /**
+         * Notification of an incremental file changed since last successful run of the task.
+         *
+         * Usually, we just copy the changed file into the merged folder. However, if the user
+         * specified {@link PackagingOption#PICK_FIRST}, the file will only be copied if it the
+         * first pick. Also, if the user specified {@link PackagingOption#MERGE}, all the files
+         * with the same entry archive path will be re-merged.
+         *
+         * @param outputDir merged resources folder.
+         * @param changedFile changed file located in a temporary expansion folder
+         * @throws IOException
+         */
+        void handleChanged(@NonNull File outputDir, @NonNull File changedFile)
+                throws IOException {
+            String archivePath = getArchivePath(changedFile);
+            PackagingOption packagingOption = getPackagingAction(archivePath);
+            switch (packagingOption) {
+                case EXCLUDE:
+                    return;
+                case MERGE:
+                    // one of the merged file has changed, re-merge all of them.
+                    mergeAll(outputDir, archivePath);
+                    return;
+                case PICK_FIRST:
+                    copy(changedFile, outputDir, archivePath);
+                    return;
+                case NONE:
+                    copy(changedFile, outputDir, archivePath);
+            }
+        }
+
+        /**
+         * Notification of a file removal.
+         *
+         * file was removed, we need to check that it was not a pickFirst item (since we
+         * may now need to pick another one) or a merged item since we would need to re-merge
+         * all remaining items.
+         *
+         * @param outputDir expected merged output directory.
+         * @param removedFile removed file from the temporary resources folders.
+         * @throws IOException
+         */
+        public void handleRemoved(@NonNull File outputDir, @NonNull File removedFile)
+                throws IOException {
+
+
+            String archivePath = getArchivePath(removedFile);
+            // first delete the output file, it will be eventually replaced.
+            File outFile = new File(outputDir, archivePath);
+            if (outFile.exists()) {
+                if (!outFile.delete()) {
+                    throw new IOException("Cannot delete " + outFile.getAbsolutePath());
+                }
+            }
+            FileFilter.PackagingOption itemPackagingOption = getPackagingAction(archivePath);
+
+            switch(itemPackagingOption) {
+                case PICK_FIRST:
+                    // this was a picked up item, make sure we copy the first still available
+                    com.google.common.base.Optional<File> firstPick = getFirstPick(archivePath);
+                    if (firstPick.isPresent()) {
+                        copy(firstPick.get(), outputDir, archivePath);
+                    }
+                    return;
+                case MERGE:
+                    // re-merge all
+                    mergeAll(outputDir, archivePath);
+                    return;
+                case EXCLUDE:
+                case NONE:
+                    // do nothing
+                    return;
+                default:
+                    throw new RuntimeException("Unhandled package option"
+                            + itemPackagingOption);
+
+            }
+        }
+
+        private static void copy(@NonNull File inputFile,
+                @NonNull File outputDir,
+                @NonNull String archivePath) throws IOException {
+
+            File outputFile = new File(outputDir, archivePath);
+            createParentFolderIfNecessary(outputFile);
+            Files.copy(inputFile, outputFile);
+        }
+
+        private void mergeAll(@NonNull File outputDir, @NonNull String archivePath)
+                throws IOException {
+
+            File outputFile = new File(outputDir, archivePath);
+            if (outputFile.exists() && !outputFile.delete()) {
+                throw new RuntimeException("Cannot delete " + outputFile);
+            }
+            createParentFolderIfNecessary(outputFile);
+            List<File> allFiles = getAllFiles(archivePath);
+            if (!allFiles.isEmpty()) {
+                OutputStream os = null;
+                try {
+                    os = new BufferedOutputStream(new FileOutputStream(outputFile));
+                    // take each file in order and merge them.
+                    for (File file : allFiles) {
+                        Files.copy(file, os);
+                    }
+                } finally {
+                    if (os != null) {
+                        os.close();
+                    }
+                }
+            }
+        }
+
+        private static void createParentFolderIfNecessary(@NonNull File outputFile) {
+            File parentFolder = outputFile.getParentFile();
+            if (!parentFolder.exists()) {
+                if (!parentFolder.mkdirs()) {
+                    throw new RuntimeException("Cannot create folder " + parentFolder);
+                }
+            }
+        }
+
+        /**
+         * Return the first file from the temporary expansion folders that satisfy the archive path.
+         * @param archivePath the entry archive path.
+         * @return the first file reference of {@link com.google.common.base.Optional#absent()} if
+         * none exist in any temporary expansion folders.
+         */
+        @NonNull
+        private com.google.common.base.Optional<File> getFirstPick(
+                @NonNull final String archivePath) {
+
+            return com.google.common.base.Optional.fromNullable(
+                    forEachExpansionFolder(new FolderAction() {
+                        @Nullable
+                        @Override
+                        public File on(File folder) {
+                            File expandedFile = new File(folder, archivePath);
+                            if (expandedFile.exists()) {
+                                return expandedFile;
+                            }
+                            return null;
+                        }
+                    }));
+        }
+
+        /**
+         * Returns all files from temporary expansion folders with the same archive path.
+         * @param archivePath the entry archive path.
+         * @return a list possibly empty of {@link File}s that satisfy the archive path.
+         */
+        @NonNull
+        private List<File> getAllFiles(@NonNull final String archivePath) {
+            final ImmutableList.Builder<File> matchingFiles = ImmutableList.builder();
+            forEachExpansionFolder(new FolderAction() {
+                @Nullable
+                @Override
+                public File on(File folder) {
+                    File expandedFile = new File(folder, archivePath);
+                    if (expandedFile.exists()) {
+                        matchingFiles.add(expandedFile);
+                    }
+                    return null;
+                }
+            });
+            return matchingFiles.build();
+        }
+
+        /**
+         * An action on a folder.
+         */
+        private interface FolderAction {
+
+            /**
+             * Perform an action on a folder and stop the processing if something is returned
+             * @param folder the folder to perform the action on.
+             * @return a file to stop processing or null to continue to the next expansion folder
+             * if any.
+             */
+            @Nullable
+            File on(File folder);
+        }
+
+        /**
+         * Perform the passed action on each expansion folder.
+         * @param action the action to perform on each folder.
+         * @return a file if any action returned a value, or null if none returned a value.
+         */
+        @Nullable
+        private File forEachExpansionFolder(@NonNull FolderAction action) {
+            for (File expansionParentFolder : owner.getExpandedFolders()) {
+                File[] expansionFolders = expansionParentFolder.listFiles();
+                if (expansionFolders != null) {
+                    for (File expansionFolder : expansionFolders) {
+                        if (expansionFolder.isDirectory()) {
+                            File value = action.on(expansionFolder);
+                            if (value != null) {
+                                return value;
+                            }
+                        }
+                    }
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Returns the expansion folder for an expanded file. This represents the location
+         * where the packaged jar our source directories java resources were extracted into.
+         * @param expandedFile the java resource file.
+         * @return the expansion folder used to extract the java resource into.
+         */
+        @NonNull
+        private File getExpansionFolder(@NonNull final File expandedFile) {
+            File expansionFolder = forEachExpansionFolder(new FolderAction() {
+                @Nullable
+                @Override
+                public File on(File folder) {
+                    return expandedFile.getAbsolutePath().startsWith(folder.getAbsolutePath())
+                            ? folder : null;
+                    }
+                });
+            if (expansionFolder == null) {
+                throw new RuntimeException("Cannot determine expansion folder for " + expandedFile
+                        + " with folders "  + Joiner.on(",").join(owner.getExpandedFolders()));
+            }
+            return expansionFolder;
+        }
+
+        /**
+         * Determines the archive entry path relative to its expansion folder. The archive entry
+         * path is the path that was used to save the entry in the original .jar file that got
+         * expanded in the expansion folder.
+         * @param expandedFile the expanded file to find the relative archive entry from.
+         * @return the expanded file relative path to its expansion folder.
+         */
+        @NonNull
+        private String getArchivePath(@NonNull File expandedFile) {
+            File expansionFolder = getExpansionFolder(expandedFile);
+            return expandedFile.getAbsolutePath()
+                    .substring(expansionFolder.getAbsolutePath().length() + 1);
+        }
+
+        /**
+         * Determine the user's intention for a particular archive entry.
+         * @param archivePath the archive entry
+         * @return a {@link FileFilter.PackagingOption} as provided by the user in the build.gradle
+         */
+        @NonNull
+        private PackagingOption getPackagingAction(@NonNull String archivePath) {
+            if (packagingOptions != null) {
+                if (pickFirsts.contains(archivePath)) {
+                    return PackagingOption.PICK_FIRST;
+                }
+                if (packagingOptions.getMerges().contains(archivePath)) {
+                    return PackagingOption.MERGE;
+                }
+                if (excludes.contains(archivePath)) {
+                    return PackagingOption.EXCLUDE;
+                }
+            }
+            return PackagingOption.NONE;
+        }
+    }
+
+    @NonNull
+    @Override
+    public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+        return ImmutableList.of(new JavaResourcesLocation(Type.FOLDER, getOutputDir()));
+    }
+
+    public static class Config implements TaskConfigAction<MergeJavaResourcesTask> {
+
+        private final VariantScope scope;
+
+        public Config(VariantScope variantScope) {
+            this.scope = variantScope;
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName("merge", "JavaResources");
+        }
+
+        @Override
+        public Class<MergeJavaResourcesTask> getType() {
+            return MergeJavaResourcesTask.class;
+        }
+
+        @Override
+        public void execute(MergeJavaResourcesTask mergeJavaResourcesTask) {
+            mergeJavaResourcesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+
+            ConventionMappingHelper.map(mergeJavaResourcesTask, "sourceJavaResourcesFolder",
+                    new Callable<File>() {
+                        @Override
+                        public File call() throws Exception {
+                            return scope.getSourceFoldersJavaResDestinationDir().exists()
+                                    ? scope.getSourceFoldersJavaResDestinationDir()
+                                    : null;
+                        }
+                    });
+
+            ConventionMappingHelper.map(mergeJavaResourcesTask, "packagedJarsJavaResourcesFolder",
+                    new Callable<File>() {
+                        @Override
+                        public File call() throws Exception {
+                            return scope.getPackagedJarsJavaResDestinationDir().exists()
+                                ? scope.getPackagedJarsJavaResDestinationDir()
+                                : null;
+                        }
+                    });
+
+            File outputDir = scope.getJavaResourcesDestinationDir();
+            if (!outputDir.exists() && !outputDir.mkdirs()) {
+                throw new RuntimeException("Cannot create output directory " + outputDir);
+            }
+            mergeJavaResourcesTask.outputDir = outputDir;
+
+            PackagingOptions packagingOptions =
+                    scope.getGlobalScope().getExtension().getPackagingOptions();
+            mergeJavaResourcesTask.packagingOptionsFilter =
+                    new FileFilter(mergeJavaResourcesTask, packagingOptions);
+            mergeJavaResourcesTask.packagingOptions = packagingOptions;
+            scope.setPackagingOptionsFilter(mergeJavaResourcesTask.packagingOptionsFilter);
+        }
+    }
+}
+
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java
index cf8972b..bf70530 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java
@@ -17,9 +17,8 @@
 package com.android.build.gradle.internal.tasks;
 
 import com.android.builder.testing.MockableJarGenerator;
-import com.android.sdklib.internal.project.ProjectProperties;
 
-import groovy.transform.CompileStatic;
+import org.gradle.api.DefaultTask;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.InputFile;
 import org.gradle.api.tasks.OutputFile;
@@ -31,7 +30,7 @@
 /**
  * Task for generating a mockable android.jar
  */
-public class MockableAndroidJarTask extends DefaultAndroidTask {
+public class MockableAndroidJarTask extends DefaultTask {
 
    private File mAndroidJar;
 
@@ -56,13 +55,25 @@
       return mReturnDefaultValues;
    }
 
+   public void setReturnDefaultValues(boolean returnDefaultValues) {
+      mReturnDefaultValues = returnDefaultValues;
+   }
+
    @OutputFile
    public File getOutputFile() {
       return mOutputFile;
    }
 
+   public void setOutputFile(File outputFile) {
+       mOutputFile = outputFile;
+   }
+
    @InputFile
    public File getAndroidJar() {
       return mAndroidJar;
    }
+
+   public void setAndroidJar(File androidJar) {
+       mAndroidJar = androidJar;
+   }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
index 5d40f2b..afe5b5e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
@@ -16,7 +16,7 @@
 
 package com.android.build.gradle.internal.tasks
 
-import com.android.build.gradle.internal.core.NdkConfig
+import com.android.build.gradle.internal.dsl.CoreNdkOptions
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.Optional
 
@@ -25,7 +25,7 @@
  */
 class NdkTask extends BaseTask {
 
-    NdkConfig ndkConfig
+    CoreNdkOptions ndkConfig
 
     @Input @Optional
     String getModuleName() {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.groovy
deleted file mode 100755
index 2207d19..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.groovy
+++ /dev/null
@@ -1,81 +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.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.dependency.DependencyChecker
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.builder.model.ApiVersion
-import com.android.builder.model.SyncIssue
-import com.android.sdklib.SdkVersionInfo
-import com.android.utils.Pair
-import groovy.transform.CompileStatic
-import org.gradle.api.GradleException
-import org.gradle.api.tasks.TaskAction
-
-@CompileStatic
-public class PrepareDependenciesTask extends BaseTask {
-    BaseVariantData variant
-    final List<DependencyChecker> checkers = []
-    final Set<Pair<Integer, String>> androidDependencies = []
-
-    public void addDependency(Pair<Integer, String> api) {
-        androidDependencies.add(api)
-    }
-
-    @TaskAction
-    protected void prepare() {
-        ApiVersion minSdkVersion = variant.variantConfiguration.minSdkVersion
-        int minSdk = 1
-        if (minSdkVersion != null) {
-            if (minSdkVersion.getCodename() != null) {
-                minSdk = SdkVersionInfo.getApiByBuildCode(minSdkVersion.getCodename(), true)
-            } else {
-                minSdk = minSdkVersion.getApiLevel()
-            }
-        }
-
-        boolean foundError = false;
-
-        for (DependencyChecker checker : checkers) {
-            checker.legacyApiLevels.each { mavenVersion, api ->
-                if (api > minSdk) {
-                    foundError = true;
-                    def configurationName = checker.configurationDependencies.name.capitalize()
-                    logger.error(
-                            "Variant ${configurationName} has a dependency on version ${mavenVersion.version} " +
-                                    "of the legacy ${mavenVersion.group} Maven artifact, which corresponds to " +
-                                    "API level ${api}. This is not compatible with min SDK of this module, " +
-                                    "which is ${minSdk}. Please use the 'gradle dependencies' task to debug your " +
-                                    "dependencies graph.")
-                }
-            }
-
-            for (SyncIssue syncIssue : checker.getSyncIssues()) {
-                foundError = true
-                logger.error(syncIssue.message);
-            }
-        }
-
-        if (foundError) {
-            throw new GradleException("Dependency Error. See console for details.");
-        }
-
-    }
-
-    def addChecker(DependencyChecker checker) {
-        checkers.add(checker)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.java
new file mode 100755
index 0000000..e605002
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.java
@@ -0,0 +1,95 @@
+/*
+ * 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.internal.tasks;
+
+import com.android.build.gradle.internal.dependency.DependencyChecker;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.SyncIssue;
+import com.android.sdklib.SdkVersionInfo;
+import com.android.utils.StringHelper;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.tasks.TaskAction;
+
+import java.util.List;
+import java.util.Map;
+
+public class PrepareDependenciesTask extends BaseTask {
+
+    private BaseVariantData variant;
+    private final List<DependencyChecker> checkers = Lists.newArrayList();
+
+    @TaskAction
+    protected void prepare() {
+        ApiVersion minSdkVersion = variant.getVariantConfiguration().getMinSdkVersion();
+        int minSdk = 1;
+        if (minSdkVersion.getCodename() != null) {
+            minSdk = SdkVersionInfo.getApiByBuildCode(minSdkVersion.getCodename(), true);
+        } else {
+            minSdk = minSdkVersion.getApiLevel();
+        }
+
+        boolean foundError = false;
+
+        for (DependencyChecker checker : checkers) {
+            for (Map.Entry<ModuleVersionIdentifier, Integer> entry :
+                    checker.getLegacyApiLevels().entrySet()) {
+                ModuleVersionIdentifier mavenVersion = entry.getKey();
+                int api = entry.getValue();
+                if (api > minSdk) {
+                    foundError = true;
+                    String configurationName = checker.getConfigurationDependencies().getName();
+                    getLogger().error(
+                            "Variant {} has a dependency on version {} of the legacy {} Maven " +
+                                    "artifact, which corresponds to API level {}. This is not " +
+                                    "compatible with min SDK of this module, which is {}. " +
+                                    "Please use the 'gradle dependencies' task to debug your " +
+                                    "dependencies graph.",
+                            StringHelper.capitalize(configurationName),
+                            mavenVersion.getVersion(),
+                            mavenVersion.getGroup(),
+                            api,
+                            minSdk);
+                }
+            }
+
+            for (SyncIssue syncIssue : checker.getSyncIssues()) {
+                foundError = true;
+                getLogger().error(syncIssue.getMessage());
+            }
+        }
+
+        if (foundError) {
+            throw new GradleException("Dependency Error. See console for details.");
+        }
+
+    }
+
+    public void addChecker(DependencyChecker checker) {
+        checkers.add(checker);
+    }
+
+    public BaseVariantData getVariant() {
+        return variant;
+    }
+
+    public void setVariant(BaseVariantData variant) {
+        this.variant = variant;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.groovy
deleted file mode 100644
index e3645be..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.groovy
+++ /dev/null
@@ -1,37 +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.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.LibraryCache
-import com.android.builder.Version
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-
-public class PrepareLibraryTask extends DefaultAndroidTask {
-    @InputFile
-    File bundle
-
-    @OutputDirectory
-    File explodedDir
-
-    @TaskAction
-    def prepare() {
-        //LibraryCache.getCache().unzipLibrary(this.name, project, getBundle(), getExplodedDir())
-        LibraryCache.unzipAar(getBundle(), getExplodedDir(), project)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.java
new file mode 100644
index 0000000..1ac6372
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.java
@@ -0,0 +1,54 @@
+/*
+ * 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.internal.tasks;
+
+import com.android.build.gradle.internal.LibraryCache;
+
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+
+public class PrepareLibraryTask extends DefaultAndroidTask {
+    private File bundle;
+    private File explodedDir;
+
+    @TaskAction
+    public void prepare() {
+        //LibraryCache.getCache().unzipLibrary(this.name, project, getBundle(), getExplodedDir())
+        LibraryCache.unzipAar(getBundle(), getExplodedDir(), getProject());
+    }
+
+    @InputFile
+    public File getBundle() {
+        return bundle;
+    }
+
+    @OutputDirectory
+    public File getExplodedDir() {
+        return explodedDir;
+    }
+
+    public void setBundle(File bundle) {
+        this.bundle = bundle;
+    }
+
+    public void setExplodedDir(File explodedDir) {
+        this.explodedDir = explodedDir;
+    }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.groovy
deleted file mode 100644
index 5f2f0b3..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.groovy
+++ /dev/null
@@ -1,198 +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.tasks
-
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.builder.model.SigningConfig
-import com.android.ide.common.signing.CertificateInfo
-import com.android.ide.common.signing.KeystoreHelper
-import com.android.ide.common.signing.KeytoolException
-import com.google.common.collect.Maps
-import org.gradle.api.tasks.TaskAction
-import org.gradle.logging.StyledTextOutput
-import org.gradle.logging.StyledTextOutputFactory
-
-import java.security.MessageDigest
-import java.security.NoSuchAlgorithmException
-import java.security.cert.Certificate
-import java.security.cert.CertificateEncodingException
-import java.text.DateFormat
-
-import static org.gradle.logging.StyledTextOutput.Style.Description
-import static org.gradle.logging.StyledTextOutput.Style.Failure
-import static org.gradle.logging.StyledTextOutput.Style.Identifier
-import static org.gradle.logging.StyledTextOutput.Style.Normal
-
-/**
- * Report tasks displaying the signing information for all variants.
- */
-class SigningReportTask extends BaseTask {
-
-    private Set<BaseVariantData> variants = [];
-
-    @TaskAction
-    public void generate() throws IOException {
-
-        StyledTextOutput textOutput = getServices().get(
-                StyledTextOutputFactory.class).create(getClass())
-
-        Map<SigningConfig, SigningInfo> cache = Maps.newHashMap()
-
-        for (BaseVariantData variant : variants) {
-            textOutput.withStyle(Identifier).text("Variant: ")
-            textOutput.withStyle(Description).text(variant.name)
-            textOutput.println()
-
-            // get the data
-            com.android.build.gradle.internal.dsl.SigningConfig signingConfig = (com.android.build.gradle.internal.dsl.SigningConfig) variant.variantConfiguration.signingConfig
-            if (signingConfig == null) {
-                textOutput.withStyle(Identifier).text("Config: ")
-                textOutput.withStyle(Normal).text("none")
-                textOutput.println()
-            } else {
-                SigningInfo signingInfo = getSigningInfo(signingConfig, cache)
-
-
-                textOutput.withStyle(Identifier).text("Config: ")
-                textOutput.withStyle(Description).text(signingConfig.name)
-                textOutput.println()
-
-                textOutput.withStyle(Identifier).text("Store: ")
-                textOutput.withStyle(Description).text(signingConfig.getStoreFile())
-                textOutput.println()
-
-                textOutput.withStyle(Identifier).text("Alias: ")
-                textOutput.withStyle(Description).text(signingConfig.getKeyAlias())
-                textOutput.println()
-
-                if (signingInfo.isValid()) {
-                    if (signingInfo.error != null) {
-                        textOutput.withStyle(Identifier).text("Error: ")
-                        textOutput.withStyle(Failure).text(signingInfo.error)
-                        textOutput.println()
-                    } else {
-                        textOutput.withStyle(Identifier).text("MD5: ")
-                        textOutput.withStyle(Description).text(signingInfo.md5)
-                        textOutput.println()
-
-                        textOutput.withStyle(Identifier).text("SHA1: ")
-                        textOutput.withStyle(Description).text(signingInfo.sha1)
-                        textOutput.println()
-
-                        textOutput.withStyle(Identifier).text("Valid until: ")
-                        DateFormat df = DateFormat.getDateInstance(DateFormat.FULL)
-                        textOutput.withStyle(Description).text(df.format(signingInfo.notAfter))
-                        textOutput.println()
-                    }
-                }
-            }
-
-            textOutput.withStyle(Normal).text("----------")
-            textOutput.println()
-        }
-    }
-
-    /**
-     * Sets the configurations to generate the report for.
-     *
-     * @param configurations The configuration. Must not be null.
-     */
-    public void setVariants(Collection<BaseVariantData> variants) {
-        this.variants.addAll(variants);
-    }
-
-    private static SigningInfo getSigningInfo(SigningConfig signingConfig,
-                                       Map<SigningConfig, SigningInfo> cache) {
-        SigningInfo signingInfo = cache.get(signingConfig)
-
-        if (signingInfo == null) {
-            signingInfo = new SigningInfo()
-
-            if (signingConfig.isSigningReady()) {
-                try {
-                    CertificateInfo certificateInfo = KeystoreHelper.getCertificateInfo(
-                            signingConfig.getStoreType(), signingConfig.getStoreFile(),
-                            signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
-                            signingConfig.getKeyAlias())
-                    if (certificateInfo != null) {
-                        signingInfo.md5 = getFingerprint(certificateInfo.certificate, "MD5")
-                        signingInfo.sha1 = getFingerprint(certificateInfo.certificate, "SHA1")
-                        signingInfo.notAfter = certificateInfo.certificate.notAfter
-                    } else {
-
-                    }
-                } catch (KeytoolException e) {
-                    signingInfo.error = e.getMessage()
-                } catch (FileNotFoundException e) {
-                    signingInfo.error = "Missing keystore"
-                }
-            }
-
-            cache.put(signingConfig, signingInfo)
-        }
-
-        return signingInfo
-    }
-
-    private final static class SigningInfo {
-        String md5
-        String sha1
-        Date notAfter
-        String error
-
-        boolean isValid() {
-            return md5 != null || error != null
-        }
-    }
-
-    /**
-     * Returns the {@link Certificate} fingerprint as returned by <code>keytool</code>.
-     *
-     * @param certificate
-     * @param hashAlgorithm
-     */
-    public static String getFingerprint(Certificate cert, String hashAlgorithm) {
-        if (cert == null) {
-            return null;
-        }
-        try {
-            MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
-            return toHexadecimalString(digest.digest(cert.getEncoded()));
-        } catch(NoSuchAlgorithmException e) {
-            // ignore
-        } catch(CertificateEncodingException e) {
-            // ignore
-        }
-        return null;
-    }
-
-    private static String toHexadecimalString(byte[] value) {
-        StringBuilder sb = new StringBuilder();
-        int len = value.length;
-        for (int i = 0; i < len; i++) {
-            int num = ((int) value[i]) & 0xff;
-            if (num < 0x10) {
-                sb.append('0');
-            }
-            sb.append(Integer.toHexString(num));
-            if (i < len - 1) {
-                sb.append(':');
-            }
-        }
-        return sb.toString().toUpperCase(Locale.US);
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.java
new file mode 100644
index 0000000..dbb8167
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.java
@@ -0,0 +1,203 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.builder.model.SigningConfig;
+import com.android.ide.common.signing.CertificateInfo;
+import com.android.ide.common.signing.KeystoreHelper;
+import com.android.ide.common.signing.KeytoolException;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.logging.StyledTextOutput.Style.Description;
+import static org.gradle.logging.StyledTextOutput.Style.Failure;
+import static org.gradle.logging.StyledTextOutput.Style.Identifier;
+import static org.gradle.logging.StyledTextOutput.Style.Normal;
+
+/**
+ * Report tasks displaying the signing information for all variants.
+ */
+public class SigningReportTask extends DefaultTask {
+
+    private Set<BaseVariantData> variants = Sets.newHashSet();
+
+    @TaskAction
+    public void generate() throws IOException {
+
+        StyledTextOutput textOutput = getServices().get(
+                StyledTextOutputFactory.class).create(getClass());
+
+        Map<SigningConfig, SigningInfo> cache = Maps.newHashMap();
+
+        for (BaseVariantData variant : variants) {
+            textOutput.withStyle(Identifier).text("Variant: ");
+            textOutput.withStyle(Description).text(variant.getName());
+            textOutput.println();
+
+            // get the data
+            SigningConfig signingConfig = variant.getVariantConfiguration().getSigningConfig();
+            if (signingConfig == null) {
+                textOutput.withStyle(Identifier).text("Config: ");
+                textOutput.withStyle(Normal).text("none");
+                textOutput.println();
+            } else {
+                SigningInfo signingInfo = getSigningInfo(signingConfig, cache);
+
+
+                textOutput.withStyle(Identifier).text("Config: ");
+                textOutput.withStyle(Description).text(signingConfig.getName());
+                textOutput.println();
+
+                textOutput.withStyle(Identifier).text("Store: ");
+                textOutput.withStyle(Description).text(signingConfig.getStoreFile());
+                textOutput.println();
+
+                textOutput.withStyle(Identifier).text("Alias: ");
+                textOutput.withStyle(Description).text(signingConfig.getKeyAlias());
+                textOutput.println();
+
+                if (signingInfo.isValid()) {
+                    if (signingInfo.error != null) {
+                        textOutput.withStyle(Identifier).text("Error: ");
+                        textOutput.withStyle(Failure).text(signingInfo.error);
+                        textOutput.println();
+                    } else {
+                        textOutput.withStyle(Identifier).text("MD5: ");
+                        textOutput.withStyle(Description).text(signingInfo.md5);
+                        textOutput.println();
+
+                        textOutput.withStyle(Identifier).text("SHA1: ");
+                        textOutput.withStyle(Description).text(signingInfo.sha1);
+                        textOutput.println();
+
+                        textOutput.withStyle(Identifier).text("Valid until: ");
+                        DateFormat df = DateFormat.getDateInstance(DateFormat.FULL);
+                        textOutput.withStyle(Description).text(df.format(signingInfo.notAfter));
+                        textOutput.println();
+                    }
+                }
+            }
+
+            textOutput.withStyle(Normal).text("----------");
+            textOutput.println();
+        }
+    }
+
+    /**
+     * Sets the configurations to generate the report for.
+     */
+    public void setVariants(@NonNull Collection<? extends BaseVariantData> variants) {
+        this.variants.addAll(variants);
+    }
+
+    private static SigningInfo getSigningInfo(
+            @NonNull SigningConfig signingConfig,
+            @NonNull Map<SigningConfig, SigningInfo> cache) {
+        SigningInfo signingInfo = cache.get(signingConfig);
+
+        if (signingInfo == null) {
+            signingInfo = new SigningInfo();
+
+            if (signingConfig.isSigningReady()) {
+                try {
+                    CertificateInfo certificateInfo = KeystoreHelper.getCertificateInfo(
+                            signingConfig.getStoreType(), signingConfig.getStoreFile(),
+                            signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
+                            signingConfig.getKeyAlias());
+                    if (certificateInfo != null) {
+                        signingInfo.md5 = getFingerprint(certificateInfo.getCertificate(), "MD5");
+                        signingInfo.sha1 = getFingerprint(certificateInfo.getCertificate(), "SHA1");
+                        signingInfo.notAfter = certificateInfo.getCertificate().getNotAfter();
+                    }
+                } catch (KeytoolException e) {
+                    signingInfo.error = e.getMessage();
+                } catch (FileNotFoundException e) {
+                    signingInfo.error = "Missing keystore";
+                }
+            }
+
+            cache.put(signingConfig, signingInfo);
+        }
+
+        return signingInfo;
+    }
+
+    private static final class SigningInfo {
+        String md5;
+        String sha1;
+        Date notAfter;
+        String error;
+
+        boolean isValid() {
+            return md5 != null || error != null;
+        }
+    }
+
+    /**
+     * Returns the {@link Certificate} fingerprint as returned by <code>keytool</code>.
+     */
+    public static String getFingerprint(Certificate cert, String hashAlgorithm) {
+        if (cert == null) {
+            return null;
+        }
+        try {
+            MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
+            return toHexadecimalString(digest.digest(cert.getEncoded()));
+        } catch(NoSuchAlgorithmException e) {
+            // ignore
+        } catch(CertificateEncodingException e) {
+            // ignore
+        }
+        return null;
+    }
+
+    private static String toHexadecimalString(byte[] value) {
+        StringBuilder sb = new StringBuilder();
+        int len = value.length;
+        for (int i = 0; i < len; i++) {
+            int num = ((int) value[i]) & 0xff;
+            if (num < 0x10) {
+                sb.append('0');
+            }
+            sb.append(Integer.toHexString(num));
+            if (i < len - 1) {
+                sb.append(':');
+            }
+        }
+        return sb.toString().toUpperCase(Locale.US);
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java
index 112bd0a..c242e39 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java
@@ -16,13 +16,14 @@
 
 package com.android.build.gradle.internal.tasks;
 
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.api.AndroidSourceDirectorySet;
 import com.android.build.gradle.api.AndroidSourceSet;
 import com.android.builder.core.VariantType;
 import com.google.common.collect.Lists;
 
 import org.gradle.api.Project;
+import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.diagnostics.AbstractReportTask;
 import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
 import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer;
@@ -40,16 +41,26 @@
 
     private final TextReportRenderer mRenderer = new TextReportRenderer();
 
+    private AndroidConfig config;
+
     @Override
     protected ReportRenderer getRenderer() {
         return mRenderer;
     }
 
+    @Input
+    public AndroidConfig getConfig() {
+        return config;
+    }
+
+    public void setConfig(AndroidConfig config) {
+        this.config = config;
+    }
+
     @Override
     protected void generate(Project project) throws IOException {
-        BaseExtension extension = project.getExtensions().findByType(BaseExtension.class);
-        if (extension != null) {
-            for (AndroidSourceSet sourceSet : extension.getSourceSets()) {
+        if (config != null) {
+            for (AndroidSourceSet sourceSet : config.getSourceSets()) {
                 mRenderer.getBuilder().subheading(sourceSet.getName());
 
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.groovy
deleted file mode 100644
index a1d0841..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.groovy
+++ /dev/null
@@ -1,45 +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.tasks
-
-import com.android.builder.testing.api.TestServer
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-
-/**
- * Task sending APKs out to a {@link TestServer}
- */
-public class TestServerTask extends DefaultAndroidTask {
-
-    @InputFile
-    File testApk
-
-    @InputFile @Optional
-    File testedApk
-
-    @Input
-    String variantName
-
-    TestServer testServer
-
-    @TaskAction
-    void sendToServer() {
-        testServer.uploadApks(getVariantName(), getTestApk(), getTestedApk())
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.java
new file mode 100644
index 0000000..9e248e4
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.java
@@ -0,0 +1,79 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.builder.testing.api.TestServer;
+import com.google.common.base.Preconditions;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+
+/**
+ * Task sending APKs out to a {@link TestServer}
+ */
+public class TestServerTask extends DefaultAndroidTask {
+
+    private File testApk;
+
+    File testedApk;
+
+    TestServer testServer;
+
+    @TaskAction
+    public void sendToServer() {
+        testServer.uploadApks(getVariantName(), getTestApk(), getTestedApk());
+    }
+
+    @InputFile
+    public File getTestApk() {
+        return testApk;
+    }
+
+    public void setTestApk(File testApk) {
+        this.testApk = testApk;
+    }
+
+    @InputFile @Optional
+    public File getTestedApk() {
+        return testedApk;
+    }
+
+    public void setTestedApk(File testedApk) {
+        this.testedApk = testedApk;
+    }
+
+    @NonNull
+    @Override
+    @Input
+    public String getVariantName() {
+        return Preconditions.checkNotNull(super.getVariantName(),
+                "Test server task must have a variant name.");
+    }
+
+    public TestServer getTestServer() {
+        return testServer;
+    }
+
+    public void setTestServer(TestServer testServer) {
+        this.testServer = testServer;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java
index e83b8db..73adc95 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java
@@ -141,6 +141,7 @@
             uninstallTask.setGroup(TaskManager.INSTALL_GROUP);
             uninstallTask.setVariant(scope.getVariantData());
             uninstallTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            uninstallTask.setVariantName(scope.getVariantConfiguration().getFullName());
             uninstallTask.setTimeOutInMs(
                     scope.getGlobalScope().getExtension().getAdbOptions().getTimeOutInMs());
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.groovy
deleted file mode 100644
index feabc35..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.groovy
+++ /dev/null
@@ -1,71 +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.tasks
-
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.builder.model.SigningConfig
-import com.android.ide.common.signing.KeystoreHelper
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-import org.gradle.tooling.BuildException
-
-/**
- * A validate task that creates the debug keystore if it's missing.
- * It only creates it if it's in the default debug keystore location.
- *
- * It's linked to a given SigningConfig
- *
- */
-class ValidateSigningTask extends BaseTask {
-
-    SigningConfig signingConfig
-
-    /**
-     * Annotated getter for task input.
-     *
-     * This is an Input and not an InputFile because the file might not exist.
-     * This is not actually used by the task, this is only for Gradle to check inputs.
-     *
-     * @return the path of the keystore.
-     */
-    @Input @Optional
-    String getStoreLocation() {
-        File f = signingConfig.getStoreFile()
-        if (f != null) {
-            return f.absolutePath
-        }
-        return null;
-    }
-
-    @TaskAction
-    void validate() {
-
-        File storeFile = signingConfig.getStoreFile()
-        if (storeFile != null && !storeFile.exists()) {
-            if (KeystoreHelper.defaultDebugKeystoreLocation().equals(storeFile.absolutePath)) {
-                getLogger().info("Creating default debug keystore at %s" + storeFile.absolutePath)
-                if (!KeystoreHelper.createDebugStore(
-                        signingConfig.getStoreType(), signingConfig.getStoreFile(),
-                        signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
-                        signingConfig.getKeyAlias(), getILogger())) {
-                    throw new BuildException("Unable to recreate missing debug keystore.", null);
-                }
-            }
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.java
new file mode 100644
index 0000000..0c3c898
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.java
@@ -0,0 +1,100 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkState;
+
+import com.android.builder.model.SigningConfig;
+import com.android.ide.common.signing.KeystoreHelper;
+import com.android.ide.common.signing.KeytoolException;
+import com.android.prefs.AndroidLocation;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.tooling.BuildException;
+
+import java.io.File;
+
+/**
+ * A validate task that creates the debug keystore if it's missing.
+ * It only creates it if it's in the default debug keystore location.
+ *
+ * It's linked to a given SigningConfig
+ *
+ */
+public class ValidateSigningTask extends BaseTask {
+
+    private SigningConfig signingConfig;
+
+    public void setSigningConfig(SigningConfig signingConfig) {
+        this.signingConfig = signingConfig;
+    }
+
+    public SigningConfig getSigningConfig() {
+        return signingConfig;
+    }
+
+    /**
+     * Annotated getter for task input.
+     *
+     * This is an Input and not an InputFile because the file might not exist.
+     * This is not actually used by the task, this is only for Gradle to check inputs.
+     *
+     * @return the path of the keystore.
+     */
+    @Input @Optional
+    public String getStoreLocation() {
+        File f = signingConfig.getStoreFile();
+        if (f != null) {
+            return f.getAbsolutePath();
+        }
+        return null;
+    }
+
+    @TaskAction
+    public void validate() throws AndroidLocation.AndroidLocationException, KeytoolException {
+        File storeFile = signingConfig.getStoreFile();
+        if (storeFile == null) {
+            throw new IllegalArgumentException(
+                    "Keystore file not set for signing config " + signingConfig.getName());
+        }
+        if (!storeFile.exists()) {
+            if (KeystoreHelper.defaultDebugKeystoreLocation().equals(storeFile.getAbsolutePath())) {
+                checkState(signingConfig.isSigningReady(), "Debug signing config not ready.");
+
+                getLogger().info(
+                        "Creating default debug keystore at {}",
+                        storeFile.getAbsolutePath());
+
+                //noinspection ConstantConditions - isSigningReady() called above
+                if (!KeystoreHelper.createDebugStore(
+                        signingConfig.getStoreType(), signingConfig.getStoreFile(),
+                        signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
+                        signingConfig.getKeyAlias(), getILogger())) {
+                    throw new BuildException("Unable to recreate missing debug keystore.", null);
+                }
+            } else {
+                throw new IllegalArgumentException(
+                        String.format(
+                                "Keystore file %s not found for signing config '%s'.",
+                                storeFile.getAbsolutePath(),
+                                signingConfig.getName()));
+            }
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy
index 122960c..c5bf7a4 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy
@@ -18,22 +18,20 @@
 
 package com.android.build.gradle.internal.tasks.multidex
 
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
 import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.builder.model.AndroidProject
 import com.google.common.base.Charsets
 import com.google.common.base.Joiner
 import com.google.common.io.Files
-import org.gradle.api.Task
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.TaskAction
 
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
+import java.util.concurrent.Callable
 
 /**
  * Task to create the main (non-obfuscated) list of classes to keep.
@@ -106,11 +104,11 @@
 
         VariantScope scope;
 
-        private Closure<List<File>> inputFiles
+        private Callable<List<File>> inputFiles
 
-        ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+        ConfigAction(VariantScope scope, PostCompilationData pcData) {
             this.scope = scope
-            inputFiles = pcData.inputFiles;
+            inputFiles = pcData.inputFilesCallable;
         }
 
         @Override
@@ -126,9 +124,10 @@
         @Override
         void execute(CreateMainDexList createMainDexList) {
             createMainDexList.androidBuilder = scope.globalScope.androidBuilder
+            createMainDexList.setVariantName(scope.getVariantConfiguration().getFullName())
 
             def files = inputFiles
-            createMainDexList.allClassesJarFile = files().first()
+            createMainDexList.allClassesJarFile = files.call().first()
             ConventionMappingHelper.map(createMainDexList, "componentsJarFile") {
                 scope.getProguardComponentsJarFile()
             }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy
index b4dbd8c..894352f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy
@@ -18,7 +18,7 @@
 
 package com.android.build.gradle.internal.tasks.multidex
 
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
@@ -36,6 +36,7 @@
 import javax.xml.parsers.SAXParser
 import javax.xml.parsers.SAXParserFactory
 
+
 class CreateManifestKeepList extends DefaultAndroidTask {
 
     @InputFile
@@ -120,9 +121,9 @@
     public static class ConfigAction implements TaskConfigAction<CreateManifestKeepList> {
 
         VariantScope scope;
-        TaskManager.PostCompilationData pcData;
+        PostCompilationData pcData;
 
-        ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+        ConfigAction(VariantScope scope, PostCompilationData pcData) {
             this.scope = scope
             this.pcData = pcData
         }
@@ -139,6 +140,8 @@
 
         @Override
         void execute(CreateManifestKeepList manifestKeepListTask) {
+            manifestKeepListTask.setVariantName(scope.getVariantConfiguration().getFullName())
+
             // since all the output have the same manifest, besides the versionCode,
             // we can take any of the output and use that.
             final BaseVariantOutputData output = scope.variantData.outputs.get(0)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy
index 78b968f..0a81415 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-
-
 package com.android.build.gradle.internal.tasks.multidex
 
 import com.android.build.gradle.internal.tasks.DefaultAndroidTask
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
@@ -32,6 +30,7 @@
 import org.gradle.api.tasks.OutputFile
 import org.gradle.api.tasks.TaskAction
 
+import java.util.concurrent.Callable
 import java.util.jar.JarEntry
 import java.util.jar.JarOutputStream
 import java.util.zip.ZipEntry
@@ -161,14 +160,14 @@
 
         private VariantScope scope
 
-        private Closure<File> inputDir;
+        private Callable<File> inputDir;
 
-        private Closure<List<File>> inputLibraries;
+        private Callable<List<File>> inputLibraries;
 
-        ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+        ConfigAction(VariantScope scope, PostCompilationData pcData) {
             this.scope = scope
-            inputDir = pcData.inputDir
-            inputLibraries = pcData.inputLibraries
+            inputDir = pcData.inputDirCallable
+            inputLibraries = pcData.inputLibrariesCallable
         }
 
         @Override
@@ -183,6 +182,7 @@
 
         @Override
         void execute(JarMergingTask jarMergingTask) {
+            jarMergingTask.setVariantName(scope.getVariantConfiguration().getFullName())
             ConventionMappingHelper.map(jarMergingTask, "inputJars", inputLibraries)
             ConventionMappingHelper.map(jarMergingTask, "inputDir", inputDir)
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy
index 1e46d97..23fdade 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy
@@ -18,7 +18,7 @@
 
 package com.android.build.gradle.internal.tasks.multidex
 
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
@@ -128,9 +128,9 @@
 
         VariantScope scope;
 
-        TaskManager.PostCompilationData pcData;
+        PostCompilationData pcData;
 
-        ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+        ConfigAction(VariantScope scope, PostCompilationData pcData) {
             this.scope = scope
             this.pcData = pcData
         }
@@ -147,6 +147,7 @@
 
         @Override
         void execute(RetraceMainDexList retraceTask) {
+            retraceTask.setVariantName(scope.getVariantConfiguration().getFullName())
             ConventionMappingHelper.map(retraceTask, "mainDexListFile") { scope.getMainDexListFile() }
             ConventionMappingHelper.map(retraceTask, "mappingFile") {
                 scope.variantData.getMappingFile()
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java
index b602863..8bada32 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java
@@ -16,12 +16,17 @@
 
 package com.android.build.gradle.internal.test;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.android.annotations.NonNull;
 import com.android.builder.core.VariantConfiguration;
 import com.android.builder.model.ApiVersion;
 import com.android.builder.testing.TestData;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * Common implementation of {@link TestData} for embedded test projects (in androidTest folder)
@@ -30,10 +35,14 @@
 public abstract class AbstractTestDataImpl implements TestData {
 
     @NonNull
-    private final VariantConfiguration testVariantConfig;
+    private final VariantConfiguration<?, ?, ?> testVariantConfig;
 
-    public AbstractTestDataImpl(@NonNull VariantConfiguration testVariantConfig) {
-        this.testVariantConfig = testVariantConfig;
+    @NonNull
+    private Map<String, String> extraInstrumentationTestRunnerArgs;
+
+    public AbstractTestDataImpl(@NonNull VariantConfiguration<?, ?, ?> testVariantConfig) {
+        this.testVariantConfig = checkNotNull(testVariantConfig);
+        this.extraInstrumentationTestRunnerArgs = Maps.newHashMap();
     }
 
     @NonNull
@@ -42,6 +51,21 @@
         return testVariantConfig.getInstrumentationRunner();
     }
 
+    @NonNull
+    @Override
+    public Map<String, String> getInstrumentationRunnerArguments() {
+        return ImmutableMap.<String, String>builder()
+                .putAll(testVariantConfig.getInstrumentationRunnerArguments())
+                .putAll(extraInstrumentationTestRunnerArgs)
+                .build();
+    }
+
+    public void setExtraInstrumentationTestRunnerArgs(
+            @NonNull Map<String, String> extraInstrumentationTestRunnerArgs) {
+        this.extraInstrumentationTestRunnerArgs =
+                ImmutableMap.copyOf(extraInstrumentationTestRunnerArgs);
+    }
+
     @Override
     public boolean isTestCoverageEnabled() {
         return testVariantConfig.isTestCoverageEnabled();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java
index 02304fe..11e9b6f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java
@@ -29,16 +29,13 @@
 import com.android.builder.testing.TestData;
 import com.android.builder.testing.api.DeviceConfigProvider;
 import com.android.ide.common.build.SplitOutputMatcher;
-import com.android.ide.common.build.SplitSelectTool;
 import com.android.ide.common.process.ProcessException;
 import com.android.ide.common.process.ProcessExecutor;
 import com.android.sdklib.BuildToolInfo;
 import com.android.utils.ILogger;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
index 077e3cd..28edd3e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
@@ -18,7 +18,7 @@
 import com.android.annotations.NonNull;
 import com.android.build.FilterData;
 import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.tasks.Dex;
@@ -39,10 +39,10 @@
     public DefaultTask uninstallTask;
 
     protected ApkVariantData(
-            @NonNull BaseExtension baseExtension,
+            @NonNull AndroidConfig androidConfig,
             @NonNull TaskManager taskManager,
             @NonNull GradleVariantConfiguration config) {
-        super(baseExtension, taskManager, config);
+        super(androidConfig, taskManager, config);
     }
 
     @Override
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
index ef68abb..99251f1 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
@@ -17,7 +17,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.builder.core.VariantType;
@@ -35,10 +35,10 @@
     private Set<String> compatibleScreens = null;
 
     public ApplicationVariantData(
-            @NonNull BaseExtension baseExtension,
+            @NonNull AndroidConfig androidConfig,
             @NonNull GradleVariantConfiguration config,
             @NonNull TaskManager taskManager) {
-        super(baseExtension, taskManager, config);
+        super(androidConfig, taskManager, config);
         testVariants = Maps.newEnumMap(VariantType.class);
     }
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java
index f638ea3..7b42dd1 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java
@@ -16,18 +16,16 @@
 
 package com.android.build.gradle.internal.variant;
 
+import static com.android.build.OutputFile.NO_FILTER;
 import static com.android.builder.core.BuilderConstants.DEBUG;
 import static com.android.builder.core.BuilderConstants.RELEASE;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 import com.android.build.FilterData;
 import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.api.ApplicationVariant;
 import com.android.build.gradle.api.BaseVariantOutput;
-import com.android.build.gradle.internal.BuildTypeData;
-import com.android.build.gradle.internal.ProductFlavorData;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.VariantModel;
 import com.android.build.gradle.internal.api.ApkVariantImpl;
@@ -36,7 +34,6 @@
 import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
 import com.android.build.gradle.internal.dsl.ProductFlavor;
 import com.android.build.gradle.internal.dsl.SigningConfig;
 import com.android.build.gradle.internal.model.FilterDataImpl;
@@ -49,6 +46,7 @@
 import org.gradle.api.Project;
 import org.gradle.internal.reflect.Instantiator;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -63,14 +61,14 @@
 
     Instantiator instantiator;
     @NonNull
-    protected final BaseExtension extension;
+    protected final AndroidConfig extension;
     @NonNull
     private final AndroidBuilder androidBuilder;
 
     public ApplicationVariantFactory(
             @NonNull Instantiator instantiator,
             @NonNull AndroidBuilder androidBuilder,
-            @NonNull BaseExtension extension) {
+            @NonNull AndroidConfig extension) {
         this.instantiator = instantiator;
         this.androidBuilder = androidBuilder;
         this.extension = extension;
@@ -80,23 +78,41 @@
     @NonNull
     public BaseVariantData createVariantData(
             @NonNull GradleVariantConfiguration variantConfiguration,
-            @NonNull Set<String> densities,
-            @NonNull Set<String> abis,
-            @NonNull Set<String> compatibleScreens,
             @NonNull TaskManager taskManager) {
         ApplicationVariantData variant =
                 new ApplicationVariantData(extension, variantConfiguration, taskManager);
 
+        variant.calculateFilters(extension.getSplits());
+
+        Set<String> densities = variant.getFilters(OutputFile.FilterType.DENSITY);
+        Set<String> abis = variant.getFilters(OutputFile.FilterType.ABI);
+
         if (!densities.isEmpty()) {
-            variant.setCompatibleScreens(compatibleScreens);
+            variant.setCompatibleScreens(extension.getSplits().getDensity()
+                    .getCompatibleScreens());
         }
 
         // create its outputs
         if (variant.getSplitHandlingPolicy() ==
                 BaseVariantData.SplitHandlingPolicy.PRE_21_POLICY) {
+
+            // Always dd an entry with no filter for universal and add it FIRST,
+            // since code assume that the first variant output will be the universal one.
+            List<String> orderedDensities = new ArrayList<String>();
+            orderedDensities.add(NO_FILTER);
+            orderedDensities.addAll(densities);
+
+            List<String> orderedAbis = new ArrayList<String>();
+            // if the abi list is empty or we must generate a universal apk, add a NO_FILTER
+            if (abis.isEmpty() || (extension.getSplits().getAbi().isEnable() &&
+                    extension.getSplits().getAbi().isUniversalApk())) {
+                orderedAbis.add(NO_FILTER);
+            }
+            orderedAbis.addAll(abis);
+
             // create its outputs
-            for (String density : densities) {
-                for (String abi : abis) {
+            for (String density : orderedDensities) {
+                for (String abi : orderedAbis) {
                     ImmutableList.Builder<FilterData> builder = ImmutableList.builder();
                     if (density != null) {
                         builder.add(FilterDataImpl.build(OutputFile.DENSITY, density));
@@ -183,7 +199,7 @@
     @Override
     public void createDefaultComponents(
             @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
             @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
         // must create signing config first so that build type 'debug' can be initialized
         // with the debug signing config.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
index 222ac06..d154358 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
@@ -19,7 +19,7 @@
 import com.android.annotations.Nullable;
 import com.android.build.FilterData;
 import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.api.AndroidSourceSet;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
@@ -35,8 +35,8 @@
 import com.android.build.gradle.tasks.AidlCompile;
 import com.android.build.gradle.tasks.BinaryFileProviderTask;
 import com.android.build.gradle.tasks.GenerateBuildConfig;
-import com.android.build.gradle.tasks.PreprocessResourcesTask;
 import com.android.build.gradle.tasks.GenerateResValues;
+import com.android.build.gradle.tasks.JackTask;
 import com.android.build.gradle.tasks.MergeAssets;
 import com.android.build.gradle.tasks.MergeResources;
 import com.android.build.gradle.tasks.NdkCompile;
@@ -52,8 +52,10 @@
 import org.gradle.api.Task;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
 import org.gradle.api.tasks.bundling.Jar;
 import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -84,7 +86,7 @@
 
 
     @NonNull
-    protected final BaseExtension baseExtension;
+    protected final AndroidConfig androidConfig;
     @NonNull
     protected final TaskManager taskManager;
     @NonNull
@@ -114,13 +116,13 @@
     public Copy copyApkTask;
     public GenerateApkDataTask generateApkDataTask;
 
-    public PreprocessResourcesTask preprocessResourcesTask;
-
-    public Copy processJavaResourcesTask;
+    public Sync processJavaResourcesTask;
     public NdkCompile ndkCompileTask;
 
-    // can be JavaCompile or JackTask depending on user's settings.
-    public AbstractCompile javaCompileTask;
+    /** Can be JavaCompile or JackTask depending on user's settings. */
+    public AbstractCompile javaCompilerTask;
+    public JavaCompile javacTask;
+    public JackTask jackTask;
     public Jar classesJarTask;
     // empty anchor compile task to set all compilations tasks as dependents.
     public Task compileTask;
@@ -131,7 +133,6 @@
 
     // TODO : why is Jack not registered as the obfuscationTask ???
     public Task obfuscationTask;
-    public File obfuscatedClassesJar;
 
     // Task to assemble the variant and all its output.
     public Task assembleVariantTask;
@@ -157,22 +158,22 @@
 
 
     public BaseVariantData(
-            @NonNull BaseExtension baseExtension,
+            @NonNull AndroidConfig androidConfig,
             @NonNull TaskManager taskManager,
             @NonNull GradleVariantConfiguration variantConfiguration) {
-        this.baseExtension = baseExtension;
+        this.androidConfig = androidConfig;
         this.variantConfiguration = variantConfiguration;
         this.taskManager = taskManager;
 
         // eventually, this will require a more open ended comparison.
         mSplitHandlingPolicy =
-                baseExtension.getGeneratePureSplits()
+                androidConfig.getGeneratePureSplits()
                     && variantConfiguration.getMinSdkVersion().getApiLevel() >= 21
                     ? SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY
                     : SplitHandlingPolicy.PRE_21_POLICY;
 
         // warn the user in case we are forced to ignore the generatePureSplits flag.
-        if (baseExtension.getGeneratePureSplits()
+        if (androidConfig.getGeneratePureSplits()
                 && mSplitHandlingPolicy != SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
             Logging.getLogger(BaseVariantData.class).warn(
                     String.format("Variant %s, MinSdkVersion %s is too low (<21) "
@@ -180,9 +181,8 @@
                             variantConfiguration.getFullName(),
                             variantConfiguration.getMinSdkVersion().getApiLevel()));
         }
-        variantConfiguration.checkSourceProviders();
-
         scope = new VariantScope(taskManager.getGlobalScope(), this);
+        taskManager.configureScopeForNdk(scope);
     }
 
 
@@ -289,7 +289,12 @@
         sourceGenTask.dependsOn(task);
 
         for (File f : generatedSourceFolders) {
-            javaCompileTask.source(f);
+            javacTask.source(f);
+
+            // Jack task is not always created.
+            if (jackTask != null) {
+                jackTask.source(f);
+            }
         }
 
         addJavaSourceFoldersToModel(generatedSourceFolders);
@@ -299,7 +304,12 @@
         sourceGenTask.dependsOn(task);
 
         for (File f : generatedSourceFolders) {
-            javaCompileTask.source(f);
+            javacTask.source(f);
+
+            // Jack task is not always created.
+            if (jackTask != null) {
+                jackTask.source(f);
+            }
         }
 
         addJavaSourceFoldersToModel(generatedSourceFolders);
@@ -487,7 +497,7 @@
         if (filterType.isAuto(splits)) {
             filtersList.addAll(getAllFilters(resourceSets, filterType.folderPrefix));
         } else {
-            filtersList.addAll(removeAllNullEntries(filterType.getConfiguredFilters(splits)));
+            filtersList.addAll(filterType.getConfiguredFilters(splits));
         }
         return filtersList;
     }
@@ -638,23 +648,6 @@
     }
 
     @NonNull
-    public File getFinalResourcesDir() {
-        return preprocessResourcesTask != null
-                ? preprocessResourcesTask.getOutputResDirectory()
-                : mergeResourcesTask.getOutputDir();
-    }
-
-    private static <T> Set<T> removeAllNullEntries(Collection<T> input) {
-        HashSet<T> output = new HashSet<T>();
-        for (T element : input) {
-            if (element != null) {
-                output.add(element);
-            }
-        }
-        return output;
-    }
-
-    @NonNull
     public VariantScope getScope() {
         return scope;
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java
index e0c47c8..4c78e6a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java
@@ -74,7 +74,7 @@
         scope = new VariantOutputScope(variantData.getScope(), this);
     }
 
-    @Nullable
+    @NonNull
     @Override
     public ApkOutputFile getMainOutputFile() {
         return mainApkOutputFile;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
index 7b573cd..2aeb1d6 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
@@ -19,7 +19,7 @@
 import com.android.annotations.Nullable;
 import com.android.build.FilterData;
 import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.tasks.ExtractAnnotations;
@@ -44,10 +44,10 @@
     public ExtractAnnotations generateAnnotationsTask = null;
 
     public LibraryVariantData(
-            @NonNull BaseExtension baseExtension,
+            @NonNull AndroidConfig androidConfig,
             @NonNull TaskManager taskManager,
             @NonNull GradleVariantConfiguration config) {
-        super(baseExtension, taskManager, config);
+        super(androidConfig, taskManager, config);
         testVariants = Maps.newEnumMap(VariantType.class);
 
         // create default output
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.groovy
deleted file mode 100644
index e94c33d..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.groovy
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2014 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.annotations.Nullable
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.api.BaseVariantOutput
-import com.android.build.gradle.api.LibraryVariant
-import com.android.build.gradle.internal.BuildTypeData
-import com.android.build.gradle.internal.ProductFlavorData
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantModel
-import com.android.build.gradle.internal.api.LibraryVariantImpl
-import com.android.build.gradle.internal.api.LibraryVariantOutputImpl
-import com.android.build.gradle.internal.api.ReadOnlyObjectProvider
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.ProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.VariantType
-import com.google.common.collect.Lists
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-import static com.android.builder.core.BuilderConstants.RELEASE
-
-public class LibraryVariantFactory implements VariantFactory {
-
-    @NonNull
-    Instantiator instantiator
-    @NonNull
-    private final LibraryExtension extension
-    @NonNull
-    private final AndroidBuilder androidBuilder;
-
-    public LibraryVariantFactory(
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull LibraryExtension extension) {
-        this.instantiator = instantiator;
-        this.androidBuilder = androidBuilder;
-        this.extension = extension;
-    }
-
-    @Override
-    @NonNull
-    public BaseVariantData createVariantData(
-            @NonNull GradleVariantConfiguration variantConfiguration,
-            @NonNull Set<String> densities,
-            @NonNull Set<String> abis,
-            @NonNull Set<String> compatibleScreens,
-            @NonNull TaskManager taskManager) {
-        return new LibraryVariantData(extension, taskManager, variantConfiguration)
-    }
-
-    @Override
-    @NonNull
-    public LibraryVariant createVariantApi(
-            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
-            @NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
-        LibraryVariantImpl variant = instantiator.newInstance(
-                LibraryVariantImpl.class, variantData, androidBuilder, readOnlyObjectProvider)
-
-        // now create the output objects
-        List<? extends BaseVariantOutputData> outputList = variantData.getOutputs();
-        List<BaseVariantOutput> apiOutputList = Lists.newArrayListWithCapacity(outputList.size());
-
-        for (BaseVariantOutputData variantOutputData : outputList) {
-            LibVariantOutputData libOutput = (LibVariantOutputData) variantOutputData;
-
-            LibraryVariantOutputImpl output = instantiator.newInstance(
-                    LibraryVariantOutputImpl.class, libOutput);
-
-            apiOutputList.add(output);
-        }
-
-        variant.addOutputs(apiOutputList);
-
-        return variant
-    }
-
-    @NonNull
-    @Override
-    public VariantType getVariantConfigurationType() {
-        return VariantType.LIBRARY
-    }
-
-    @Override
-    boolean isLibrary() {
-        return true
-    }
-
-    @Override
-    boolean hasTestScope() {
-        return true
-    }
-
-    /***
-     * Prevent customization of applicationId or applicationIdSuffix.
-     */
-    @Override
-    public void validateModel(@NonNull VariantModel model) {
-        if (model.getDefaultConfig().getProductFlavor().getApplicationId() != null) {
-            throw new GradleException("Library projects cannot set applicationId. " +
-                    "applicationId is set to '" +
-                    model.getDefaultConfig().getProductFlavor().getApplicationId() +
-                    "' in default config.");
-        }
-
-        for (BuildTypeData buildType : model.getBuildTypes().values()) {
-            if (buildType.getBuildType().getApplicationIdSuffix() != null) {
-                throw new GradleException("Library projects cannot set applicationId. " +
-                        "applicationIdSuffix is set to '" +
-                        buildType.getBuildType().getApplicationIdSuffix() +
-                        "' in build type '" + buildType.getBuildType().getName() + "'.");
-            }
-        }
-        for (ProductFlavorData productFlavor : model.getProductFlavors().values()) {
-            if (productFlavor.getProductFlavor().getApplicationId() != null) {
-                throw new GradleException("Library projects cannot set applicationId. " +
-                        "applicationId is set to '" +
-                        productFlavor.getProductFlavor().getApplicationId() + "' in flavor '" +
-                        productFlavor.getProductFlavor().getName() + "'.");
-            }
-        }
-
-    }
-
-    @Override
-    void preVariantWork(Project project) {
-        // nothing to be done here.
-    }
-
-    @Override
-    public void createDefaultComponents(
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
-        // must create signing config first so that build type 'debug' can be initialized
-        // with the debug signing config.
-        signingConfigs.create(DEBUG);
-        buildTypes.create(DEBUG);
-        buildTypes.create(RELEASE);
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.java
new file mode 100644
index 0000000..636c1d0
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 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.build.gradle.AndroidConfig;
+import com.android.build.gradle.api.BaseVariantOutput;
+import com.android.build.gradle.api.LibraryVariant;
+import com.android.build.gradle.internal.BuildTypeData;
+import com.android.build.gradle.internal.ProductFlavorData;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantModel;
+import com.android.build.gradle.internal.api.LibraryVariantImpl;
+import com.android.build.gradle.internal.api.LibraryVariantOutputImpl;
+import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantType;
+import com.google.common.collect.Lists;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.builder.core.BuilderConstants.DEBUG;
+import static com.android.builder.core.BuilderConstants.RELEASE;
+
+import java.util.List;
+
+public class LibraryVariantFactory implements VariantFactory {
+
+    @NonNull
+    private Instantiator instantiator;
+    @NonNull
+    private final AndroidConfig extension;
+    @NonNull
+    private final AndroidBuilder androidBuilder;
+
+    public LibraryVariantFactory(
+            @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder,
+            @NonNull AndroidConfig extension) {
+        this.instantiator = instantiator;
+        this.androidBuilder = androidBuilder;
+        this.extension = extension;
+    }
+
+    @Override
+    @NonNull
+    public BaseVariantData createVariantData(
+            @NonNull GradleVariantConfiguration variantConfiguration,
+            @NonNull TaskManager taskManager) {
+        return new LibraryVariantData(extension, taskManager, variantConfiguration);
+    }
+
+    @Override
+    @NonNull
+    public LibraryVariant createVariantApi(
+            @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
+            @NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
+        LibraryVariantImpl variant = instantiator.newInstance(
+                LibraryVariantImpl.class, variantData, androidBuilder, readOnlyObjectProvider);
+
+        // now create the output objects
+        List<? extends BaseVariantOutputData> outputList = variantData.getOutputs();
+        List<BaseVariantOutput> apiOutputList = Lists.newArrayListWithCapacity(outputList.size());
+
+        for (BaseVariantOutputData variantOutputData : outputList) {
+            LibVariantOutputData libOutput = (LibVariantOutputData) variantOutputData;
+
+            LibraryVariantOutputImpl output = instantiator.newInstance(
+                    LibraryVariantOutputImpl.class, libOutput);
+
+            apiOutputList.add(output);
+        }
+
+        variant.addOutputs(apiOutputList);
+
+        return variant;
+    }
+
+    @NonNull
+    @Override
+    public VariantType getVariantConfigurationType() {
+        return VariantType.LIBRARY;
+    }
+
+    @Override
+    public boolean isLibrary() {
+        return true;
+    }
+
+    @Override
+    public boolean hasTestScope() {
+        return true;
+    }
+
+    /***
+     * Prevent customization of applicationId or applicationIdSuffix.
+     */
+    @Override
+    public void validateModel(@NonNull VariantModel model) {
+        if (model.getDefaultConfig().getProductFlavor().getApplicationId() != null) {
+            throw new GradleException("Library projects cannot set applicationId. " +
+                    "applicationId is set to '" +
+                    model.getDefaultConfig().getProductFlavor().getApplicationId() +
+                    "' in default config.");
+        }
+
+        for (BuildTypeData buildType : model.getBuildTypes().values()) {
+            if (buildType.getBuildType().getApplicationIdSuffix() != null) {
+                throw new GradleException("Library projects cannot set applicationId. " +
+                        "applicationIdSuffix is set to '" +
+                        buildType.getBuildType().getApplicationIdSuffix() +
+                        "' in build type '" + buildType.getBuildType().getName() + "'.");
+            }
+        }
+        for (ProductFlavorData productFlavor : model.getProductFlavors().values()) {
+            if (productFlavor.getProductFlavor().getApplicationId() != null) {
+                throw new GradleException("Library projects cannot set applicationId. " +
+                        "applicationId is set to '" +
+                        productFlavor.getProductFlavor().getApplicationId() + "' in flavor '" +
+                        productFlavor.getProductFlavor().getName() + "'.");
+            }
+        }
+
+    }
+
+    @Override
+    public void preVariantWork(Project project) {
+        // nothing to be done here.
+    }
+
+    @Override
+    public void createDefaultComponents(
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
+        // must create signing config first so that build type 'debug' can be initialized
+        // with the debug signing config.
+        signingConfigs.create(DEBUG);
+        buildTypes.create(DEBUG);
+        buildTypes.create(RELEASE);
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
index 1f3d534..49c31a9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
@@ -18,7 +18,7 @@
 import com.android.annotations.NonNull;
 import com.android.build.FilterData;
 import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
@@ -38,11 +38,11 @@
     private final TestedVariantData testedVariantData;
 
     public TestVariantData(
-            @NonNull BaseExtension baseExtension,
+            @NonNull AndroidConfig androidConfig,
             @NonNull TaskManager taskManager,
             @NonNull GradleVariantConfiguration config,
             @NonNull TestedVariantData testedVariantData) {
-        super(baseExtension, taskManager, config);
+        super(androidConfig, taskManager, config);
         this.testedVariantData = testedVariantData;
 
         // create default output
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.groovy
deleted file mode 100644
index 12dde47..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.groovy
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 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.annotations.Nullable
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.BuildTypeData
-import com.android.build.gradle.internal.ProductFlavorData
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.ProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.TestExtension
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.VariantType
-import com.android.builder.model.SourceProvider
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Project
-import org.gradle.api.artifacts.dsl.DependencyHandler
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-
-/**
- * Customization of ApplcationVariantFactory for test-only projects.
- */
-public class TestVariantFactory extends ApplicationVariantFactory {
-
-    public TestVariantFactory(
-            @NonNull Instantiator instantiator,
-            @NonNull AndroidBuilder androidBuilder,
-            @NonNull BaseExtension extension) {
-        super(instantiator, androidBuilder, extension)
-    }
-
-    @Override
-    public boolean hasTestScope() {
-        return false
-    }
-
-    @Override
-    public void preVariantWork(Project project) {
-        TestExtension testExtension = (TestExtension) extension
-
-        String path = testExtension.targetProjectPath
-        if (path == null) {
-            throw new GradleException("targetProjectPath cannot be null in test project ${project.name}")
-        }
-
-        if (testExtension.targetVariant == null) {
-            throw new GradleException("targetVariant cannot be null in test project ${project.name}")
-        }
-
-        String variant = "${testExtension.targetVariant}-classes"
-
-        DependencyHandler handler = project.getDependencies()
-        handler.add("provided", handler.project(path: path, configuration: variant))
-    }
-
-    @Override
-    public void createDefaultComponents(
-            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
-            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
-        // don't call super as we don't want the default app version.
-        // must create signing config first so that build type 'debug' can be initialized
-        // with the debug signing config.
-        signingConfigs.create(DEBUG);
-        buildTypes.create(DEBUG);
-    }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.java
new file mode 100644
index 0000000..60c9443
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 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.build.gradle.AndroidConfig;
+import com.android.build.gradle.TestAndroidConfig;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.google.common.collect.ImmutableMap;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * Customization of ApplcationVariantFactory for test-only projects.
+ */
+public class TestVariantFactory extends ApplicationVariantFactory {
+
+    public TestVariantFactory(
+            @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder,
+            @NonNull AndroidConfig extension) {
+        super(instantiator, androidBuilder, extension);
+    }
+
+    @Override
+    public boolean hasTestScope() {
+        return false;
+    }
+
+    @Override
+    public void preVariantWork(final Project project) {
+        final TestAndroidConfig testExtension = (TestAndroidConfig) extension;
+
+        String path = testExtension.getTargetProjectPath();
+        if (path == null) {
+            throw new GradleException(
+                    "targetProjectPath cannot be null in test project " + project.getName());
+        }
+
+        if (testExtension.getTargetVariant() == null) {
+            throw new GradleException(
+                    "targetVariant cannot be null in test project " + project.getName());
+        }
+
+        DependencyHandler handler = project.getDependencies();
+        handler.add("provided", handler.project(ImmutableMap.of(
+                "path", path,
+                "configuration", testExtension.getTargetVariant() + "-classes"
+        )));
+    }
+
+    @Override
+    public void createDefaultComponents(
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
+        // don't call super as we don't want the default app version.
+        // must create signing config first so that build type 'debug' can be initialized
+        // with the debug signing config.
+        signingConfigs.create(BuilderConstants.DEBUG);
+        buildTypes.create(BuilderConstants.DEBUG);
+    }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java
index ff85f16..4544f96 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java
@@ -17,26 +17,18 @@
 package com.android.build.gradle.internal.variant;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 import com.android.build.gradle.api.BaseVariant;
-import com.android.build.gradle.internal.BuildTypeData;
-import com.android.build.gradle.internal.ProductFlavorData;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.VariantModel;
 import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
 import com.android.build.gradle.internal.dsl.ProductFlavor;
 import com.android.build.gradle.internal.dsl.SigningConfig;
 import com.android.builder.core.VariantType;
 
 import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.api.Project;
-import org.gradle.api.Task;
-
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Interface for Variant Factory.
@@ -49,9 +41,6 @@
     @NonNull
     BaseVariantData createVariantData(
             @NonNull GradleVariantConfiguration variantConfiguration,
-            @NonNull Set<String> densities,
-            @NonNull Set<String> abi,
-            @NonNull Set<String> compatibleScreens,
             @NonNull TaskManager taskManager);
 
     @NonNull
@@ -77,6 +66,6 @@
 
     void createDefaultComponents(
             @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
-            @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
             @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs);
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.groovy
deleted file mode 100644
index 7d91e40..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.groovy
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 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.google.common.collect.Sets
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.maven.MavenDeployer
-import org.gradle.api.plugins.MavenPlugin
-import org.gradle.api.tasks.Upload
-
-/**
- */
-public class VariantHelper {
-
-    public static void setupDefaultConfig(@NonNull Project project, @NonNull Configuration configuration) {
-        // The library artifact is published (inter-project( for the "default" configuration so
-        // we make sure "default" extends from the actual configuration used for building.
-        Configuration defaultConfig = project.configurations["default"]
-        defaultConfig.setExtendsFrom(Collections.singleton(configuration))
-
-        // for the maven publication (for now), we need to manually include all the configuration
-        // object in a special mapping.
-        // It's not possible to put the top level config object as extended from config won't
-        // be included.
-        Set<Configuration> flattenedConfigs = flattenConfigurations(configuration)
-
-        project.plugins.withType(MavenPlugin) {
-            project.tasks.withType(Upload) { task ->
-                task.repositories.withType(MavenDeployer) { repo ->
-                    for (Configuration config : flattenedConfigs) {
-                        repo.pom.scopeMappings.addMapping(300,
-                                project.configurations[config.name],
-                                "compile")
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Build a set of configuration containing all the Configuration object that a given
-     * configuration extends from, directly or transitively.
-     *
-     * @param configuration the configuration
-     * @return a set of config.
-     */
-    private static Set<Configuration> flattenConfigurations(@NonNull Configuration configuration) {
-        Set<Configuration> configs = Sets.newHashSet()
-        configs.add(configuration)
-
-        for (Configuration extend : configuration.getExtendsFrom()) {
-            configs.addAll(flattenConfigurations(extend))
-        }
-
-        return configs
-    }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.java
new file mode 100644
index 0000000..f777d0b
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 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.google.common.collect.Sets;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.MavenDeployer;
+import org.gradle.api.plugins.MavenPlugin;
+import org.gradle.api.tasks.Upload;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ */
+public class VariantHelper {
+
+    public static void setupDefaultConfig(
+            @NonNull final Project project,
+            @NonNull Configuration configuration) {
+        // The library artifact is published (inter-project( for the "default" configuration so
+        // we make sure "default" extends from the actual configuration used for building.
+        Configuration defaultConfig = project.getConfigurations().getAt("default");
+        defaultConfig.setExtendsFrom(Collections.singleton(configuration));
+
+        // for the maven publication (for now), we need to manually include all the configuration
+        // object in a special mapping.
+        // It's not possible to put the top level config object as extended from config won't
+        // be included.
+        final Set<Configuration> flattenedConfigs = flattenConfigurations(configuration);
+
+        project.getPlugins().withType(MavenPlugin.class, new Action<MavenPlugin>() {
+            @Override
+            public void execute(MavenPlugin mavenPlugin) {
+                project.getTasks().withType(Upload.class, new Action<Upload>() {
+                    @Override
+                    public void execute(Upload upload) {
+                        upload.getRepositories().withType(
+                                MavenDeployer.class,
+                                new Action<MavenDeployer>() {
+                                    @Override
+                                    public void execute(MavenDeployer mavenDeployer) {
+                                        for (Configuration config : flattenedConfigs) {
+                                            mavenDeployer.getPom().getScopeMappings().addMapping(
+                                                    300,
+                                                    project.getConfigurations().getByName(
+                                                            config.getName()),
+                                                    "compile");
+                                        }
+                                    }
+                                });
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Build a set of configuration containing all the Configuration object that a given
+     * configuration extends from, directly or transitively.
+     *
+     * @param configuration the configuration
+     * @return a set of config.
+     */
+    private static Set<Configuration> flattenConfigurations(@NonNull Configuration configuration) {
+        Set<Configuration> configs = Sets.newHashSet();
+        configs.add(configuration);
+
+        for (Configuration extend : configuration.getExtendsFrom()) {
+            configs.addAll(flattenConfigurations(extend));
+        }
+
+        return configs;
+    }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/profiling/ProfilingBuildListener.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/profiling/ProfilingBuildListener.groovy
deleted file mode 100644
index 9af97ab..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/profiling/ProfilingBuildListener.groovy
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2015 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.profiling
-
-import groovy.json.JsonBuilder
-import groovy.transform.CompileStatic
-import org.gradle.BuildListener
-import org.gradle.BuildResult
-import org.gradle.api.Project
-import org.gradle.api.ProjectEvaluationListener
-import org.gradle.api.ProjectState
-import org.gradle.api.Task
-import org.gradle.api.artifacts.DependencyResolutionListener
-import org.gradle.api.artifacts.ResolvableDependencies
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.execution.TaskExecutionGraphListener
-import org.gradle.api.execution.TaskExecutionListener
-import org.gradle.api.initialization.Settings
-import org.gradle.api.invocation.Gradle
-import org.gradle.api.tasks.TaskState
-import org.gradle.initialization.BuildCompletionListener
-
-/**
- * A simple build profiler that listens to the Gradle build and outputs Chrome trace data.
- *
- * <p>Here's an example of how to enable it from a Gradle init script (-I):
- * <pre><code>
- * import com.android.build.gradle.profiling.ProfilingBuildListener
- * gradle.addListener(new ProfilingBuildListener("build.trace"))
- * </code></pre>
- *
- * <p>See <a href="https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit">
- *     Chrome trace event format<a>
- */
-@CompileStatic
-public class ProfilingBuildListener implements BuildListener, TaskExecutionListener,
-        TaskExecutionGraphListener, ProjectEvaluationListener, DependencyResolutionListener,
-        BuildCompletionListener {
-
-    PrintWriter traceWriter;
-
-    public ProfilingBuildListener(String traceFile) {
-        traceWriter = new PrintWriter(new FileOutputStream(new File(traceFile)));
-        traceWriter.append('[')
-    }
-
-    @Override
-    void buildStarted(Gradle gradle) {
-    }
-
-    @Override
-    void settingsEvaluated(Settings settings) {
-    }
-
-    @Override
-    void projectsLoaded(Gradle gradle) {
-    }
-
-    @Override
-    void projectsEvaluated(Gradle gradle) {
-    }
-
-    @Override
-    void buildFinished(BuildResult buildResult) {
-    }
-
-    @Override
-    void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
-    }
-
-    @Override
-    void beforeExecute(Task task) {
-        long now = System.currentTimeMillis();
-        task.convention.add("__started", now);
-
-        writeStart(task.name)
-    }
-
-    @Override
-    void afterExecute(Task task, TaskState taskState) {
-        long start = (long) task.convention.getByName("__started")
-        long now = System.currentTimeMillis()
-
-        def args = [
-                project: task.project.name,
-                description: task.description,
-                didWork: taskState.didWork,
-                failure: taskState.getFailure(),
-        ]
-        writeEnd(task.name, now - start, args)
-    }
-
-    @Override
-    void completed() {
-        def builder = new JsonBuilder([
-                cat: 'gradle',
-                name: 'DONE',
-                pid: 0,
-                tid: Thread.currentThread().name,
-                ts: System.currentTimeMillis() * 1000,
-                ph: 'I'
-        ])
-        writeOut(builder)
-        traceWriter.append(']')
-        traceWriter.close()
-    }
-
-    @Override
-    void beforeResolve(ResolvableDependencies resolvableDependencies) {
-        writeStart(resolvableDependencies.name)
-    }
-
-    @Override
-    void afterResolve(ResolvableDependencies resolvableDependencies) {
-        def args = [
-                path: resolvableDependencies.path,
-                description: 'Resolving ' + resolvableDependencies.name,
-                resultSize: resolvableDependencies.resolutionResult.allDependencies.size(),
-        ]
-
-        writeEnd(resolvableDependencies.name, 0, args)
-    }
-
-    @Override
-    void beforeEvaluate(Project project) {
-        long now = System.currentTimeMillis();
-        project.convention.add("__started", now);
-
-        writeStart(project.name)
-    }
-
-    @Override
-    void afterEvaluate(Project project, ProjectState projectState) {
-        long start = (long) project.convention.getByName("__started")
-        long now = System.currentTimeMillis()
-
-        def args = [
-                project: project.path,
-                description: project.description,
-                executed: projectState.executed,
-                failure: projectState.failure,
-        ]
-        writeEnd(project.name, now - start, args)
-    }
-
-    /**
-     * Writes the start of an event with the current timestamp and thread.
-     */
-    public void writeStart(String eventName) {
-        long now = System.currentTimeMillis();
-        def builder = new JsonBuilder([
-                cat: 'gradle',
-                name: eventName,
-                pid: 0,
-                tid: Thread.currentThread().name,
-                ts: now * 1000,
-                ph: 'B'
-        ])
-        writeOut(builder)
-    }
-
-    /**
-     * Writes the end of an event with the current timestamp, adding total duration (wall-clock time)
-     * and a user-specified property map.
-     */
-    public void writeEnd(String eventName, long duration, Object properties) {
-        long now = System.currentTimeMillis();
-        def builder = new JsonBuilder([
-                cat: 'gradle',
-                name: eventName,
-                pid: 0,
-                tid: Thread.currentThread().name,
-                ts: now * 1000,
-                ph: 'E',
-                dur: duration,
-                args: properties
-        ])
-        writeOut(builder)
-    }
-
-    private synchronized void writeOut(JsonBuilder builder) {
-        builder.writeTo(traceWriter)
-        traceWriter.append(',')
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
deleted file mode 100644
index 38dda4b..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
+++ /dev/null
@@ -1,346 +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.build.gradle.tasks
-
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.annotations.concurrency.GuardedBy
-import com.android.build.gradle.api.ApkVariant
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.IncrementalTask
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.compiling.DependencyFileProcessor
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.internal.incremental.DependencyData
-import com.android.builder.internal.incremental.DependencyDataStore
-import com.android.builder.model.AndroidProject
-import com.android.ide.common.internal.WaitableExecutor
-import com.android.ide.common.res2.FileStatus
-import com.google.common.collect.Lists
-import com.google.common.collect.Multimap
-import org.gradle.api.file.FileTree
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.util.PatternSet
-
-import java.util.concurrent.Callable
-
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-/**
- * Task to compile aidl files. Supports incremental update.
- */
-public class AidlCompile extends IncrementalTask {
-
-    private static final String DEPENDENCY_STORE = "dependency.store"
-
-    // ----- PUBLIC TASK API -----
-
-    @OutputDirectory
-    File sourceOutputDir
-
-    @OutputDirectory @Optional
-    File aidlParcelableDir
-
-    // ----- PRIVATE TASK API -----
-    @Input
-    String getBuildToolsVersion() {
-        getBuildTools().getRevision()
-    }
-
-    List<File> sourceDirs
-
-    @InputFiles
-    List<File> importDirs
-
-    final PatternSet patternSet = new PatternSet().include("**/*.aidl")
-
-    @InputFiles
-    FileTree getSourceFiles() {
-        FileTree src = null
-        Set<File> sources = getSourceDirs()
-        if (!sources.isEmpty()) {
-            src = getProject().files(new ArrayList<Object>(sources)).getAsFileTree().matching(patternSet)
-        }
-        return src == null ? getProject().files().getAsFileTree() : src
-    }
-
-    private static class DepFileProcessor implements DependencyFileProcessor {
-
-        @GuardedBy("this")
-        List<DependencyData> dependencyDataList = Lists.newArrayList()
-
-        List<DependencyData> getDependencyDataList() {
-            return dependencyDataList
-        }
-
-        @Override
-        DependencyData processFile(@NonNull File dependencyFile) {
-            DependencyData data = DependencyData.parseDependencyFile(dependencyFile)
-            if (data != null) {
-                synchronized (this) {
-                    dependencyDataList.add(data)
-                }
-            }
-
-            return data;
-        }
-    }
-
-    protected boolean isIncremental() {
-        // TODO fix once dep file parsing is resolved.
-        return false
-    }
-
-    /**
-     * Action methods to compile all the files.
-     *
-     * The method receives a {@link DependencyFileProcessor} to be used by the
-     * {@link com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during
-     * the compilation.
-     *
-     * @param dependencyFileProcessor a DependencyFileProcessor
-     */
-    private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor) {
-        getBuilder().compileAllAidlFiles(
-                getSourceDirs(),
-                getSourceOutputDir(),
-                getAidlParcelableDir(),
-                getImportDirs(),
-                dependencyFileProcessor)
-    }
-
-    /**
-     * Returns the import folders.
-     */
-    @NonNull
-    private List<File> getImportFolders() {
-        List<File> fullImportDir = Lists.newArrayList()
-        fullImportDir.addAll(getImportDirs())
-        fullImportDir.addAll(getSourceDirs())
-
-        return fullImportDir
-    }
-
-    /**
-     * Compiles a single file.
-     * @param sourceFolder the file to compile.
-     * @param file the file to compile.
-     * @param importFolders the import folders.
-     * @param dependencyFileProcessor a DependencyFileProcessor
-     */
-    private void compileSingleFile(
-            @NonNull File sourceFolder,
-            @NonNull File file,
-            @Nullable List<File> importFolders,
-            @NonNull DependencyFileProcessor dependencyFileProcessor) {
-        getBuilder().compileAidlFile(
-                sourceFolder,
-                file,
-                getSourceOutputDir(),
-                getAidlParcelableDir(),
-                importFolders,
-                dependencyFileProcessor)
-    }
-
-    @Override
-    protected void doFullTaskAction() {
-        // this is full run, clean the previous output
-        File destinationDir = getSourceOutputDir()
-        emptyFolder(destinationDir)
-
-        File parcelableDir = getAidlParcelableDir()
-        if (parcelableDir != null) {
-            emptyFolder(parcelableDir)
-        }
-
-        DepFileProcessor processor = new DepFileProcessor()
-
-        compileAllFiles(processor)
-
-        List<DependencyData> dataList = processor.getDependencyDataList()
-
-        DependencyDataStore store = new DependencyDataStore()
-        store.addData(dataList)
-
-        store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE))
-    }
-
-    @Override
-    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
-        File incrementalData = new File(getIncrementalFolder(), DEPENDENCY_STORE)
-        DependencyDataStore store = new DependencyDataStore()
-        Multimap<String, DependencyData> inputMap
-        try {
-            inputMap = store.loadFrom(incrementalData)
-        } catch (Exception ignored) {
-            incrementalData.delete()
-            project.logger.info(
-                    "Failed to read dependency store: full task run!")
-            doFullTaskAction()
-            return
-        }
-
-        final List<File> importFolders = getImportFolders()
-        final DepFileProcessor processor = new DepFileProcessor()
-
-        // use an executor to parallelize the compilation of multiple files.
-        WaitableExecutor<Void> executor = new WaitableExecutor<Void>()
-
-        Map<String,DependencyData> mainFileMap = store.getMainFileMap()
-
-        for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
-            FileStatus status = entry.getValue()
-
-            switch (status) {
-                case FileStatus.NEW:
-                    executor.execute(new Callable<Void>() {
-                        @Override
-                        Void call() throws Exception {
-                            File file = entry.getKey()
-                            compileSingleFile(getSourceFolder(file), file, importFolders, processor)
-                        }
-                    })
-                    break
-                case FileStatus.CHANGED:
-                    List<DependencyData> impactedData = inputMap.get(entry.getKey().absolutePath)
-                    if (impactedData != null) {
-                        final int count = impactedData.size();
-                        for (int i = 0; i < count; i++) {
-                            final DependencyData data = impactedData.get(i);
-
-                            executor.execute(new Callable<Void>() {
-                                @Override
-                                Void call() throws Exception {
-                                    File file = new File(data.getMainFile());
-                                    compileSingleFile(getSourceFolder(file), file,
-                                            importFolders, processor)
-                                }
-                            })
-                        }
-                    }
-                    break
-                case FileStatus.REMOVED:
-                    final DependencyData data2 = mainFileMap.get(entry.getKey().absolutePath)
-                    if (data2 != null) {
-                        executor.execute(new Callable<Void>() {
-                            @Override
-                            Void call() throws Exception {
-                                cleanUpOutputFrom(data2)
-                            }
-                        })
-                        store.remove(data2)
-                    }
-                    break
-            }
-        }
-
-        try {
-            executor.waitForTasksWithQuickFail(true /*cancelRemaining*/)
-        } catch (Throwable t) {
-            incrementalData.delete()
-            throw t
-        }
-
-        // get all the update data for the recompiled objects
-        store.updateAll(processor.getDependencyDataList())
-
-        store.saveTo(incrementalData)
-    }
-
-    private File getSourceFolder(@NonNull File file) {
-        File parentDir = file
-        while ((parentDir = parentDir.getParentFile()) != null) {
-            for (File folder : getSourceDirs()) {
-                if (parentDir.equals(folder)) {
-                    return folder;
-                }
-            }
-        }
-
-        assert false
-    }
-
-    private static void cleanUpOutputFrom(@NonNull DependencyData dependencyData) {
-        for (String output : dependencyData.getOutputFiles()) {
-            new File(output).delete()
-        }
-        for (String output : dependencyData.getSecondaryOutputFiles()) {
-            new File(output).delete()
-        }
-    }
-
-
-    public static class ConfigAction implements TaskConfigAction<AidlCompile> {
-
-        @NonNull
-        VariantScope scope
-
-        ConfigAction(@NonNull VariantScope scope) {
-            this.scope = scope
-        }
-
-        @Override
-        @NonNull
-        String getName() {
-            return scope.getTaskName("compile", "Aidl")
-        }
-
-        @Override
-        @NonNull
-        Class<AidlCompile> getType() {
-            return AidlCompile
-        }
-
-        @Override
-        void execute(AidlCompile compileTask) {
-            VariantConfiguration variantConfiguration = scope.variantConfiguration
-
-            scope.variantData.aidlCompileTask = compileTask
-
-            compileTask.androidBuilder = scope.globalScope.androidBuilder
-            compileTask.incrementalFolder =
-                    new File(
-                            "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/incremental/aidl/${variantConfiguration.dirName}")
-
-            ConventionMappingHelper.map(compileTask, "sourceDirs") { variantConfiguration.aidlSourceList }
-            ConventionMappingHelper.map(compileTask, "importDirs") { variantConfiguration.aidlImports }
-
-            ConventionMappingHelper.map(compileTask, "sourceOutputDir") {
-                //new File(scope.globalScope.generatedDir, "source/aidl/${variantConfiguration.dirName}")
-                scope.getAidlSourceOutputDir()
-            }
-
-            if (variantConfiguration.type == VariantType.LIBRARY) {
-                compileTask.aidlParcelableDir = new File(
-                        "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/$TaskManager.DIR_BUNDLES/${variantConfiguration.dirName}/$SdkConstants.FD_AIDL")
-            }
-
-        }
-    }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.java
new file mode 100644
index 0000000..bb98123
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.java
@@ -0,0 +1,404 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.GuardedBy;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.IncrementalTask;
+import com.android.builder.compiling.DependencyFileProcessor;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.core.VariantType;
+import com.android.builder.internal.incremental.DependencyData;
+import com.android.builder.internal.incremental.DependencyDataStore;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.ide.common.res2.FileStatus;
+import com.android.utils.FileUtils;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import org.gradle.api.file.FileTree;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.util.PatternSet;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Task to compile aidl files. Supports incremental update.
+ */
+public class AidlCompile extends IncrementalTask {
+
+    private static final String DEPENDENCY_STORE = "dependency.store";
+    private static final PatternSet PATTERN_SET = new PatternSet().include("**/*.aidl");
+
+    // ----- PUBLIC TASK API -----
+    private File sourceOutputDir;
+    private File aidlParcelableDir;
+
+    // ----- PRIVATE TASK API -----
+    @Input
+    String getBuildToolsVersion() {
+        return getBuildTools().getRevision().toString();
+    }
+    private List<File> sourceDirs;
+    private List<File> importDirs;
+
+    @InputFiles
+    FileTree getSourceFiles() {
+        FileTree src = null;
+        List<File> sources = getSourceDirs();
+        if (!sources.isEmpty()) {
+            src = getProject().files(sources).getAsFileTree().matching(PATTERN_SET);
+        }
+        return src == null ? getProject().files().getAsFileTree() : src;
+    }
+
+    private static class DepFileProcessor implements DependencyFileProcessor {
+
+        @GuardedBy("this")
+        List<DependencyData> dependencyDataList = Lists.newArrayList();
+
+        List<DependencyData> getDependencyDataList() {
+            return dependencyDataList;
+        }
+
+        @Override
+        public DependencyData processFile(@NonNull File dependencyFile) throws IOException {
+            DependencyData data = DependencyData.parseDependencyFile(dependencyFile);
+            if (data != null) {
+                synchronized (this) {
+                    dependencyDataList.add(data);
+                }
+            }
+
+            return data;
+        }
+    }
+
+    @Override
+    protected boolean isIncremental() {
+        // TODO fix once dep file parsing is resolved.
+        return false;
+    }
+
+    /**
+     * Action methods to compile all the files.
+     *
+     * The method receives a {@link DependencyFileProcessor} to be used by the
+     * {@link com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during
+     * the compilation.
+     *
+     * @param dependencyFileProcessor a DependencyFileProcessor
+     */
+    private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor)
+            throws InterruptedException, ProcessException, LoggedErrorException, IOException {
+        getBuilder().compileAllAidlFiles(
+                getSourceDirs(),
+                getSourceOutputDir(),
+                getAidlParcelableDir(),
+                getImportDirs(),
+                dependencyFileProcessor,
+                new LoggedProcessOutputHandler(getILogger()));
+    }
+
+    /**
+     * Returns the import folders.
+     */
+    @NonNull
+    private List<File> getImportFolders() {
+        List<File> fullImportDir = Lists.newArrayList();
+        fullImportDir.addAll(getImportDirs());
+        fullImportDir.addAll(getSourceDirs());
+
+        return fullImportDir;
+    }
+
+    /**
+     * Compiles a single file.
+     * @param sourceFolder the file to compile.
+     * @param file the file to compile.
+     * @param importFolders the import folders.
+     * @param dependencyFileProcessor a DependencyFileProcessor
+     */
+    private void compileSingleFile(
+            @NonNull File sourceFolder,
+            @NonNull File file,
+            @Nullable List<File> importFolders,
+            @NonNull DependencyFileProcessor dependencyFileProcessor,
+            @NonNull ProcessOutputHandler processOutputHandler)
+            throws InterruptedException, ProcessException, LoggedErrorException, IOException {
+        getBuilder().compileAidlFile(
+                sourceFolder,
+                file,
+                getSourceOutputDir(),
+                getAidlParcelableDir(),
+                importFolders,
+                dependencyFileProcessor,
+                processOutputHandler);
+    }
+
+    @Override
+    protected void doFullTaskAction() throws IOException {
+        // this is full run, clean the previous output
+        File destinationDir = getSourceOutputDir();
+        File parcelableDir = getAidlParcelableDir();
+        FileUtils.emptyFolder(destinationDir);
+        if (parcelableDir != null) {
+            FileUtils.emptyFolder(parcelableDir);
+        }
+
+
+        DepFileProcessor processor = new DepFileProcessor();
+
+        try {
+            compileAllFiles(processor);
+        } catch (Exception e) {
+            throw  new RuntimeException(e);
+        }
+
+        List<DependencyData> dataList = processor.getDependencyDataList();
+
+        DependencyDataStore store = new DependencyDataStore();
+        store.addData(dataList);
+
+        try {
+            store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws IOException {
+        File incrementalData = new File(getIncrementalFolder(), DEPENDENCY_STORE);
+        DependencyDataStore store = new DependencyDataStore();
+        Multimap<String, DependencyData> inputMap;
+        try {
+            inputMap = store.loadFrom(incrementalData);
+        } catch (Exception ignored) {
+            incrementalData.delete();
+            getProject().getLogger().info(
+                    "Failed to read dependency store: full task run!");
+            doFullTaskAction();
+            return;
+        }
+
+        final List<File> importFolders = getImportFolders();
+        final DepFileProcessor processor = new DepFileProcessor();
+        final ProcessOutputHandler processOutputHandler =
+                new LoggedProcessOutputHandler(getILogger());
+
+        // use an executor to parallelize the compilation of multiple files.
+        WaitableExecutor<Void> executor = new WaitableExecutor<Void>();
+
+        Map<String,DependencyData> mainFileMap = store.getMainFileMap();
+
+        for (final Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
+            FileStatus status = entry.getValue();
+
+            switch (status) {
+                case NEW:
+                    executor.execute(new Callable<Void>() {
+                        @Override
+                        public Void call() throws Exception {
+                            File file = entry.getKey();
+                            compileSingleFile(getSourceFolder(file), file, importFolders,
+                                    processor, processOutputHandler);
+                            return null;
+                        }
+                    });
+                    break;
+                case CHANGED:
+                    Collection<DependencyData> impactedData =
+                            inputMap.get(entry.getKey().getAbsolutePath());
+                    if (impactedData != null) {
+                        for (final DependencyData data: impactedData) {
+                            executor.execute(new Callable<Void>() {
+                                @Override
+                                public Void call() throws Exception {
+                                    File file = new File(data.getMainFile());
+                                    compileSingleFile(getSourceFolder(file), file,
+                                            importFolders, processor, processOutputHandler);
+                                    return null;
+                                }
+                            });
+                        }
+                    }
+                    break;
+                case REMOVED:
+                    final DependencyData data2 = mainFileMap.get(entry.getKey().getAbsolutePath());
+                    if (data2 != null) {
+                        executor.execute(new Callable<Void>() {
+                            @Override
+                            public Void call() throws Exception {
+                                cleanUpOutputFrom(data2);
+                                return null;
+                            }
+                        });
+                        store.remove(data2);
+                    }
+                    break;
+            }
+        }
+
+        try {
+            executor.waitForTasksWithQuickFail(true /*cancelRemaining*/);
+        } catch (Throwable t) {
+            incrementalData.delete();
+            throw new RuntimeException(t);
+        }
+
+        // get all the update data for the recompiled objects
+        store.updateAll(processor.getDependencyDataList());
+
+        try {
+            store.saveTo(incrementalData);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private File getSourceFolder(@NonNull File file) {
+        File parentDir = file;
+        while ((parentDir = parentDir.getParentFile()) != null) {
+            for (File folder : getSourceDirs()) {
+                if (parentDir.equals(folder)) {
+                    return folder;
+                }
+            }
+        }
+
+        throw new IllegalArgumentException(String.format("File '%s' is not in a source dir", file));
+    }
+
+    private static void cleanUpOutputFrom(@NonNull DependencyData dependencyData) {
+        for (String output : dependencyData.getOutputFiles()) {
+            new File(output).delete();
+        }
+        for (String output : dependencyData.getSecondaryOutputFiles()) {
+            new File(output).delete();
+        }
+    }
+
+    @OutputDirectory
+    public File getSourceOutputDir() {
+        return sourceOutputDir;
+    }
+
+    public void setSourceOutputDir(File sourceOutputDir) {
+        this.sourceOutputDir = sourceOutputDir;
+    }
+
+    @OutputDirectory @Optional
+    public File getAidlParcelableDir() {
+        return aidlParcelableDir;
+    }
+
+    public void setAidlParcelableDir(File aidlParcelableDir) {
+        this.aidlParcelableDir = aidlParcelableDir;
+    }
+
+    public List<File> getSourceDirs() {
+        return sourceDirs;
+    }
+
+    public void setSourceDirs(List<File> sourceDirs) {
+        this.sourceDirs = sourceDirs;
+    }
+
+    @InputFiles
+    public List<File> getImportDirs() {
+        return importDirs;
+    }
+
+    public void setImportDirs(List<File> importDirs) {
+        this.importDirs = importDirs;
+    }
+
+    public static class ConfigAction implements TaskConfigAction<AidlCompile> {
+
+        @NonNull
+        VariantScope scope;
+
+        public ConfigAction(@NonNull VariantScope scope) {
+            this.scope = scope;
+        }
+
+        @Override
+        @NonNull
+        public String getName() {
+            return scope.getTaskName("compile", "Aidl");
+        }
+
+        @Override
+        @NonNull
+        public Class<AidlCompile> getType() {
+            return AidlCompile.class;
+        }
+
+        @Override
+        public void execute(AidlCompile compileTask) {
+            final VariantConfiguration<?,?,?> variantConfiguration = scope.getVariantConfiguration();
+
+            scope.getVariantData().aidlCompileTask = compileTask;
+
+            compileTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            compileTask.setVariantName(scope.getVariantConfiguration().getFullName());
+            compileTask.setIncrementalFolder(scope.getAidlIncrementalDir());
+
+            ConventionMappingHelper.map(compileTask, "sourceDirs", new Callable<List<File>>() {
+                @Override
+                public List<File> call() throws Exception {
+                    return variantConfiguration.getAidlSourceList();
+                }
+            });
+            ConventionMappingHelper.map(compileTask, "importDirs", new Callable<List<File>>() {
+                @Override
+                public List<File> call() throws Exception {
+                    return variantConfiguration.getAidlImports();
+                }
+            });
+
+            ConventionMappingHelper.map(compileTask, "sourceOutputDir", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return scope.getAidlSourceOutputDir();
+                }
+            });
+
+            if (variantConfiguration.getType() == VariantType.LIBRARY) {
+                compileTask.setAidlParcelableDir(scope.getAidlParcelableDir());
+            }
+        }
+    }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java
index cd8fa12..19c4fd2 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java
@@ -16,13 +16,13 @@
 
 package com.android.build.gradle.tasks;
 
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
 import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
 
+import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.LibraryExtension;
 import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.PostCompilationData;
 import com.android.build.gradle.internal.TaskManager;
 import com.android.build.gradle.internal.scope.TaskConfigAction;
 import com.android.build.gradle.internal.scope.VariantScope;
@@ -31,12 +31,16 @@
 import com.android.build.gradle.internal.variant.BaseVariantOutputData;
 import com.android.build.gradle.internal.variant.LibraryVariantData;
 import com.android.builder.core.VariantConfiguration;
-import com.android.utils.StringHelper;
+import com.android.builder.tasks.Job;
+import com.android.builder.tasks.JobContext;
+import com.android.ide.common.packaging.PackagingUtils;
+import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.gradle.api.Action;
 import org.gradle.api.Task;
 import org.gradle.api.tasks.InputFile;
@@ -46,12 +50,12 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Callable;
 
 import groovy.lang.Closure;
 import proguard.ParseException;
@@ -61,7 +65,7 @@
  * Decoration for the {@link ProGuardTask} so it implements shared interfaces with our custom
  * tasks.
  */
-public class AndroidProGuardTask extends ProGuardTask implements FileSupplier {
+public class AndroidProGuardTask extends ProGuardTask implements FileSupplier, JavaResourcesProvider {
 
     /**
      * resulting obfuscation mapping file.
@@ -80,6 +84,8 @@
     @Optional
     File testedAppMappingFile;
 
+    File obfuscatedClassesJar;
+
     @Override
     public void printmapping(Object printMapping) throws ParseException {
         mappingFile = (File) printMapping;
@@ -104,7 +110,32 @@
 
     @Override
     @TaskAction
-    public void proguard() throws ParseException, IOException {
+    public void proguard() throws IOException, ParseException {
+        final Job<Void> job = new Job<Void>(getName(),
+                new com.android.builder.tasks.Task<Void>() {
+                    @Override
+                    public void run(@NonNull Job<Void> job,
+                            @NonNull JobContext<Void> context) throws IOException {
+                        try {
+                            AndroidProGuardTask.this.doMinification();
+                        } catch (ParseException e) {
+                            throw new IOException(e);
+                        }
+                    }
+                });
+        try {
+            SimpleWorkQueue.push(job);
+
+            // wait for the task completion.
+            job.await();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    public void doMinification() throws ParseException, IOException {
         // only set the tested application mapping file if it exists (it must at this point or that
         // means the tested application did not request obfuscation).
         if (testedAppMappingFile != null && testedAppMappingFile.exists()) {
@@ -113,19 +144,26 @@
         super.proguard();
     }
 
+    @NonNull
+    @Override
+    public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+        return ImmutableList.of(new JavaResourcesLocation(Type.JAR, obfuscatedClassesJar));
+    }
+
     public static class ConfigAction implements TaskConfigAction<AndroidProGuardTask> {
 
         private VariantScope scope;
 
-        private Closure<File> inputDir;
+        private Callable<File> inputDir;
+        private Callable<File> javaResourcesInputDir;
 
-        private Closure<List<File>> inputLibraries;
+        private Callable<List<File>> inputLibraries;
 
-        public ConfigAction(VariantScope scope,
-                TaskManager.PostCompilationData pcData) {
+        public ConfigAction(VariantScope scope, final PostCompilationData pcData) {
             this.scope = scope;
-            inputDir = pcData.getInputDir();
-            inputLibraries = pcData.getInputLibraries();
+            inputDir = pcData.getInputDirCallable();
+            javaResourcesInputDir = pcData.getJavaResourcesInputDirCallable();
+            inputLibraries = pcData.getInputLibrariesCallable();
         }
 
         @Override
@@ -156,14 +194,7 @@
 
             // --- Output File ---
 
-            final File outFile = variantData instanceof LibraryVariantData ? new File(
-                    String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_INTERMEDIATES + "/"
-                            + TaskManager.DIR_BUNDLES + "/" + variantData.getVariantConfiguration()
-                            .getDirName() + "/classes.jar") : new File(
-                    String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_INTERMEDIATES
-                            + "/classes-proguard/" + variantData.getVariantConfiguration().getDirName()
-                            + "/classes.jar");
-            variantData.obfuscatedClassesJar = outFile;
+            proguardTask.obfuscatedClassesJar = scope.getProguardOutputFile();
 
             // --- Proguard Config ---
 
@@ -178,6 +209,7 @@
                     proguardTask.keep("class * {*;}");
                     proguardTask.keep("interface * {*;}");
                     proguardTask.keep("enum * {*;}");
+                    proguardTask.keepattributes();
 
                     // Input the mapping from the tested app so that we can deal with obfuscated code.
                     proguardTask.applymapping(testedVariantData.getMappingFile());
@@ -194,21 +226,16 @@
                         proguardTask.dontwarn("org.jacoco.**");
                     }
 
-                    proguardTask.configuration(new Closure<Collection<File>>(this, this) {
-                        public Collection<File> doCall(Object it) {
+                    proguardTask.configuration(new Callable<Collection<File>>() {
+                        @Override
+                        public Collection<File> call() throws Exception {
                             List<File> proguardFiles = variantConfig.getProguardFiles(true,
-                                    Collections.singletonList(scope.getGlobalScope().getExtension()
-                                            .getDefaultProguardFile(
-                                                    TaskManager.DEFAULT_PROGUARD_CONFIG_FILE)));
+                                    Collections.singletonList(getDefaultProguardFile(
+                                            TaskManager.DEFAULT_PROGUARD_CONFIG_FILE)));
                             proguardFiles.add(
                                     variantOutputData.processResourcesTask.getProguardOutputFile());
                             return proguardFiles;
                         }
-
-                        public Collection<File> doCall() {
-                            return doCall(null);
-                        }
-
                     });
                 }
 
@@ -226,8 +253,7 @@
                     // exclude R files and such from output
                     String exclude = "!" + packageName + "/R.class";
                     exclude += (", !" + packageName + "/R$*.class");
-                    if (!((LibraryExtension) scope.getGlobalScope().getExtension())
-                            .getPackageBuildConfig()) {
+                    if (!scope.getGlobalScope().getExtension().getPackageBuildConfig()) {
                         exclude += (", !" + packageName + "/Manifest.class");
                         exclude += (", !" + packageName + "/Manifest$*.class");
                         exclude += (", !" + packageName + "/BuildConfig.class");
@@ -242,24 +268,21 @@
                     proguardTask.libraryjars(map1, inputDir);
 
                     // injar: the local dependencies
-                    Closure inJars = new Closure<List<File>>(this, this) {
-                        public List<File> doCall(Object it) {
+                    Callable inJars = new Callable<List<File>>() {
+                        @Override
+                        public List<File> call() throws Exception {
                             return DependencyManager
                                     .getPackagedLocalJarFileList(variantData.getVariantDependency());
                         }
-
-                        public List<File> doCall() {
-                            return doCall(null);
-                        }
-
                     };
 
                     proguardTask.injars(ImmutableMap.of("filter", "!META-INF/MANIFEST.MF"), inJars);
 
                     // libjar: the library dependencies. In this case we take all the compile-scope
                     // dependencies
-                    Closure libJars = new Closure<Iterable<File>>(this, this) {
-                        public Iterable<File> doCall(Object it) {
+                    Callable libJars = new Callable<Iterable<File>>() {
+                        @Override
+                        public Iterable<File> call() throws Exception {
                             // get all the compiled jar.
                             Set<File> compiledJars = scope.getGlobalScope().getAndroidBuilder()
                                     .getCompileClasspath(variantConfig);
@@ -274,10 +297,6 @@
                                 }
                             });
                         }
-
-                        public Iterable<File> doCall() {
-                            return doCall(null);
-                        }
                     };
 
                     proguardTask.libraryjars(ImmutableMap.of("filter", "!META-INF/MANIFEST.MF"), libJars);
@@ -287,22 +306,30 @@
                 } else {
                     // injar: the compilation output
                     proguardTask.injars(inputDir);
+                    if (javaResourcesInputDir != null) {
+                        proguardTask.injars(javaResourcesInputDir);
+                    }
 
                     // injar: the packaged dependencies
                     LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1);
-                    map.put("filter", "!META-INF/MANIFEST.MF");
+
+                    // add a filter to explicitly add files of the following extensions to the
+                    // resulting proguarded classes.jar by only including the extensions that were
+                    // not filtered by the libraries resource extraction task.
+                    String filter = Joiner.on(", **/*.").join(
+                            PackagingUtils.NON_RESOURCES_EXTENSIONS);
+
+                    map.put("filter", "!META-INF/MANIFEST.MF" + (Strings.isNullOrEmpty(filter)
+                            ? ""
+                            : ", **/*." + filter));
                     proguardTask.injars(map, inputLibraries);
 
                     // the provided-only jars as libraries.
-                    Closure libJars = new Closure<List<File>>(this, this) {
-                        public List<File> doCall(Object it) {
+                    Callable<List<File>> libJars = new Callable<List<File>>() {
+                        @Override
+                        public List<File> call() throws Exception {
                             return variantData.getVariantConfiguration().getProvidedOnlyJars();
                         }
-
-                        public List<File> doCall() {
-                            return doCall(null);
-                        }
-
                     };
 
                     proguardTask.libraryjars(libJars);
@@ -326,18 +353,14 @@
 
                 if (testedVariantData != null) {
                     // input the tested app as library
-                    proguardTask.libraryjars(testedVariantData.javaCompileTask.getDestinationDir());
+                    proguardTask.libraryjars(testedVariantData.javacTask.getDestinationDir());
                     // including its dependencies
-                    Closure testedPackagedJars = new Closure<Set<File>>(this, this) {
-                        public Set<File> doCall(Object it) {
+                    Callable testedPackagedJars = new Callable<Set<File>>() {
+                        @Override
+                        public Set<File> call() throws Exception {
                             return scope.getGlobalScope().getAndroidBuilder()
                                     .getPackagedJars(testedVariantData.getVariantConfiguration());
                         }
-
-                        public Set<File> doCall() {
-                            return doCall(null);
-                        }
-
                     };
 
                     LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1);
@@ -347,7 +370,7 @@
 
                 // --- Out files ---
 
-                proguardTask.outjars(outFile);
+                proguardTask.outjars(scope.getProguardOutputFile());
 
                 final File proguardOut = new File(
                         String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_OUTPUTS
@@ -376,5 +399,13 @@
                 throw new RuntimeException(e);
             }
         }
+
+        private File getDefaultProguardFile(String name) {
+            File sdkDir = scope.getGlobalScope().getSdkHandler().getAndCheckSdkFolder();
+            return new File(sdkDir,
+                    SdkConstants.FD_TOOLS + File.separatorChar
+                            + SdkConstants.FD_PROGUARD + File.separatorChar
+                            + name);
+        }
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy
index 7320ecc..ca05d0c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy
@@ -106,6 +106,7 @@
 
         @Override
         void execute(CompatibleScreensManifest csmTask) {
+            csmTask.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName())
 
             csmTask.screenDensity = scope.variantOutputData.getMainOutputFile().getFilter(
                     com.android.build.OutputFile.DENSITY)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
index 36af87b..fbd5ea0 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
@@ -17,7 +17,6 @@
 
 import com.android.SdkConstants
 import com.android.annotations.Nullable
-import com.android.build.gradle.internal.TaskManager
 import com.android.build.gradle.internal.core.GradleVariantConfiguration
 import com.android.build.gradle.internal.dsl.DexOptions
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
@@ -26,8 +25,10 @@
 import com.android.build.gradle.internal.tasks.BaseTask
 import com.android.build.gradle.internal.variant.ApkVariantData
 import com.android.build.gradle.internal.variant.TestVariantData
-import com.android.utils.StringHelper
+import com.android.ide.common.process.LoggedProcessOutputHandler
+import com.android.utils.FileUtils
 import org.codehaus.groovy.runtime.DefaultGroovyMethods
+import com.android.build.gradle.internal.PostCompilationData
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputDirectory
 import org.gradle.api.tasks.InputFile
@@ -77,9 +78,6 @@
     boolean multiDexEnabled = false
 
     @Input
-    boolean legacyMultiDexMode = false
-
-    @Input
     boolean optimize = true
 
     @InputFile @Optional
@@ -140,7 +138,7 @@
             boolean incremental) {
         File outFolder = getOutputFolder()
         if (!incremental) {
-            emptyFolder(outFolder)
+            FileUtils.emptyFolder(outFolder)
         }
 
         File tmpFolder = getTmpFolder()
@@ -161,14 +159,13 @@
                 getLibraries(),
                 outFolder,
                 getMultiDexEnabled(),
-                getLegacyMultiDexMode(),
                 getMainDexListFile(),
                 getDexOptions(),
                 getAdditionalParameters(),
                 tmpFolder,
                 incremental,
                 getOptimize(),
-                )
+                new LoggedProcessOutputHandler(getILogger()))
     }
 
 
@@ -176,9 +173,9 @@
 
         private final VariantScope scope;
 
-        private final TaskManager.PostCompilationData pcData;
+        private final PostCompilationData pcData;
 
-        public ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+        public ConfigAction(VariantScope scope, PostCompilationData pcData) {
             this.scope = scope;
             this.pcData = pcData;
         }
@@ -206,7 +203,8 @@
             boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode();
 
             variantData.dexTask = dexTask;
-            dexTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            dexTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder())
+            dexTask.setVariantName(config.getFullName())
             ConventionMappingHelper.map(dexTask, "outputFolder", new Callable<File>() {
                 @Override
                 public File call() throws Exception {
@@ -218,16 +216,15 @@
                             + "/tmp/dex/" + config.getDirName()));
             dexTask.setDexOptions(scope.getGlobalScope().getExtension().getDexOptions());
             dexTask.setMultiDexEnabled(isMultiDexEnabled);
-            dexTask.setLegacyMultiDexMode(isLegacyMultiDexMode);
             // dx doesn't work with receving --no-optimize in debug so we disable it for now.
             dexTask.setOptimize(true);//!variantData.variantConfiguration.buildType.debuggable
 
             // inputs
-            if (pcData.getInputDir() != null) {
-                ConventionMappingHelper.map(dexTask, "inputDir", pcData.getInputDir());
+            if (pcData.getInputDirCallable() != null) {
+                ConventionMappingHelper.map(dexTask, "inputDir", pcData.getInputDirCallable());
             }
-            ConventionMappingHelper.map(dexTask, "inputFiles", pcData.getInputFiles());
-            ConventionMappingHelper.map(dexTask, "libraries", pcData.getInputLibraries());
+            ConventionMappingHelper.map(dexTask, "inputFiles", pcData.getInputFilesCallable());
+            ConventionMappingHelper.map(dexTask, "libraries", pcData.getInputLibrariesCallable());
 
             if (isMultiDexEnabled && isLegacyMultiDexMode) {
                 // configure the dex task to receive the generated class list.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
index ddd5f5b..4867946 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
@@ -45,6 +45,22 @@
 import static com.android.SdkConstants.DOT_JAVA
 import static com.android.SdkConstants.UTF_8
 
+/**
+ * Task which extracts annotations from the source files, and writes them to one of
+ * two possible destinations:
+ * <ul>
+ *     <li> A "external annotations" file (pointed to by {@link ExtractAnnotations#output})
+ *          which records the annotations in a zipped XML format for use by the IDE and by
+ *          lint to associate the (source retention) annotations back with the compiled code</li>
+ *     <li> For any {@code Keep} annotated elements, a Proguard keep file (pointed to by
+ *          {@link ExtractAnnotations#proguard}, which lists APIs (classes, methods and fields)
+ *          that should not be removed even if no references in code are found to those APIs.</li>
+ * <p>
+ * We typically only extract external annotations when building libraries; ProGuard annotations
+ * are extracted when building libraries (to record in the AAR), <b>or</b> when building an
+ * app module where ProGuarding is enabled.
+ * </ul>
+ */
 class ExtractAnnotations extends AbstractAndroidCompile {
     public BaseVariantData variant
 
@@ -53,9 +69,15 @@
     public List<String> bootClasspath
 
     /** The output .zip file to write the annotations database to, if any */
+    @Optional
     @OutputFile
     public File output
 
+    /** The output proguard file to write any @Keep rules into, if any */
+    @Optional
+    @OutputFile
+    public File proguard
+
     /**
      * An optional pointer to an API file to filter the annotations by (any annotations
      * not found in the API file are considered hidden/not exposed.) This is in the same
@@ -137,15 +159,20 @@
                 }
             }
 
-            Extractor extractor = new Extractor(database, classDir,
-                    project.logger.isEnabled(LogLevel.INFO), false);
+
+            def displayInfo = project.logger.isEnabled(LogLevel.INFO)
+            def includeClassRetentionAnnotations = false
+            def sortAnnotations = false
+
+            Extractor extractor = new Extractor(database, classDir, displayInfo,
+                    includeClassRetentionAnnotations, sortAnnotations);
             extractor.extractFromProjectSource(parsedUnits)
             if (mergeJars != null) {
                 for (File jar : mergeJars) {
                     extractor.mergeExisting(jar);
                 }
             }
-            extractor.export(output)
+            extractor.export(output, proguard)
             extractor.removeTypedefClasses();
         } finally {
             if (environment != null) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
index 0216881..816c4c5 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
@@ -25,6 +25,8 @@
 import com.android.builder.compiling.BuildConfigGenerator
 import com.android.builder.core.VariantConfiguration
 import com.android.builder.model.ClassField
+import com.android.utils.FileUtils
+import com.google.common.base.Strings
 import com.google.common.collect.Lists
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.Optional
@@ -89,21 +91,16 @@
     }
 
     @TaskAction
-    void generate() {
+    void generate() throws IOException {
         // must clear the folder in case the packagename changed, otherwise,
         // there'll be two classes.
         File destinationDir = getSourceOutputDir()
-        emptyFolder(destinationDir)
+        FileUtils.emptyFolder(destinationDir)
 
         BuildConfigGenerator generator = new BuildConfigGenerator(
                 getSourceOutputDir(),
                 getBuildConfigPackageName());
 
-        String vn = getVersionName()
-        if (vn == null) {
-            vn = ""
-        }
-
         // 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
@@ -117,7 +114,7 @@
                 .addField("String", "BUILD_TYPE", "\"${getBuildTypeName()}\"")
                 .addField("String", "FLAVOR", "\"${getFlavorName()}\"")
                 .addField("int", "VERSION_CODE", Integer.toString(getVersionCode()))
-                .addField("String", "VERSION_NAME", "\"${vn}\"")
+                .addField("String", "VERSION_NAME", "\"${Strings.nullToEmpty(getVersionName())}\"")
                 .addItems(getItems())
 
         List<String> flavors = getFlavorNamesWithDimensionNames()
@@ -165,6 +162,7 @@
             VariantConfiguration variantConfiguration = variantData.variantConfiguration
 
             generateBuildConfigTask.androidBuilder = scope.globalScope.androidBuilder
+            generateBuildConfigTask.setVariantName(scope.getVariantConfiguration().getFullName())
 
             ConventionMappingHelper.map(generateBuildConfigTask, "buildConfigPackageName") {
                 variantConfiguration.originalApplicationId
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy
index 6fe016e..f55f769 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 package com.android.build.gradle.tasks
-
 import com.android.annotations.NonNull
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
@@ -22,7 +21,6 @@
 import com.android.build.gradle.internal.tasks.BaseTask
 import com.android.builder.compiling.ResValueGenerator
 import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
 import com.android.builder.model.ClassField
 import com.google.common.collect.Lists
 import org.gradle.api.tasks.Input
@@ -30,8 +28,6 @@
 import org.gradle.api.tasks.ParallelizableTask
 import org.gradle.api.tasks.TaskAction
 
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-
 @ParallelizableTask
 public class GenerateResValues extends BaseTask {
 
@@ -105,6 +101,7 @@
             VariantConfiguration variantConfiguration = scope.variantData.variantConfiguration
 
             generateResValuesTask.androidBuilder = scope.globalScope.androidBuilder
+            generateResValuesTask.setVariantName(variantConfiguration.getFullName())
 
             ConventionMappingHelper.map(generateResValuesTask, "items") {
                 variantConfiguration.resValues
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.groovy
deleted file mode 100644
index 87e97ad..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.groovy
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2014 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.build.gradle.internal.dsl.AaptOptions
-import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.builder.core.AaptPackageProcessBuilder
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-
-/**
- * Generates all metadata (like AndroidManifest.xml) necessary for a ABI dimension split APK.
- */
-@ParallelizableTask
-class GenerateSplitAbiRes extends BaseTask {
-
-    @Input
-    String applicationId
-
-    @Input
-    int versionCode
-
-    @Input
-    @Optional
-    String versionName
-
-    @Input
-    String outputBaseName
-
-    @Input
-    Set<String> splits
-
-    File outputDirectory
-
-    @OutputFiles
-    List<File> getOutputFiles() {
-        List<File> outputFiles = new ArrayList<>();
-        for (String split : getSplits()) {
-            outputFiles.add(getOutputFileForSplit(split))
-        }
-        return outputFiles;
-    }
-
-    @Input
-    boolean debuggable
-
-    @Nested
-    AaptOptions aaptOptions
-
-    @TaskAction
-    protected void doFullTaskAction() {
-
-        for (String split : getSplits()) {
-            String resPackageFileName = getOutputFileForSplit(split).getAbsolutePath()
-
-            File tmpDirectory = new File(getOutputDirectory(), "${getOutputBaseName()}")
-            tmpDirectory.mkdirs()
-
-            File tmpFile = new File(tmpDirectory, "AndroidManifest.xml")
-
-            OutputStreamWriter fileWriter = new OutputStreamWriter(new FileOutputStream(tmpFile), "UTF-8");
-            String versionNameToUse = getVersionName();
-            if (versionNameToUse == null) {
-                versionNameToUse = String.valueOf(getVersionCode())
-            }
-            try {
-                fileWriter.append(
-                        "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
-                                "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
-                                "      package=\"" + getApplicationId() + "\"\n" +
-                                "      android:versionCode=\"" + getVersionCode() + "\"\n" +
-                                "      android:versionName=\"" + versionNameToUse + "\"\n" +
-                                "      split=\"lib_${getOutputBaseName()}\">\n" +
-                                "       <uses-sdk android:minSdkVersion=\"21\"/>\n" +
-                                "</manifest> ")
-                fileWriter.flush()
-            } finally {
-                fileWriter.close()
-            }
-
-            AaptPackageProcessBuilder aaptPackageCommandBuilder =
-                    new AaptPackageProcessBuilder(tmpFile, getAaptOptions())
-                        .setDebuggable(getDebuggable())
-                        .setResPackageOutput(resPackageFileName);
-
-            getBuilder().processResources(aaptPackageCommandBuilder, false /* enforceUniquePackageName */)
-        }
-    }
-
-    private File getOutputFileForSplit(String split) {
-        return new File(getOutputDirectory(), "resources-${getOutputBaseName()}-${split}.ap_")
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.java
new file mode 100644
index 0000000..7ce9a68
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 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.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.tasks.BaseTask;
+import com.android.builder.core.AaptPackageProcessBuilder;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Generates all metadata (like AndroidManifest.xml) necessary for a ABI dimension split APK.
+ */
+@ParallelizableTask
+public class GenerateSplitAbiRes extends BaseTask {
+
+    private String applicationId;
+
+    private int versionCode;
+
+    private String versionName;
+
+    private String outputBaseName;
+
+    private Set<String> splits;
+
+    private File outputDirectory;
+
+    private boolean debuggable;
+
+    private AaptOptions aaptOptions;
+
+    @OutputFiles
+    public List<File> getOutputFiles() {
+        List<File> outputFiles = new ArrayList<File>();
+        for (String split : getSplits()) {
+            outputFiles.add(getOutputFileForSplit(split));
+        }
+
+        return outputFiles;
+    }
+
+    @TaskAction
+    protected void doFullTaskAction() throws IOException, InterruptedException, ProcessException {
+
+        for (String split : getSplits()) {
+            String resPackageFileName = getOutputFileForSplit(split).getAbsolutePath();
+
+            File tmpDirectory = new File(getOutputDirectory(), getOutputBaseName());
+            tmpDirectory.mkdirs();
+
+            File tmpFile = new File(tmpDirectory, "AndroidManifest.xml");
+
+            String versionNameToUse = getVersionName();
+            if (versionNameToUse == null) {
+                versionNameToUse = String.valueOf(getVersionCode());
+            }
+
+            OutputStreamWriter fileWriter = new OutputStreamWriter(new FileOutputStream(tmpFile), "UTF-8");
+            try {
+                fileWriter.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "      package=\"" + getApplicationId() + "\"\n"
+                        + "      android:versionCode=\"" + getVersionCode() + "\"\n"
+                        + "      android:versionName=\"" + versionNameToUse + "\"\n"
+                        + "      split=\"lib_" + getOutputBaseName() + "\">\n"
+                        + "       <uses-sdk android:minSdkVersion=\"21\"/>\n" + "</manifest> ");
+                fileWriter.flush();
+            } finally {
+                fileWriter.close();
+            }
+
+            AaptPackageProcessBuilder aaptPackageCommandBuilder =
+                    new AaptPackageProcessBuilder(tmpFile, getAaptOptions())
+                        .setDebuggable(isDebuggable())
+                        .setResPackageOutput(resPackageFileName);
+
+            getBuilder().processResources(
+                    aaptPackageCommandBuilder,
+                    false /* enforceUniquePackageName */,
+                    new LoggedProcessOutputHandler(getILogger()));
+        }
+    }
+
+    private File getOutputFileForSplit(final String split) {
+        return new File(getOutputDirectory(),
+                "resources-" + getOutputBaseName() + "-" + split + ".ap_");
+    }
+
+    @Input
+    public String getApplicationId() {
+        return applicationId;
+    }
+
+    public void setApplicationId(String applicationId) {
+        this.applicationId = applicationId;
+    }
+
+    @Input
+    public int getVersionCode() {
+        return versionCode;
+    }
+
+    public void setVersionCode(int versionCode) {
+        this.versionCode = versionCode;
+    }
+
+    @Input
+    @Optional
+    public String getVersionName() {
+        return versionName;
+    }
+
+    public void setVersionName(String versionName) {
+        this.versionName = versionName;
+    }
+
+    @Input
+    public String getOutputBaseName() {
+        return outputBaseName;
+    }
+
+    public void setOutputBaseName(String outputBaseName) {
+        this.outputBaseName = outputBaseName;
+    }
+
+    @Input
+    public Set<String> getSplits() {
+        return splits;
+    }
+
+    public void setSplits(Set<String> splits) {
+        this.splits = splits;
+    }
+
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    @Input
+    public boolean isDebuggable() {
+        return debuggable;
+    }
+
+    public void setDebuggable(boolean debuggable) {
+        this.debuggable = debuggable;
+    }
+
+    @Nested
+    public AaptOptions getAaptOptions() {
+        return aaptOptions;
+    }
+
+    public void setAaptOptions(AaptOptions aaptOptions) {
+        this.aaptOptions = aaptOptions;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.groovy
deleted file mode 100644
index 1a973e5..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.groovy
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2014 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.AbstractAndroidCompile
-import com.android.build.gradle.internal.tasks.FileSupplier
-import com.android.builder.core.AndroidBuilder
-import com.android.sdklib.BuildToolInfo
-import com.android.sdklib.repository.FullRevision
-import com.google.common.base.Charsets
-import com.google.common.io.Files
-import org.gradle.api.Task
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-/**
- * Jack task.
- */
-@ParallelizableTask
-public class JackTask extends AbstractAndroidCompile
-        implements FileSupplier, BinaryFileProviderTask {
-
-    final static FullRevision JACK_MIN_REV = new FullRevision(21, 1, 0)
-
-    AndroidBuilder androidBuilder
-
-    boolean isVerbose
-
-    boolean isDebugLog
-
-    @InputFile
-    File getJackExe() {
-        new File(androidBuilder.targetInfo.buildTools.getPath(BuildToolInfo.PathId.JACK))
-    }
-
-    @InputFiles
-    Collection<File> packagedLibraries
-
-    @InputFiles
-    @Optional
-    Collection<File> proguardFiles
-
-    @Input
-    boolean debug
-
-    File tempFolder
-
-    @OutputFile
-    File jackFile
-
-    @OutputFile
-    @Optional
-    File mappingFile
-
-    @Input
-    boolean multiDexEnabled
-
-    @Input
-    int minSdkVersion
-
-    @Input
-    @Optional
-    String javaMaxHeapSize
-
-    @TaskAction
-    void compile() {
-
-        if (System.getenv("USE_JACK_API") == null ||
-                !androidBuilder.convertByteCodeUsingJackApis(
-                getDestinationDir(),
-                getJackFile(),
-                getClasspath().files,
-                getPackagedLibraries(),
-                getSource().files,
-                getProguardFiles(),
-                getMappingFile(),
-                isMultiDexEnabled(),
-                getMinSdkVersion())) {
-
-            androidBuilder.convertByteCodeWithJack(
-                    getDestinationDir(),
-                    getJackFile(),
-                    computeBootClasspath(),
-                    getPackagedLibraries(),
-                    computeEcjOptionFile(),
-                    getProguardFiles(),
-                    getMappingFile(),
-                    isMultiDexEnabled(),
-                    getMinSdkVersion(),
-                    isDebugLog,
-                    getJavaMaxHeapSize())
-        }
-    }
-
-    private File computeEcjOptionFile() {
-        File folder = getTempFolder()
-        folder.mkdirs()
-        File file = new File(folder, "ecj-options.txt");
-
-        StringBuffer sb = new StringBuffer()
-
-        for (File sourceFile : getSource().files) {
-            sb.append(sourceFile.absolutePath).append('\n')
-        }
-
-        file.getParentFile().mkdirs()
-
-        Files.write(sb.toString(), file, Charsets.UTF_8)
-
-        return file
-    }
-
-    private String computeBootClasspath() {
-        StringBuilder sb = new StringBuilder()
-
-        boolean first = true;
-        for (File file : getClasspath().files) {
-            if (!first) {
-                sb.append(':')
-            } else {
-                first = false
-            }
-            sb.append(file.getAbsolutePath())
-        }
-
-        return sb.toString()
-    }
-
-    @Override
-    @NonNull
-    BinaryFileProviderTask.Artifact getArtifact() {
-        return new BinaryFileProviderTask.Artifact(
-                BinaryFileProviderTask.BinaryArtifactType.JACK,
-                getJackFile())
-    }
-
-    // ----- FileSupplierTask ----
-    @NonNull
-    @Override
-    Task getTask() {
-        return this
-    }
-
-    @Override
-    File get() {
-        return getMappingFile()
-    }
-}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.java
new file mode 100644
index 0000000..6f6848f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2014 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.TaskManager;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.GlobalScope;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.AbstractAndroidCompile;
+import com.android.build.gradle.internal.tasks.FileSupplier;
+import com.android.build.gradle.internal.variant.ApplicationVariantData;
+import com.android.build.gradle.tasks.factory.AbstractCompilesUtil;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.tasks.Job;
+import com.android.builder.tasks.JobContext;
+import com.android.builder.tasks.Task;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.repository.FullRevision;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+import org.gradle.api.Project;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Jack task.
+ */
+@ParallelizableTask
+public class JackTask extends AbstractAndroidCompile
+        implements FileSupplier, BinaryFileProviderTask, JavaResourcesProvider {
+
+    public static final FullRevision JACK_MIN_REV = new FullRevision(21, 1, 0);
+
+    private AndroidBuilder androidBuilder;
+
+    private boolean isVerbose;
+    private boolean isDebugLog;
+
+    private Collection<File> packagedLibraries;
+    private Collection<File> proguardFiles;
+    private Collection<File> jarJarRuleFiles;
+
+    private boolean debug;
+
+    private File tempFolder;
+    private File jackFile;
+    private File javaResourcesFolder;
+
+    private File mappingFile;
+
+    private boolean multiDexEnabled;
+
+    private int minSdkVersion;
+
+    private String javaMaxHeapSize;
+
+    private File incrementalDir;
+
+    @Override
+    @TaskAction
+    public void compile() {
+        final Job<Void> job = new Job<Void>(getName(), new Task<Void>() {
+            @Override
+            public void run(@NonNull Job<Void> job, @NonNull JobContext<Void> context)
+                    throws IOException {
+                try {
+                    JackTask.this.doMinification();
+                } catch (ProcessException e) {
+                    throw new IOException(e);
+                }
+            }
+
+        });
+        try {
+            SimpleWorkQueue.push(job);
+
+            // wait for the task completion.
+            job.await();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    private void doMinification() throws ProcessException, IOException {
+
+        if (System.getenv("USE_JACK_API") != null) {
+            androidBuilder.convertByteCodeUsingJackApis(
+                    getDestinationDir(),
+                    getJackFile(),
+                    getClasspath().getFiles(),
+                    getPackagedLibraries(),
+                    getSource().getFiles(),
+                    getProguardFiles(),
+                    getMappingFile(),
+                    getJarJarRuleFiles(),
+                    getIncrementalDir(),
+                    getJavaResourcesFolder(),
+                    isMultiDexEnabled(),
+                    getMinSdkVersion());
+        } else {
+            // no incremental support through command line so far.
+            androidBuilder.convertByteCodeWithJack(
+                    getDestinationDir(),
+                    getJackFile(),
+                    computeBootClasspath(),
+                    getPackagedLibraries(),
+                    computeEcjOptionFile(),
+                    getProguardFiles(),
+                    getMappingFile(),
+                    getJarJarRuleFiles(),
+                    isMultiDexEnabled(),
+                    getMinSdkVersion(),
+                    isDebugLog,
+                    getJavaMaxHeapSize(),
+                    new LoggedProcessOutputHandler(androidBuilder.getLogger()));
+        }
+
+    }
+
+    private File computeEcjOptionFile() throws IOException {
+        File folder = getTempFolder();
+        //noinspection ResultOfMethodCallIgnored
+        folder.mkdirs();
+        File file = new File(folder, "ecj-options.txt");
+
+        StringBuilder sb = new StringBuilder();
+
+        for (File sourceFile : getSource().getFiles()) {
+            sb.append(sourceFile.getAbsolutePath()).append("\n");
+        }
+
+        //noinspection ResultOfMethodCallIgnored
+        file.getParentFile().mkdirs();
+
+        Files.write(sb.toString(), file, Charsets.UTF_8);
+
+        return file;
+    }
+
+    private String computeBootClasspath() {
+        return Joiner.on(':').join(
+                Iterables.transform(getClasspath().getFiles(), GET_ABSOLUTE_PATH));
+    }
+
+    private static final Function<File, String> GET_ABSOLUTE_PATH = new Function<File, String>() {
+        @Override
+        public String apply(File file) {
+            return file.getAbsolutePath();
+        }
+    };
+
+
+    @InputFile
+    public File getJackExe() {
+        return new File(
+                androidBuilder.getTargetInfo().getBuildTools().getPath(BuildToolInfo.PathId.JACK));
+    }
+
+    public AndroidBuilder getAndroidBuilder() {
+        return androidBuilder;
+    }
+
+    public void setAndroidBuilder(AndroidBuilder androidBuilder) {
+        this.androidBuilder = androidBuilder;
+    }
+
+    public boolean getIsVerbose() {
+        return isVerbose;
+    }
+
+    public void setIsVerbose(boolean isVerbose) {
+        this.isVerbose = isVerbose;
+    }
+
+    public boolean getIsDebugLog() {
+        return isDebugLog;
+    }
+
+    public void setIsDebugLog(boolean isDebugLog) {
+        this.isDebugLog = isDebugLog;
+    }
+
+    @InputFiles
+    public Collection<File> getPackagedLibraries() {
+        return packagedLibraries;
+    }
+
+    public void setPackagedLibraries(Collection<File> packagedLibraries) {
+        this.packagedLibraries = packagedLibraries;
+    }
+
+    @InputFiles
+    @Optional
+    public Collection<File> getProguardFiles() {
+        return proguardFiles;
+    }
+
+    public void setProguardFiles(Collection<File> proguardFiles) {
+        this.proguardFiles = proguardFiles;
+    }
+
+    @InputFiles
+    @Optional
+    public Collection<File> getJarJarRuleFiles() {
+        return jarJarRuleFiles;
+    }
+
+    public void setJarJarRuleFiles(Collection<File> jarJarRuleFiles) {
+        this.jarJarRuleFiles = jarJarRuleFiles;
+    }
+
+    @Input
+    public boolean getDebug() {
+        return debug;
+    }
+
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    public File getTempFolder() {
+        return tempFolder;
+    }
+
+    public void setTempFolder(File tempFolder) {
+        this.tempFolder = tempFolder;
+    }
+
+    @OutputFile
+    public File getJackFile() {
+        return jackFile;
+    }
+
+    public void setJackFile(File jackFile) {
+        this.jackFile = jackFile;
+    }
+
+    @OutputFile
+    @Optional
+    public File getMappingFile() {
+        return mappingFile;
+    }
+
+    public void setMappingFile(File mappingFile) {
+        this.mappingFile = mappingFile;
+    }
+
+    @Input
+    public boolean isMultiDexEnabled() {
+        return multiDexEnabled;
+    }
+
+    public void setMultiDexEnabled(boolean multiDexEnabled) {
+        this.multiDexEnabled = multiDexEnabled;
+    }
+
+    @Input
+    public int getMinSdkVersion() {
+        return minSdkVersion;
+    }
+
+    public void setMinSdkVersion(int minSdkVersion) {
+        this.minSdkVersion = minSdkVersion;
+    }
+
+    @Input
+    @Optional
+    public String getJavaMaxHeapSize() {
+        return javaMaxHeapSize;
+    }
+
+    public void setJavaMaxHeapSize(String javaMaxHeapSize) {
+        this.javaMaxHeapSize = javaMaxHeapSize;
+    }
+
+    @Input
+    @Optional
+    public File getIncrementalDir() {
+        return incrementalDir;
+    }
+
+    public void setIncrementalDir(File incrementalDir) {
+        this.incrementalDir = incrementalDir;
+    }
+
+    @Input
+    @Optional
+    public File getJavaResourcesFolder() {
+        return javaResourcesFolder;
+    }
+
+    public void setJavaResourcesFolder(File javaResourcesFolder) {
+        this.javaResourcesFolder = javaResourcesFolder;
+    }
+
+    @NonNull
+    @Override
+    public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+        return ImmutableList.of(
+                new JavaResourcesLocation(Type.FOLDER, getDestinationDir()));
+    }
+
+    @Override
+    @NonNull
+    public BinaryFileProviderTask.Artifact getArtifact() {
+        return new BinaryFileProviderTask.Artifact(
+                BinaryFileProviderTask.BinaryArtifactType.JACK,
+                getJackFile());
+    }
+
+    // ----- FileSupplierTask ----
+    @NonNull
+    @Override
+    public org.gradle.api.Task getTask() {
+        return this;
+    }
+
+    @Override
+    public File get() {
+        return getMappingFile();
+    }
+
+    public static class ConfigAction implements TaskConfigAction<JackTask> {
+
+        private final VariantScope scope;
+        private final boolean isVerbose;
+        private final boolean isDebugLog;
+
+        public ConfigAction(VariantScope scope, boolean isVerbose, boolean isDebugLog) {
+            this.scope = scope;
+            this.isVerbose = isVerbose;
+            this.isDebugLog = isDebugLog;
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName("compile", "JavaWithJack");
+        }
+
+        @Override
+        public Class<JackTask> getType() {
+            return JackTask.class;
+        }
+
+        @Override
+        public void execute(JackTask jackTask) {
+            jackTask.setIsVerbose(isVerbose);
+            jackTask.setIsDebugLog(isDebugLog);
+
+            GlobalScope globalScope = scope.getGlobalScope();
+
+            jackTask.androidBuilder = globalScope.getAndroidBuilder();
+            jackTask.setJavaMaxHeapSize(
+                    globalScope.getExtension().getDexOptions().getJavaMaxHeapSize());
+
+            jackTask.setSource(scope.getVariantData().getJavaSources());
+
+            final GradleVariantConfiguration config = scope.getVariantData().getVariantConfiguration();
+            jackTask.setMultiDexEnabled(config.isMultiDexEnabled());
+            jackTask.setMinSdkVersion(config.getMinSdkVersion().getApiLevel());
+            jackTask.incrementalDir  = scope.getJackIncrementalDir();
+
+            // if the tested variant is an app, add its classpath. For the libraries,
+            // it's done automatically since the classpath includes the library output as a normal
+            // dependency.
+            if (scope.getTestedVariantData() instanceof ApplicationVariantData) {
+                ConventionMappingHelper.map(jackTask, "classpath", new Callable<FileCollection>() {
+                    @Override
+                    public FileCollection call() throws Exception {
+                        Project project = scope.getGlobalScope().getProject();
+                        return project.fileTree(scope.getJillRuntimeLibrariesDir()).plus(
+                                project.fileTree(
+                                        scope.getTestedVariantData().getScope()
+                                                .getJillRuntimeLibrariesDir())).plus(
+                                project.fileTree(
+                                        scope.getTestedVariantData().getScope().getJackClassesZip()
+                                ));
+                    }
+                });
+            } else {
+                ConventionMappingHelper.map(jackTask, "classpath", new Callable<FileCollection>() {
+                    @Override
+                    public FileCollection call() throws Exception {
+                        return scope.getGlobalScope().getProject().fileTree(
+                                scope.getJillRuntimeLibrariesDir());
+                    }
+                });
+            }
+
+            ConventionMappingHelper.map(jackTask, "packagedLibraries", new Callable<Collection<File>>() {
+                @Override
+                public Collection<File> call() throws Exception {
+                    return scope.getGlobalScope().getProject()
+                            .fileTree(scope.getJillPackagedLibrariesDir()).getFiles();
+                }
+            });
+
+            jackTask.setDestinationDir(scope.getJackDestinationDir());
+            jackTask.setJackFile(scope.getJackClassesZip());
+            jackTask.setTempFolder(new File(scope.getGlobalScope().getIntermediatesDir(),
+                    "/tmp/jack/" + scope.getVariantConfiguration().getDirName()));
+
+            jackTask.setJavaResourcesFolder(scope.getJavaResourcesDestinationDir());
+            scope.setJavaResourcesProvider(jackTask);
+
+            if (config.isMinifyEnabled()) {
+                ConventionMappingHelper.map(jackTask, "proguardFiles", new Callable<List<File>>() {
+                    @Override
+                    public List<File> call() throws Exception {
+                        // since all the output use the same resources, we can use the first output
+                        // to query for a proguard file.
+                        File sdkDir = scope.getGlobalScope().getSdkHandler().getAndCheckSdkFolder();
+                        File defaultProguardFile =  new File(sdkDir,
+                                SdkConstants.FD_TOOLS + File.separatorChar
+                                        + SdkConstants.FD_PROGUARD + File.separatorChar
+                                        + TaskManager.DEFAULT_PROGUARD_CONFIG_FILE);
+
+                        List<File> proguardFiles = config.getProguardFiles(true /*includeLibs*/,
+                                ImmutableList.of(defaultProguardFile));
+                        File proguardResFile = scope.getProcessAndroidResourcesProguardOutputFile();
+                        proguardFiles.add(proguardResFile);
+                        // for tested app, we only care about their aapt config since the base
+                        // configs are the same files anyway.
+                        if (scope.getTestedVariantData() != null) {
+                            proguardResFile = scope.getTestedVariantData().getScope()
+                                    .getProcessAndroidResourcesProguardOutputFile();
+                            proguardFiles.add(proguardResFile);
+                        }
+
+                        return proguardFiles;
+                    }
+                });
+
+                jackTask.mappingFile = new File(scope.getProguardOutputFolder(), "mapping.txt");
+            }
+
+
+            ConventionMappingHelper.map(jackTask, "jarJarRuleFiles", new Callable<List<File>>() {
+                @Override
+                public List<File> call() throws Exception {
+                    List<File> jarJarRuleFiles = Lists.newArrayListWithCapacity(
+                            config.getJarJarRuleFiles().size());
+                    Project project = scope.getGlobalScope().getProject();
+                    for (File file: config.getJarJarRuleFiles()) {
+                        jarJarRuleFiles.add(project.file(file));
+                    }
+                    return jarJarRuleFiles;
+                }
+            });
+
+            AbstractCompilesUtil.configureLanguageLevel(
+                    jackTask,
+                    scope.getGlobalScope().getExtension().getCompileOptions(),
+                    scope.getGlobalScope().getExtension().getCompileSdkVersion()
+            );
+
+            scope.getVariantData().jackTask = jackTask;
+            scope.getVariantData().javaCompilerTask = jackTask;
+            scope.getVariantData().mappingFileProviderTask = jackTask;
+            scope.getVariantData().binayFileProviderTask = jackTask;
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JavaResourcesProvider.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JavaResourcesProvider.java
new file mode 100644
index 0000000..99c2963
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JavaResourcesProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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.TaskFactory;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+
+/**
+ * Denotes a provider for the java resources ready to be packaged in the final variant APK.
+ *
+ * These java resources can be the merged results of all the packaged libraries resources as well
+ * as obfuscated resources (that match obfuscated java code loading such resources).
+ *
+ * Depending on the configuration of the variant, the actual provider can be the obfuscation task
+ * or the resource merging task or something else.
+ */
+public interface JavaResourcesProvider {
+
+    /**
+     * Denotes how the java resources are provided (as a jar or as folder).
+     */
+    enum Type { JAR, FOLDER }
+
+    final class JavaResourcesLocation {
+        final Type type;
+        final File location;
+
+        public JavaResourcesLocation(Type type, File location) {
+            this.type = type;
+            this.location = location;
+        }
+    }
+
+    @NonNull
+    ImmutableList<JavaResourcesLocation> getJavaResourcesLocations();
+
+    /**
+     * Adapter for tasks that are not created yet.
+     */
+    class Adapter {
+        @NonNull
+        public static JavaResourcesProvider build(@NonNull final TaskFactory tasks,
+                @NonNull final AndroidTask<? extends JavaResourcesProvider> androidTask) {
+            return new JavaResourcesProvider() {
+                @NonNull
+                @Override
+                public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+                    return androidTask.get(tasks).getJavaResourcesLocations();
+                }
+            };
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.groovy
deleted file mode 100644
index 9c591bb..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.groovy
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2014 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.tasks.BaseTask
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.DexOptions
-import com.android.ide.common.internal.WaitableExecutor
-import com.android.sdklib.repository.FullRevision
-import com.google.common.base.Charsets
-import com.google.common.collect.Lists
-import com.google.common.collect.Sets
-import com.google.common.hash.HashCode
-import com.google.common.hash.HashFunction
-import com.google.common.hash.Hashing
-import com.google.common.io.Files
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs
-
-import java.util.concurrent.Callable
-
-import static com.android.build.gradle.tasks.JackTask.JACK_MIN_REV
-
-@ParallelizableTask
-public class JillTask extends BaseTask {
-
-    // ----- PUBLIC TASK API -----
-
-    // ----- PRIVATE TASK API -----
-    @Input
-    String getBuildToolsVersion() {
-        getBuildTools().getRevision()
-    }
-
-    @SuppressWarnings("GroovyUnusedDeclaration")
-    @InputFiles
-    Collection<File> inputLibs
-
-    @OutputDirectory
-    File outputFolder
-
-    @Nested
-    com.android.build.gradle.internal.dsl.DexOptions dexOptions
-
-    @TaskAction
-    void taskAction(IncrementalTaskInputs taskInputs) {
-        FullRevision revision = getBuilder().targetInfo.buildTools.revision
-        if (revision.compareTo(JACK_MIN_REV) < 0) {
-            throw new RuntimeException("Jack requires Build Tools ${JACK_MIN_REV.toString()} or later")
-        }
-
-        final File outFolder = getOutputFolder()
-
-        // 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)
-        }
-
-        final Set<String> hashs = Sets.newHashSet()
-        final WaitableExecutor<Void> executor = new WaitableExecutor<Void>()
-        final List<File> inputFileDetails = Lists.newArrayList()
-
-        final AndroidBuilder builder = getBuilder()
-
-        taskInputs.outOfDate { final change ->
-            inputFileDetails.add(change.file)
-        }
-
-        for (final File file : inputFileDetails) {
-            Callable<Void> action = new JillCallable(file, hashs, outFolder, builder)
-            executor.execute(action)
-        }
-
-        taskInputs.removed { change ->
-            //noinspection GroovyAssignabilityCheck
-            File jackFile = getJackFileName(outFolder, change.file)
-            jackFile.delete()
-        }
-
-        executor.waitForTasksWithQuickFail(false)
-    }
-
-    private final class JillCallable implements Callable<Void> {
-        @NonNull
-        private final File fileToProcess
-        @NonNull
-        private final Set<String> hashs
-        @NonNull
-        private final DexOptions options = getDexOptions()
-        @NonNull
-        final File outFolder
-        @NonNull
-        private final AndroidBuilder builder
-
-        private JillCallable(
-                @NonNull File file,
-                @NonNull Set<String> hashs,
-                @NonNull File outFolder,
-                @NonNull AndroidBuilder builder) {
-            this.fileToProcess = file
-            this.hashs = hashs
-            this.outFolder = outFolder
-            this.builder = builder
-        }
-
-        @Override
-        Void call() throws Exception {
-            // TODO remove once we can properly add a library as a dependency of its test.
-            String hash = getFileHash(fileToProcess)
-
-            synchronized (hashs) {
-                if (hashs.contains(hash)) {
-                    return null
-                }
-
-                hashs.add(hash)
-            }
-
-            //noinspection GroovyAssignabilityCheck
-            File jackFile = getJackFileName(outFolder, fileToProcess)
-            //noinspection GroovyAssignabilityCheck
-            builder.convertLibraryToJack(fileToProcess, jackFile, options)
-
-            return null
-        }
-    }
-
-    /**
-     * Returns the hash of a file.
-     * @param file the file to hash
-     * @return
-     */
-    private static String getFileHash(@NonNull File file) {
-        HashCode hashCode = Files.hash(file, Hashing.sha1())
-        return hashCode.toString()
-    }
-
-    /**
-     * Returns a unique File for the converted library, even
-     * if there are 2 libraries with the same file names (but different
-     * paths)
-     *
-     * @param outFolder the output folder.
-     * @param inputFile the library
-     * @return
-     */
-    @NonNull
-    static File getJackFileName(@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.
-        String input = inputFile.getAbsolutePath()
-        HashFunction hashFunction = Hashing.sha1()
-        HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE)
-
-        return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR)
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.java
new file mode 100644
index 0000000..5340a5c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2014 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.DexOptions;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.GlobalScope;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.BaseTask;
+import com.android.builder.core.AndroidBuilder;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.sdklib.repository.FullRevision;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+
+import org.gradle.api.Action;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class JillTask extends BaseTask {
+
+    private Collection<File> inputLibs;
+
+    private File outputFolder;
+
+    private DexOptions dexOptions;
+
+    @TaskAction
+    public void taskAction(IncrementalTaskInputs taskInputs)
+            throws LoggedErrorException, InterruptedException, IOException {
+        FullRevision revision = getBuilder().getTargetInfo().getBuildTools().getRevision();
+        if (revision.compareTo(JackTask.JACK_MIN_REV) < 0) {
+            throw new RuntimeException(
+                    "Jack requires Build Tools " + JackTask.JACK_MIN_REV.toString()
+                            + " or later");
+        }
+
+        final File outFolder = getOutputFolder();
+
+        // 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()) {
+            FileUtils.emptyFolder(outFolder);
+        }
+
+        final Set<String> hashs = Sets.newHashSet();
+        final WaitableExecutor<Void> executor = new WaitableExecutor<Void>();
+        final List<File> inputFileDetails = Lists.newArrayList();
+
+        final AndroidBuilder builder = getBuilder();
+
+        taskInputs.outOfDate(new Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails change) {
+                inputFileDetails.add(change.getFile());
+            }
+        });
+
+        for (final File file : inputFileDetails) {
+            Callable<Void> action = new JillCallable(this, file, hashs, outFolder, builder);
+            executor.execute(action);
+        }
+
+        taskInputs.removed(new Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails change) {
+                File jackFile = getJackFileName(outFolder, ((InputFileDetails) change).getFile());
+                //noinspection ResultOfMethodCallIgnored
+                jackFile.delete();
+            }
+        });
+
+        executor.waitForTasksWithQuickFail(false);
+    }
+
+    @Input
+    public String getBuildToolsVersion() {
+        return getBuildTools().getRevision().toString();
+    }
+
+    @InputFiles
+    public Collection<File> getInputLibs() {
+        return inputLibs;
+    }
+
+    public void setInputLibs(Collection<File> inputLibs) {
+        this.inputLibs = inputLibs;
+    }
+
+    @OutputDirectory
+    public File getOutputFolder() {
+        return outputFolder;
+    }
+
+    public void setOutputFolder(File outputFolder) {
+        this.outputFolder = outputFolder;
+    }
+
+    @Nested
+    public DexOptions getDexOptions() {
+        return dexOptions;
+    }
+
+    public void setDexOptions(DexOptions dexOptions) {
+        this.dexOptions = dexOptions;
+    }
+
+    private final class JillCallable implements Callable<Void> {
+
+        @NonNull
+        private final File fileToProcess;
+
+        @NonNull
+        private final Set<String> hashs;
+
+        @NonNull
+        private final com.android.builder.core.DexOptions options = getDexOptions();
+
+        @NonNull
+        private final File outFolder;
+
+        @NonNull
+        private final AndroidBuilder builder;
+
+        private JillCallable(JillTask enclosing, @NonNull File file, @NonNull Set<String> hashs,
+                @NonNull File outFolder, @NonNull AndroidBuilder builder) {
+            this.fileToProcess = file;
+            this.hashs = hashs;
+            this.outFolder = outFolder;
+            this.builder = builder;
+        }
+
+        @Override
+        public Void call() throws Exception {
+            // TODO remove once we can properly add a library as a dependency of its test.
+            String hash = getFileHash(fileToProcess);
+
+            synchronized (hashs) {
+                if (hashs.contains(hash)) {
+                    return null;
+                }
+
+                hashs.add(hash);
+            }
+
+            //noinspection GroovyAssignabilityCheck
+            File jackFile = getJackFileName(outFolder, fileToProcess);
+            //noinspection GroovyAssignabilityCheck
+            builder.convertLibraryToJack(fileToProcess, jackFile, options,
+                    new LoggedProcessOutputHandler(builder.getLogger()));
+
+            return null;
+        }
+
+        @NonNull
+        public final File getOutFolder() {
+            return outFolder;
+        }
+    }
+
+    /**
+     * Returns the hash of a file.
+     *
+     * @param file the file to hash
+     */
+    private static String getFileHash(@NonNull File file) throws IOException {
+        HashCode hashCode = Files.hash(file, Hashing.sha1());
+        return hashCode.toString();
+    }
+
+    /**
+     * Returns a unique File for the converted library, even if there are 2 libraries with the same
+     * file names (but different paths)
+     *
+     * @param outFolder the output folder.
+     * @param inputFile the library
+     */
+    @NonNull
+    public static File getJackFileName(@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.
+        String input = inputFile.getAbsolutePath();
+        HashFunction hashFunction = Hashing.sha1();
+        HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE);
+
+        return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR);
+    }
+
+    public static class RuntimeTaskConfigAction implements TaskConfigAction<JillTask> {
+
+        private final VariantScope variantScope;
+
+        // TODO: If task can be shared between variants, change to GlobalScope.
+        public RuntimeTaskConfigAction(VariantScope scope) {
+            this.variantScope = scope;
+        }
+
+        @Override
+        public String getName() {
+            return variantScope.getTaskName("jill", "RuntimeLibraries");
+        }
+
+        @Override
+        public Class<JillTask> getType() {
+            return JillTask.class;
+        }
+
+        @Override
+        public void execute(JillTask jillTask) {
+            final GlobalScope globalScope = variantScope.getGlobalScope();
+            final AndroidBuilder androidBuilder = globalScope.getAndroidBuilder();
+
+            jillTask.setAndroidBuilder(androidBuilder);
+            jillTask.setVariantName(variantScope.getVariantConfiguration().getFullName());
+            jillTask.setDexOptions(globalScope.getExtension().getDexOptions());
+
+            ConventionMappingHelper.map(jillTask, "inputLibs", new Callable<List<File>>() {
+                @Override
+                public List<File> call() throws Exception {
+                    return androidBuilder.getBootClasspath();
+                }
+            });
+
+            jillTask.setOutputFolder(variantScope.getJillRuntimeLibrariesDir());
+        }
+    }
+
+    public static class PackagedConfigAction implements TaskConfigAction<JillTask> {
+
+        private final VariantScope variantScope;
+
+        public PackagedConfigAction(VariantScope scope) {
+            this.variantScope = scope;
+        }
+
+        @Override
+        public String getName() {
+            return variantScope.getTaskName("jill", "PackagedLibraries");
+        }
+
+        @Override
+        public Class<JillTask> getType() {
+            return JillTask.class;
+        }
+
+        @Override
+        public void execute(JillTask jillTask) {
+            final GlobalScope globalScope = variantScope.getGlobalScope();
+            final AndroidBuilder androidBuilder = globalScope.getAndroidBuilder();
+
+            jillTask.setAndroidBuilder(androidBuilder);
+            jillTask.setVariantName(variantScope.getVariantConfiguration().getFullName());
+            jillTask.setDexOptions(globalScope.getExtension().getDexOptions());
+
+            ConventionMappingHelper.map(jillTask, "inputLibs", new Callable<Set<File>>() {
+                @Override
+                public Set<File> call() throws Exception {
+                    return androidBuilder.getPackagedJars(variantScope.getVariantConfiguration());
+                }
+            });
+
+            jillTask.setOutputFolder(variantScope.getJillPackagedLibrariesDir());
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
index 94b4793..d3b4e42 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
@@ -15,13 +15,10 @@
  */
 
 package com.android.build.gradle.tasks
-
 import com.android.annotations.NonNull
 import com.android.annotations.Nullable
 import com.android.build.gradle.internal.LintGradleClient
 import com.android.build.gradle.internal.dsl.LintOptions
-import com.android.build.gradle.internal.model.ModelBuilder
-import com.android.build.gradle.internal.scope.AndroidTask
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
 import com.android.build.gradle.internal.tasks.DefaultAndroidTask
@@ -41,12 +38,12 @@
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.tasks.ParallelizableTask
 import org.gradle.api.tasks.TaskAction
+import org.gradle.tooling.provider.model.ToolingModelBuilder
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
 
 @ParallelizableTask
 public class Lint extends DefaultAndroidTask {
     @NonNull private LintOptions mLintOptions
-    @Nullable private String mVariantName
     @Nullable private File mSdkHome
     private boolean mFatalOnly
     private ToolingModelBuilderRegistry mToolingRegistry
@@ -59,10 +56,6 @@
         mSdkHome = sdkHome
     }
 
-    public void setVariantName(@NonNull String variantName) {
-        mVariantName = variantName
-    }
-
     void setToolingRegistry(ToolingModelBuilderRegistry toolingRegistry) {
         mToolingRegistry = toolingRegistry
     }
@@ -75,8 +68,8 @@
     @TaskAction
     public void lint() {
         def modelProject = createAndroidProject(project)
-        if (mVariantName != null) {
-            lintSingleVariant(modelProject, mVariantName)
+        if (getVariantName() != null) {
+            lintSingleVariant(modelProject, getVariantName())
         } else {
             lintAllVariants(modelProject)
         }
@@ -230,7 +223,7 @@
 
     private AndroidProject createAndroidProject(@NonNull Project gradleProject) {
         String modelName = AndroidProject.class.getName()
-        ModelBuilder modelBuilder = mToolingRegistry.getBuilder(modelName) as ModelBuilder
+        ToolingModelBuilder modelBuilder = mToolingRegistry.getBuilder(modelName)
         assert modelBuilder != null
         return (AndroidProject) modelBuilder.buildAll(modelName, gradleProject)
     }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
index 3195d82..ab6d2dc 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
@@ -31,6 +31,7 @@
 import com.android.ide.common.res2.FileValidity
 import com.android.ide.common.res2.MergedAssetWriter
 import com.android.ide.common.res2.MergingException
+import com.android.utils.FileUtils
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.ParallelizableTask
@@ -65,7 +66,7 @@
     protected void doFullTaskAction() {
         // this is full run, clean the previous output
         File destinationDir = getOutputDir()
-        emptyFolder(destinationDir)
+        FileUtils.emptyFolder(destinationDir)
 
         List<AssetSet> assetSets = getInputAssetSets()
 
@@ -146,6 +147,9 @@
             println e.getMessage()
             merger.cleanBlob(getIncrementalFolder())
             throw new ResourceException(e.getMessage(), e)
+        } finally {
+            // some clean up after the task to help multi variant/module builds.
+            fileValidity.clear();
         }
     }
 
@@ -178,6 +182,7 @@
             variantData.mergeAssetsTask = mergeAssetsTask
 
             mergeAssetsTask.androidBuilder = scope.globalScope.androidBuilder
+            mergeAssetsTask.setVariantName(variantConfig.getFullName())
             mergeAssetsTask.incrementalFolder =
                     new File(
                             "$scope.globalScope.buildDir/${AndroidProject.FD_INTERMEDIATES}/incremental/mergeAssets/${variantConfig.dirName}")
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy
index 63a84ab..277638c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy
@@ -20,14 +20,11 @@
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantOutputScope
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.PrepareDependenciesTask
 import com.android.build.gradle.internal.variant.ApkVariantOutputData
 import com.android.build.gradle.internal.variant.BaseVariantData
 import com.android.build.gradle.internal.variant.BaseVariantOutputData
 import com.android.builder.core.VariantConfiguration
 import com.android.builder.dependency.LibraryDependency
-import com.android.builder.model.AndroidProject
 import com.android.manifmerger.ManifestMerger2
 import com.google.common.collect.Lists
 import org.gradle.api.tasks.Input
@@ -36,7 +33,6 @@
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.ParallelizableTask
 
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
 import static com.android.builder.model.AndroidProject.FD_OUTPUTS
 
 /**
@@ -175,6 +171,7 @@
             variantOutputData.manifestProcessorTask = processManifestTask
 
             processManifestTask.androidBuilder = scope.globalScope.androidBuilder
+            processManifestTask.setVariantName(config.getFullName())
 
             processManifestTask.dependsOn variantData.prepareDependenciesTask
             if (variantData.generateApkDataTask != null) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
deleted file mode 100644
index 3a56b00..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
+++ /dev/null
@@ -1,274 +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.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.IncrementalTask
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.VariantType
-import com.android.builder.png.QueuedCruncher
-import com.android.ide.common.internal.PngCruncher
-import com.android.ide.common.res2.FileStatus
-import com.android.ide.common.res2.FileValidity
-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 com.android.sdklib.BuildToolInfo
-import com.android.sdklib.repository.FullRevision
-import com.google.common.collect.Lists
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.OutputFile
-
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@ParallelizableTask
-public class MergeResources extends IncrementalTask {
-
-    // ----- PUBLIC TASK API -----
-
-    /** Directory to write the merged resources to */
-    @OutputDirectory
-    File outputDir
-
-    /** Optional file to write any publicly imported resource types and names to */
-    @Optional
-    @OutputFile
-    File publicFile
-
-    // ----- PRIVATE TASK API -----
-
-    // fake input to detect changes. Not actually used by the task
-    @InputFiles
-    Iterable<File> getRawInputFolders() {
-        return flattenSourceSets(getInputResourceSets())
-    }
-
-    @Input
-    String getBuildToolsVersion() {
-        getBuildTools().getRevision()
-    }
-
-    @Input
-    boolean process9Patch
-
-    @Input
-    boolean crunchPng
-
-    @Input
-    boolean useNewCruncher;
-
-    @Input
-    boolean insertSourceMarkers = true
-
-    @Input
-    boolean normalizeResources
-
-    // actual inputs
-    List<ResourceSet> inputResourceSets
-
-    private final FileValidity<ResourceSet> fileValidity = new FileValidity<ResourceSet>();
-
-    @Override
-    protected boolean isIncremental() {
-        return true
-    }
-
-    private PngCruncher getCruncher() {
-        if (getUseNewCruncher()) {
-            if (builder.getTargetInfo().buildTools.getRevision().getMajor() >= 22) {
-                return QueuedCruncher.Builder.INSTANCE.newCruncher(
-                        builder.getTargetInfo().buildTools.getPath(
-                                BuildToolInfo.PathId.AAPT), builder.getLogger())
-            }
-            logger.info("New PNG cruncher will be enabled with build tools 22 and above.")
-        }
-        return builder.aaptCruncher;
-    }
-
-    @Override
-    protected void doFullTaskAction() {
-        // this is full run, clean the previous output
-        File destinationDir = getOutputDir()
-        emptyFolder(destinationDir)
-
-        List<ResourceSet> resourceSets = getInputResourceSets()
-
-        // create a new merger and populate it with the sets.
-        ResourceMerger merger = new ResourceMerger()
-
-        try {
-            for (ResourceSet resourceSet : resourceSets) {
-                resourceSet.setNormalizeResources(normalizeResources)
-                // set needs to be loaded.
-                resourceSet.loadFromFiles(getILogger())
-                merger.addDataSet(resourceSet)
-            }
-
-            // get the merged set and write it down.
-            MergedResourceWriter writer = new MergedResourceWriter(
-                    destinationDir, getCruncher(),
-                    getCrunchPng(), getProcess9Patch(), getPublicFile())
-            writer.setInsertSourceMarkers(getInsertSourceMarkers())
-
-            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()
-        try {
-            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()
-            for (ResourceSet resourceSet : resourceSets) {
-                resourceSet.setNormalizeResources(normalizeResources)
-            }
-
-            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(), getILogger())) {
-                        project.logger.info(
-                                String.format("Failed to process %s event! Full task run",
-                                        entry.getValue()))
-                        doFullTaskAction()
-                        return
-                    }
-                }
-            }
-
-            MergedResourceWriter writer = new MergedResourceWriter(
-                    getOutputDir(), getCruncher(),
-                    getCrunchPng(), getProcess9Patch(), getPublicFile())
-            writer.setInsertSourceMarkers(getInsertSourceMarkers())
-            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)
-        }
-    }
-
-    public static class ConfigAction implements TaskConfigAction<MergeResources> {
-
-        @NonNull
-        VariantScope scope
-        @NonNull
-        String taskNamePrefix
-        @NonNull
-        File outputLocation;
-        boolean includeDependencies;
-        boolean process9Patch;
-
-        ConfigAction(VariantScope scope, String taskNamePrefix, File outputLocation,
-                boolean includeDependencies, boolean process9Patch) {
-            this.scope = scope
-            this.taskNamePrefix = taskNamePrefix
-            this.outputLocation = outputLocation
-            this.includeDependencies = includeDependencies
-            this.process9Patch = process9Patch
-
-            scope.setMergeResourceOutputDir(outputLocation)
-        }
-
-        @Override
-        String getName() {
-            return scope.getTaskName(taskNamePrefix, "Resources")
-        }
-
-        @Override
-        Class<MergeResources> getType() {
-            return MergeResources
-        }
-
-        @Override
-        void execute(MergeResources mergeResourcesTask) {
-            BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-
-            mergeResourcesTask.androidBuilder = scope.globalScope.androidBuilder
-            mergeResourcesTask.incrementalFolder = new File(
-                    "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/incremental/" +
-                            "${taskNamePrefix}Resources/${variantData.variantConfiguration.dirName}")
-
-            mergeResourcesTask.process9Patch = process9Patch
-            mergeResourcesTask.crunchPng = scope.globalScope.extension.aaptOptions.getCruncherEnabled()
-            mergeResourcesTask.normalizeResources = scope.globalScope.extension.buildToolsRevision.compareTo(new FullRevision(21, 0, 0)) < 0
-
-            ConventionMappingHelper.map(mergeResourcesTask, "useNewCruncher") { scope.globalScope.getExtension().aaptOptions.useNewCruncher }
-
-            ConventionMappingHelper.map(mergeResourcesTask, "inputResourceSets") {
-                List<File> generatedResFolders = Lists.newArrayList(
-                        scope.getRenderscriptResOutputDir(),
-                        scope.getGeneratedResOutputDir())
-                if (variantData.extraGeneratedResFolders != null) {
-                    generatedResFolders += variantData.extraGeneratedResFolders
-                }
-                if (variantData.generateApkDataTask != null &&
-                        variantData.getVariantConfiguration().getBuildType().isEmbedMicroApp()) {
-                    generatedResFolders.add(variantData.generateApkDataTask.getResOutputDir())
-                }
-                variantData.variantConfiguration.getResourceSets(generatedResFolders,
-                        includeDependencies)
-            }
-
-            ConventionMappingHelper.map(mergeResourcesTask, "outputDir") {
-                outputLocation ?: scope.getDefaultMergeResourcesOutputDir()
-            }
-            variantData.mergeResourcesTask = mergeResourcesTask
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.java
new file mode 100644
index 0000000..5805f27
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.java
@@ -0,0 +1,445 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.IncrementalTask;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.png.QueuedCruncher;
+import com.android.builder.png.VectorDrawableRenderer;
+import com.android.ide.common.internal.PngCruncher;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.res2.FileStatus;
+import com.android.ide.common.res2.FileValidity;
+import com.android.ide.common.res2.GeneratedResourceSet;
+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.ResourcePreprocessor;
+import com.android.ide.common.res2.ResourceSet;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.repository.FullRevision;
+import com.android.utils.FileUtils;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.StopExecutionException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class MergeResources extends IncrementalTask {
+
+    /**
+     * The first version of build tools that normalizes resources when packaging the APK.
+     *
+     * <p>This means that e.g. drawable-hdpi becomes drawable-hdpi-v4 to make it clear it was not
+     * available before API 4.
+     */
+    public static final FullRevision NORMALIZE_RESOURCES_BUILD_TOOLS = new FullRevision(21, 0, 0);
+
+    // ----- PUBLIC TASK API -----
+
+    /**
+     * Directory to write the merged resources to
+     */
+    private File outputDir;
+
+    // ----- PRIVATE TASK API -----
+
+    /**
+     * Optional file to write any publicly imported resource types and names to
+     */
+    private File publicFile;
+
+    private boolean process9Patch;
+
+    private boolean crunchPng;
+
+    private boolean useNewCruncher;
+
+    private boolean insertSourceMarkers = true;
+
+    private boolean normalizeResources;
+
+    private ResourcePreprocessor preprocessor;
+
+    // actual inputs
+    private List<ResourceSet> inputResourceSets;
+
+    private final FileValidity<ResourceSet> fileValidity = new FileValidity<ResourceSet>();
+
+    // fake input to detect changes. Not actually used by the task
+    @InputFiles
+    public Iterable<File> getRawInputFolders() {
+        return flattenSourceSets(getInputResourceSets());
+    }
+
+    @Input
+    public String getBuildToolsVersion() {
+        return getBuildTools().getRevision().toString();
+    }
+
+    @Override
+    protected boolean isIncremental() {
+        return true;
+    }
+
+    private PngCruncher getCruncher() {
+        if (getUseNewCruncher()) {
+            if (getBuilder().getTargetInfo().getBuildTools().getRevision().getMajor() >= 22) {
+                return QueuedCruncher.Builder.INSTANCE.newCruncher(
+                        getBuilder().getTargetInfo().getBuildTools().getPath(
+                                BuildToolInfo.PathId.AAPT), getILogger());
+            }
+            getLogger().info("New PNG cruncher will be enabled with build tools 22 and above.");
+        }
+        return getBuilder().getAaptCruncher(new LoggedProcessOutputHandler(getBuilder().getLogger()));
+    }
+
+    @Override
+    protected void doFullTaskAction() throws IOException {
+        // this is full run, clean the previous output
+        File destinationDir = getOutputDir();
+        FileUtils.emptyFolder(destinationDir);
+
+        List<ResourceSet> resourceSets = getConfiguredResourceSets();
+
+        // create a new merger and populate it with the sets.
+        ResourceMerger merger = new ResourceMerger();
+
+        try {
+            for (ResourceSet resourceSet : resourceSets) {
+                resourceSet.loadFromFiles(getILogger());
+                merger.addDataSet(resourceSet);
+            }
+
+            // get the merged set and write it down.
+            MergedResourceWriter writer = new MergedResourceWriter(
+                    destinationDir, getCruncher(),
+                    getCrunchPng(), getProcess9Patch(), getPublicFile(), preprocessor);
+            writer.setInsertSourceMarkers(getInsertSourceMarkers());
+
+            merger.mergeData(writer, false /*doCleanUp*/);
+
+            // No exception? Write the known state.
+            merger.writeBlobTo(getIncrementalFolder(), writer);
+            throw new StopExecutionException("Stop for now.");
+        } catch (MergingException e) {
+            System.out.println(e.getMessage());
+            merger.cleanBlob(getIncrementalFolder());
+            throw new ResourceException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws IOException {
+        // create a merger and load the known state.
+        ResourceMerger merger = new ResourceMerger();
+        try {
+            if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
+                doFullTaskAction();
+                return;
+            }
+
+            for (ResourceSet resourceSet : merger.getDataSets()) {
+                resourceSet.setNormalizeResources(normalizeResources);
+                resourceSet.setPreprocessor(preprocessor);
+            }
+
+            List<ResourceSet> resourceSets = getConfiguredResourceSets();
+
+            // 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.
+            if (!merger.checkValidUpdate(resourceSets)) {
+                getLogger().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.getStatus() == FileValidity.FileStatus.UNKNOWN_FILE) {
+                    doFullTaskAction();
+                    return;
+                } else if (fileValidity.getStatus() == FileValidity.FileStatus.VALID_FILE) {
+                    if (!fileValidity.getDataSet().updateWith(
+                            fileValidity.getSourceFile(), changedFile, entry.getValue(),
+                            getILogger())) {
+                        getLogger().info(
+                                String.format("Failed to process %s event! Full task run",
+                                        entry.getValue()));
+                        doFullTaskAction();
+                        return;
+                    }
+                }
+            }
+
+            MergedResourceWriter writer = new MergedResourceWriter(
+                    getOutputDir(), getCruncher(),
+                    getCrunchPng(), getProcess9Patch(), getPublicFile(), preprocessor);
+            writer.setInsertSourceMarkers(getInsertSourceMarkers());
+            merger.mergeData(writer, false /*doCleanUp*/);
+            // No exception? Write the known state.
+            merger.writeBlobTo(getIncrementalFolder(), writer);
+        } catch (MergingException e) {
+            merger.cleanBlob(getIncrementalFolder());
+            throw new ResourceException(e.getMessage(), e);
+        } finally {
+            // some clean up after the task to help multi variant/module builds.
+            fileValidity.clear();
+        }
+    }
+
+    @NonNull
+    private List<ResourceSet> getConfiguredResourceSets() {
+        List<ResourceSet> resourceSets = Lists.newArrayList(getInputResourceSets());
+        List<ResourceSet> generatedSets = Lists.newArrayListWithCapacity(resourceSets.size());
+
+        for (ResourceSet resourceSet : resourceSets) {
+            resourceSet.setNormalizeResources(normalizeResources);
+            resourceSet.setPreprocessor(preprocessor);
+            ResourceSet generatedSet = new GeneratedResourceSet(resourceSet);
+            resourceSet.setGeneratedSet(generatedSet);
+            generatedSets.add(generatedSet);
+        }
+
+        // Put all generated sets at the start of the list.
+        resourceSets.addAll(0, generatedSets);
+        return resourceSets;
+    }
+
+    @Input
+    public boolean isProcess9Patch() {
+        return process9Patch;
+    }
+
+    @Input
+    public boolean isCrunchPng() {
+        return crunchPng;
+    }
+
+    @Input
+    public boolean isUseNewCruncher() {
+        return useNewCruncher;
+    }
+
+    @Input
+    public boolean isInsertSourceMarkers() {
+        return insertSourceMarkers;
+    }
+
+    @Input
+    public boolean isNormalizeResources() {
+        return normalizeResources;
+    }
+
+    public void setNormalizeResources(boolean normalizeResources) {
+        this.normalizeResources = normalizeResources;
+    }
+
+    public List<ResourceSet> getInputResourceSets() {
+        return inputResourceSets;
+    }
+
+    public void setInputResourceSets(
+            List<ResourceSet> inputResourceSets) {
+        this.inputResourceSets = inputResourceSets;
+    }
+
+    public boolean getUseNewCruncher() {
+        return useNewCruncher;
+    }
+
+    public void setUseNewCruncher(boolean useNewCruncher) {
+        this.useNewCruncher = useNewCruncher;
+    }
+
+    @OutputDirectory
+    public File getOutputDir() {
+        return outputDir;
+    }
+
+    public void setOutputDir(File outputDir) {
+        this.outputDir = outputDir;
+    }
+
+    public boolean getCrunchPng() {
+        return crunchPng;
+    }
+
+    public void setCrunchPng(boolean crunchPng) {
+        this.crunchPng = crunchPng;
+    }
+
+    public boolean getProcess9Patch() {
+        return process9Patch;
+    }
+
+    public void setProcess9Patch(boolean process9Patch) {
+        this.process9Patch = process9Patch;
+    }
+
+    @Optional
+    @OutputFile
+    public File getPublicFile() {
+        return publicFile;
+    }
+
+    public void setPublicFile(File publicFile) {
+        this.publicFile = publicFile;
+    }
+
+    public boolean getInsertSourceMarkers() {
+        return insertSourceMarkers;
+    }
+
+    public void setInsertSourceMarkers(boolean insertSourceMarkers) {
+        this.insertSourceMarkers = insertSourceMarkers;
+    }
+
+    public static class ConfigAction implements TaskConfigAction<MergeResources> {
+
+        @NonNull
+        private VariantScope scope;
+
+        @NonNull
+        private String taskNamePrefix;
+
+        @Nullable
+        private File outputLocation;
+
+        private boolean includeDependencies;
+
+        private boolean process9Patch;
+
+        public ConfigAction(
+                @NonNull VariantScope scope,
+                @NonNull String taskNamePrefix,
+                @Nullable File outputLocation,
+                boolean includeDependencies,
+                boolean process9Patch) {
+            this.scope = scope;
+            this.taskNamePrefix = taskNamePrefix;
+            this.outputLocation = outputLocation;
+            this.includeDependencies = includeDependencies;
+            this.process9Patch = process9Patch;
+
+            scope.setMergeResourceOutputDir(outputLocation);
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName(taskNamePrefix, "Resources");
+        }
+
+        @Override
+        public Class<MergeResources> getType() {
+            return MergeResources.class;
+        }
+
+        @Override
+        public void execute(MergeResources mergeResourcesTask) {
+            final BaseVariantData<? extends BaseVariantOutputData> variantData =
+                    scope.getVariantData();
+            final AndroidConfig extension = scope.getGlobalScope().getExtension();
+
+            mergeResourcesTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            mergeResourcesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+            mergeResourcesTask.setIncrementalFolder(new File(
+                    scope.getGlobalScope().getBuildDir() + "/" + AndroidProject.FD_INTERMEDIATES +
+                            "/incremental/" + taskNamePrefix + "Resources/" +
+                            variantData.getVariantConfiguration().getDirName()));
+
+            mergeResourcesTask.process9Patch = process9Patch;
+            mergeResourcesTask.crunchPng = extension.getAaptOptions()
+                    .getCruncherEnabled();
+            mergeResourcesTask.normalizeResources =
+                    extension.getBuildToolsRevision()
+                            .compareTo(NORMALIZE_RESOURCES_BUILD_TOOLS) < 0;
+
+            // Only one pre-processor for now. The code will need slight changes when we add more.
+            mergeResourcesTask.preprocessor = new VectorDrawableRenderer(
+                    scope.getGeneratedPngsOutputDir(),
+                    extension.getPreprocessingOptions().getTypedDensities(),
+                    mergeResourcesTask.getILogger());
+
+            ConventionMappingHelper.map(mergeResourcesTask, "useNewCruncher",
+                    new Callable<Boolean>() {
+                        @Override
+                        public Boolean call() throws Exception {
+                            return extension.getAaptOptions()
+                                    .getUseNewCruncher();
+                        }
+                    });
+
+            ConventionMappingHelper.map(mergeResourcesTask, "inputResourceSets",
+                    new Callable<List<ResourceSet>>() {
+                        @Override
+                        public List<ResourceSet> call() throws Exception {
+                            List<File> generatedResFolders = Lists.newArrayList(
+                                    scope.getRenderscriptResOutputDir(),
+                                    scope.getGeneratedResOutputDir());
+                            if (variantData.getExtraGeneratedResFolders() != null) {
+                                generatedResFolders.addAll(
+                                        variantData.getExtraGeneratedResFolders());
+                            }
+                            if (variantData.generateApkDataTask != null &&
+                                    variantData.getVariantConfiguration().getBuildType()
+                                            .isEmbedMicroApp()) {
+                                generatedResFolders.add(
+                                        variantData.generateApkDataTask.getResOutputDir());
+                            }
+                            return variantData.getVariantConfiguration()
+                                    .getResourceSets(generatedResFolders, includeDependencies);
+                        }
+                    });
+            ConventionMappingHelper.map(mergeResourcesTask, "outputDir", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return outputLocation != null ? outputLocation
+                            : scope.getDefaultMergeResourcesOutputDir();
+                }
+            });
+
+            variantData.mergeResourcesTask = mergeResourcesTask;
+        }
+    }
+}
+
+
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
index d7d3e66..c06a19d 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
@@ -15,11 +15,15 @@
  */
 
 package com.android.build.gradle.tasks
+
 import com.android.annotations.NonNull
-import com.android.build.gradle.internal.core.NdkConfig
+import com.android.build.gradle.internal.dsl.CoreNdkOptions
 import com.android.build.gradle.internal.tasks.NdkTask
+import com.android.ide.common.process.LoggedProcessOutputHandler
 import com.android.ide.common.process.ProcessInfoBuilder
+import com.android.ide.common.process.ProcessOutputHandler
 import com.android.sdklib.IAndroidTarget
+import com.android.utils.FileUtils
 import com.google.common.base.Charsets
 import com.google.common.base.Joiner
 import com.google.common.collect.Lists
@@ -41,6 +45,8 @@
 
 class NdkCompile extends NdkTask {
 
+    public static String USE_DEPRECATED_NDK = "android.useDeprecatedNdk";
+
     List<File> sourceFolders
 
     @OutputFile
@@ -65,6 +71,9 @@
     @Input
     boolean ndkCygwinMode
 
+    @Input
+    boolean isForTesting
+
     @SkipWhenEmpty
     @InputFiles
     FileTree getSource() {
@@ -78,6 +87,34 @@
 
     @TaskAction
     void taskAction(IncrementalTaskInputs inputs) {
+         if (!project.hasProperty(USE_DEPRECATED_NDK)) {
+             // Normally, we would catch the user when they try to configure the NDK, but NDK do
+             // not need to be configured by default.  Throw this exception during task execution in
+             // case we miss it.
+             throw new RuntimeException(
+                     "Error: NDK integration is deprecated in the current plugin.  Consider trying " +
+                             "the new experimental plugin.  For details, see " +
+                             "http://tools.android.com/tech-docs/new-build-system/gradle-experimental.  " +
+                             "Set \"$USE_DEPRECATED_NDK=true\" in gradle.properties to " +
+                             "continue using the current NDK integration.");
+         }
+
+
+        if (isNdkOptionUnset()) {
+            logger.warn("Warning: Native C/C++ source code is found, but it seems that NDK " +
+                    "option is not configured.  Note that if you have an Android.mk, it is not " +
+                    "used for compilation.  The recommended workaround is to remove the default " +
+                    "jni source code directory by adding: \n " +
+                    "android {\n" +
+                    "    sourceSets {\n" +
+                    "        main {\n" +
+                    "            jni.srcDirs = []\n" +
+                    "        }\n" +
+                    "    }\n" +
+                    "}\n" +
+                    "to build.gradle, manually compile the code with ndk-build, " +
+                    "and then place the resulting shared object in src/main/jniLibs.");
+        }
 
         FileTree sourceFileTree = getSource()
         Set<File> sourceFiles = sourceFileTree.matching(new PatternSet().exclude("**/*.h")).files
@@ -85,8 +122,8 @@
 
         if (sourceFiles.isEmpty()) {
             makefile.delete()
-            emptyFolder(getSoFolder())
-            emptyFolder(getObjFolder())
+            FileUtils.emptyFolder(getSoFolder())
+            FileUtils.emptyFolder(getObjFolder())
             return
         }
 
@@ -103,8 +140,8 @@
         if (!inputs.isIncremental()) {
             project.logger.info("Unable do incremental execution: full task run")
             generateMakefile = true
-            emptyFolder(getSoFolder())
-            emptyFolder(getObjFolder())
+            FileUtils.emptyFolder(getSoFolder())
+            FileUtils.emptyFolder(getObjFolder())
         } else {
             // look for added or removed files *only*
 
@@ -130,7 +167,7 @@
     }
 
     private void writeMakefile(@NonNull Set<File> sourceFiles, @NonNull File makefile) {
-        NdkConfig ndk = getNdkConfig()
+        CoreNdkOptions ndk = getNdkConfig()
 
         StringBuilder sb = new StringBuilder()
 
@@ -138,12 +175,20 @@
                 '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')
+        String moduleName = ndk.moduleName != null ? ndk.moduleName : project.name
+        if (isForTesting) {
+            moduleName = moduleName + "_test"
+        }
+
+        sb.append('LOCAL_MODULE := ').append(moduleName).append('\n')
 
         if (ndk.cFlags != null) {
             sb.append('LOCAL_CFLAGS := ').append(ndk.cFlags).append('\n')
         }
 
+        // To support debugging from Android Studio.
+        sb.append("LOCAL_LDFLAGS := -Wl,--build-id\n")
+
         List<String> fullLdlibs = Lists.newArrayList()
         if (ndk.ldLibs != null) {
             fullLdlibs.addAll(ndk.ldLibs)
@@ -189,7 +234,7 @@
     }
 
     private void runNdkBuild(@NonNull File ndkLocation, @NonNull File makefile) {
-        NdkConfig ndk = getNdkConfig()
+        CoreNdkOptions ndk = getNdkConfig()
 
         ProcessInfoBuilder builder = new ProcessInfoBuilder()
 
@@ -241,6 +286,17 @@
             builder.addArgs("-j" + ndk.getJobs());
         }
 
-        getBuilder().executeProcess(builder.createProcess()).rethrowFailure().assertNormalExitValue()
+        ProcessOutputHandler handler = new LoggedProcessOutputHandler(getBuilder().getLogger());
+        getBuilder().executeProcess(builder.createProcess(), handler)
+                .rethrowFailure().assertNormalExitValue()
+    }
+
+    private boolean isNdkOptionUnset() {
+        // If none of the NDK options are set, then it is likely that NDK is not configured.
+        return (getModuleName() == null &&
+                getcFlags() == null &&
+                getLdLibs() == null &&
+                getAbiFilters() == null &&
+                getStl() == null);
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
index ffff5ae..16ab90c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
@@ -6,8 +6,8 @@
 import com.android.build.gradle.internal.annotations.ApkFile;
 import com.android.build.gradle.internal.core.GradleVariantConfiguration;
 import com.android.build.gradle.internal.dsl.AbiSplitOptions;
+import com.android.build.gradle.internal.dsl.CoreSigningConfig;
 import com.android.build.gradle.internal.dsl.PackagingOptions;
-import com.android.build.gradle.internal.dsl.SigningConfig;
 import com.android.build.gradle.internal.scope.ConventionMappingHelper;
 import com.android.build.gradle.internal.scope.TaskConfigAction;
 import com.android.build.gradle.internal.scope.VariantOutputScope;
@@ -18,7 +18,9 @@
 import com.android.build.gradle.internal.variant.ApkVariantOutputData;
 import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.builder.packaging.DuplicateFileException;
+import com.android.builder.signing.SignedJarBuilder;
 import com.android.utils.StringHelper;
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableSet;
 
 import org.codehaus.groovy.runtime.StringGroovyMethods;
@@ -92,6 +94,14 @@
         this.jniFolders = jniFolders;
     }
 
+    public File getMergingFolder() {
+        return mergingFolder;
+    }
+
+    public void setMergingFolder(File mergingFolder) {
+        this.mergingFolder = mergingFolder;
+    }
+
     @OutputFile
     public File getOutputFile() {
         return outputFile;
@@ -123,6 +133,8 @@
 
     private Set<File> jniFolders;
 
+    private File mergingFolder;
+
     @ApkFile
     private File outputFile;
 
@@ -132,10 +144,12 @@
 
     private boolean jniDebugBuild;
 
-    private SigningConfig signingConfig;
+    private CoreSigningConfig signingConfig;
 
     private PackagingOptions packagingOptions;
 
+    private SignedJarBuilder.IZipEntryFilter packagingOptionsFilter;
+
     @InputFiles
     public Set<File> getPackagedJars() {
         return packagedJars;
@@ -160,11 +174,11 @@
 
     @Nested
     @Optional
-    public SigningConfig getSigningConfig() {
+    public CoreSigningConfig getSigningConfig() {
         return signingConfig;
     }
 
-    public void setSigningConfig(SigningConfig signingConfig) {
+    public void setSigningConfig(CoreSigningConfig signingConfig) {
         this.signingConfig = signingConfig;
     }
 
@@ -177,6 +191,14 @@
         this.packagingOptions = packagingOptions;
     }
 
+    public SignedJarBuilder.IZipEntryFilter getPackagingOptionsFilter() {
+        return packagingOptionsFilter;
+    }
+
+    public void setPackagingOptionsFilter(SignedJarBuilder.IZipEntryFilter filter) {
+        this.packagingOptionsFilter = filter;
+    }
+
     @InputFiles
     public FileTree getNativeLibraries() {
         FileTree src = null;
@@ -194,8 +216,10 @@
             final File dir = getJavaResourceDir();
             getBuilder().packageApk(getResourceFile().getAbsolutePath(), getDexFolder(),
                     getDexedLibraries(), getPackagedJars(),
-                    (dir == null ? null : dir.getAbsolutePath()), getJniFolders(), getAbiFilters(),
-                    getJniDebugBuild(), getSigningConfig(), getPackagingOptions(),
+                    (dir == null ? null : dir.getAbsolutePath()), getJniFolders(),
+                    getMergingFolder(), getAbiFilters(), getJniDebugBuild(), getSigningConfig(),
+                    getPackagingOptions(),
+                    getPackagingOptionsFilter(),
                     getOutputFile().getAbsolutePath());
         } catch (DuplicateFileException e) {
             Logger logger = getLogger();
@@ -259,6 +283,8 @@
 
             variantOutputData.packageApplicationTask = packageApp;
             packageApp.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            packageApp.setVariantName(
+                    scope.getVariantScope().getVariantConfiguration().getFullName());
 
             if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() && !config
                     .getUseJack()) {
@@ -280,44 +306,61 @@
             ConventionMappingHelper.map(packageApp, "dexFolder", new Callable<File>() {
                 @Override
                 public File call() {
-                    if (scope.getVariantScope().getDexTask() != null) {
-                        return scope.getVariantScope().getDexOutputFolder();
-                    }
-
-                    if (scope.getVariantScope().getJavaCompileTask() != null) {
-                        return scope.getVariantScope().getJavaOutputDir();
-                    }
-
-                    if (variantData.javaCompileTask != null) {
-                        return variantData.javaCompileTask.getDestinationDir();
-                    }
-                    return null;
+                    return scope.getVariantScope().getDexOutputFolder();
                 }
             });
-            ConventionMappingHelper.map(packageApp, "dexedLibraries", new Callable<Collection<File>>() {
-                @Override
-                public Collection<File> call() {
-                    if (config.isMultiDexEnabled() && !config.isLegacyMultiDexMode()
-                            && variantData.preDexTask != null) {
-                        return scope.getGlobalScope().getProject()
-                                .fileTree(variantData.preDexTask.getOutputFolder()).getFiles();
-                    }
+            ConventionMappingHelper.map(packageApp, "dexedLibraries",
+                    new Callable<Collection<File>>() {
+                        @Override
+                        public Collection<File> call() {
+                            if (config.isMultiDexEnabled() && !config.isLegacyMultiDexMode()
+                                    && variantData.preDexTask != null) {
+                                return scope.getGlobalScope().getProject()
+                                        .fileTree(variantData.preDexTask.getOutputFolder())
+                                        .getFiles();
+                            }
 
-                    return Collections.emptyList();
-                }
-            });
+                            return Collections.emptyList();
+                        }
+                    });
+            scope.getVariantScope().getJavaResourcesProvider();
             ConventionMappingHelper.map(packageApp, "packagedJars", new Callable<Set<File>>() {
                 @Override
                 public Set<File> call() {
-                    return scope.getGlobalScope().getAndroidBuilder().getPackagedJars(config);
+                    ImmutableSet.Builder<File> jarFiles = ImmutableSet.builder();
+                    for (JavaResourcesProvider.JavaResourcesLocation javaResourcesProvider :
+                            scope.getVariantScope().getJavaResourcesProvider().getJavaResourcesLocations()) {
+                        if (javaResourcesProvider.type == JavaResourcesProvider.Type.JAR) {
+                            jarFiles.add(javaResourcesProvider.location);
+                        }
+                    }
+                    return jarFiles.build();
                 }
             });
             ConventionMappingHelper.map(packageApp, "javaResourceDir", new Callable<File>() {
                 @Override
                 public File call() {
-                    return getOptionalDir(variantData.processJavaResourcesTask.getDestinationDir());
+                    ImmutableSet.Builder<File> folders = ImmutableSet.builder();
+                    for (JavaResourcesProvider.JavaResourcesLocation javaResourcesProvider :
+                            scope.getVariantScope().getJavaResourcesProvider()
+                                    .getJavaResourcesLocations()) {
+                        if (javaResourcesProvider.type == JavaResourcesProvider.Type.FOLDER) {
+                            folders.add(javaResourcesProvider.location);
+                        }
+                    }
+                    ImmutableSet<File> resourceFolders = folders.build();
+                    if (resourceFolders.size() > 1) {
+                        throw new RuntimeException("More than one java resources folders : " +
+                                Joiner.on(",").join(resourceFolders));
+                    }
+                    return resourceFolders.isEmpty() ? null : resourceFolders.iterator().next();
                 }
             });
+
+            packageApp.setMergingFolder(new File(scope.getGlobalScope().getIntermediatesDir(),
+                    variantOutputData.getFullName() + "/merging"));
+
+
             ConventionMappingHelper.map(packageApp, "jniFolders", new Callable<Set<File>>() {
                 @Override
                 public Set<File> call() {
@@ -351,7 +394,7 @@
                 }
             });
 
-            SigningConfig sc = (SigningConfig) config.getSigningConfig();
+            CoreSigningConfig sc = (CoreSigningConfig) config.getSigningConfig();
             packageApp.setSigningConfig(sc);
             if (sc != null) {
                 String validateSigningTaskName = "validate" + StringHelper.capitalize(sc.getName()) + "Signing";
@@ -363,6 +406,8 @@
                                     "validate" + StringHelper.capitalize(sc.getName()) + "Signing",
                                     ValidateSigningTask.class);
                     validateSigningTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+                    validateSigningTask.setVariantName(
+                            scope.getVariantScope().getVariantConfiguration().getFullName());
                     validateSigningTask.setSigningConfig(sc);
                 }
 
@@ -376,6 +421,14 @@
                 }
             });
 
+            ConventionMappingHelper.map(packageApp, "packagingOptionsFilter",
+                    new Callable<SignedJarBuilder.IZipEntryFilter>() {
+                @Override
+                public SignedJarBuilder.IZipEntryFilter call() throws Exception {
+                    return scope.getVariantScope().getPackagingOptionsFilter();
+                }
+            });
+
             ConventionMappingHelper.map(packageApp, "outputFile", new Callable<File>() {
                 @Override
                 public File call() throws Exception {
@@ -392,6 +445,7 @@
                             .capitalize(variantOutputData.getFullName())
                             + "Resources", ShrinkResources.class);
             task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
             task.variantOutputData = variantOutputData;
 
             final String outputBaseName = variantOutputData.getBaseName();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.groovy
deleted file mode 100644
index 31e519e..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.groovy
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2014 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.FilterData
-import com.android.build.OutputFile
-import com.android.build.gradle.api.ApkOutputFile
-import com.android.build.gradle.internal.dsl.PackagingOptions
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.model.FilterDataImpl
-import com.google.common.base.Joiner
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableSet
-import com.google.common.util.concurrent.Callables
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.TaskAction
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-/**
- * Package a abi dimension specific split APK
- */
-@ParallelizableTask
-class PackageSplitAbi extends SplitRelatedTask {
-
-    ImmutableList<ApkOutputFile> outputFiles;
-
-    @InputFiles
-    Collection<File> inputFiles
-
-    File outputDirectory
-
-    @Input
-    Set<String> splits
-
-    @Input
-    String outputBaseName
-
-    @Input
-    boolean jniDebuggable
-
-    @Nested @Optional
-    SigningConfig signingConfig
-
-    @Nested
-    PackagingOptions packagingOptions
-
-    @Input
-    Collection<File> jniFolders;
-
-    @OutputFiles
-    public List<File> getOutputFiles() {
-        return getOutputSplitFiles()*.getOutputFile();
-    }
-
-    @Override
-    @Nullable
-    File getApkMetadataFile() {
-        return null;
-    }
-
-    @NonNull
-    public synchronized  ImmutableList<ApkOutputFile> getOutputSplitFiles() {
-
-        if (outputFiles == null) {
-            ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
-            for (String split : splits) {
-                String apkName = getApkName(split);
-                ApkOutputFile apkOutput = new ApkOutputFile(
-                        OutputFile.OutputType.SPLIT,
-                        ImmutableList.<FilterData> of(
-                                FilterDataImpl.build(OutputFile.ABI, apkName)),
-                        Callables.returning(new File(outputDirectory, apkName)))
-                builder.add(apkOutput)
-            }
-            outputFiles = builder.build()
-        }
-        return outputFiles;
-    }
-
-    private boolean isAbiSplit(String fileName) {
-        for (String abi : getSplits()) {
-            if (fileName.contains(abi)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @TaskAction
-    protected void doFullTaskAction() {
-
-        // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
-        final Pattern pattern = Pattern.compile(
-                "resources-${getOutputBaseName()}-(.*).ap_")
-        List<String> unprocessedSplits = new ArrayList(splits);
-        for (File file : inputFiles) {
-            Matcher matcher = pattern.matcher(file.getName());
-            if (matcher.matches() && isAbiSplit(file.getName())) {
-                String apkName = getApkName(matcher.group(1))
-
-                File outFile = new File(getOutputDirectory(), apkName);
-                getBuilder().packageApk(
-                        file.absolutePath,
-                        null, /* dexFolder */
-                        null, /* dexedLibraries */
-                        ImmutableList.of(),
-                        null, /* getJavaResourceDir */
-                        getJniFolders(),
-                        ImmutableSet.of(matcher.group(1)),
-                        getJniDebuggable(),
-                        getSigningConfig(),
-                        getPackagingOptions(),
-                        outFile.absolutePath)
-                unprocessedSplits.remove(matcher.group(1));
-            }
-        }
-        if (!unprocessedSplits.isEmpty()) {
-            String message = String.format("Could not find resource package for %1$s",
-                    Joiner.on(',').join(unprocessedSplits));
-            logger.error(message);
-            throw new IllegalStateException(message);
-        }
-    }
-
-    @Override
-    List<FilterData> getSplitsData() {
-        ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder()
-        addAllFilterData(filterDataBuilder, splits, OutputFile.FilterType.ABI);
-        return filterDataBuilder.build()
-    }
-
-    private String getApkName(String split) {
-        String apkName = "${project.archivesBaseName}-${getOutputBaseName()}_${split}"
-        return apkName + (getSigningConfig() == null
-                ? "-unsigned.apk"
-                : "-unaligned.apk")
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.java
new file mode 100644
index 0000000..17c92f7
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 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.FilterData;
+import com.android.build.OutputFile;
+import com.android.build.gradle.api.ApkOutputFile;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.model.FilterDataImpl;
+import com.android.builder.model.SigningConfig;
+import com.android.builder.packaging.DuplicateFileException;
+import com.android.builder.packaging.PackagerException;
+import com.android.builder.packaging.SigningException;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.ide.common.signing.KeytoolException;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Callables;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Package a abi dimension specific split APK
+ */
+@ParallelizableTask
+public class PackageSplitAbi extends SplitRelatedTask {
+
+    private ImmutableList<ApkOutputFile> outputFiles;
+
+    private Collection<File> inputFiles;
+
+    private File outputDirectory;
+
+    private Set<String> splits;
+
+    private String outputBaseName;
+
+    private boolean jniDebuggable;
+
+    private SigningConfig signingConfig;
+
+    private PackagingOptions packagingOptions;
+
+    private Collection<File> jniFolders;
+
+    private File mergingFolder;
+
+    private SignedJarBuilder.IZipEntryFilter packagingOptionsFilter;
+
+    @OutputFiles
+    public List<File> getOutputFiles() {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        for (ApkOutputFile apk : getOutputSplitFiles()) {
+            builder.add(apk.getOutputFile());
+        }
+        return builder.build();
+    }
+
+    @Override
+    @Nullable
+    public File getApkMetadataFile() {
+        return null;
+    }
+
+    @Override
+    @NonNull
+    public synchronized ImmutableList<ApkOutputFile> getOutputSplitFiles() {
+
+        if (outputFiles == null) {
+            ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
+            for (String split : splits) {
+                String apkName = getApkName(split);
+                ApkOutputFile apkOutput = new ApkOutputFile(
+                        OutputFile.OutputType.SPLIT,
+                        ImmutableList.of(FilterDataImpl.build(OutputFile.ABI, apkName)),
+                        Callables.returning(new File(outputDirectory, apkName)));
+                builder.add(apkOutput);
+            }
+
+            outputFiles = builder.build();
+        }
+        return outputFiles;
+    }
+
+    private boolean isAbiSplit(String fileName) {
+        for (String abi : getSplits()) {
+            if (fileName.contains(abi)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @TaskAction
+    protected void doFullTaskAction() throws FileNotFoundException, SigningException,
+            KeytoolException, DuplicateFileException, PackagerException {
+
+        // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
+        final Pattern pattern = Pattern.compile("resources-" + getOutputBaseName() + "-(.*).ap_");
+        List<String> unprocessedSplits = Lists.newArrayList(splits);
+        for (File file : inputFiles) {
+            Matcher matcher = pattern.matcher(file.getName());
+            if (matcher.matches() && isAbiSplit(file.getName())) {
+                String apkName = getApkName(matcher.group(1));
+
+                File outFile = new File(getOutputDirectory(), apkName);
+                getBuilder().packageApk(
+                        file.getAbsolutePath(),
+                        null, /* dexFolder */
+                        ImmutableList.<File>of(), /* dexedLibraries */
+                        ImmutableList.<File>of(),
+                        null, /* getJavaResourceDir */
+                        getJniFolders(),
+                        getMergingFolder(),
+                        ImmutableSet.of(matcher.group(1)),
+                        isJniDebuggable(),
+                        getSigningConfig(),
+                        getPackagingOptions(),
+                        getPackagingOptionsFilter(),
+                        outFile.getAbsolutePath());
+                unprocessedSplits.remove(matcher.group(1));
+            }
+        }
+        if (!unprocessedSplits.isEmpty()) {
+            String message = "Could not find resource package for "
+                    + Joiner.on(',').join(unprocessedSplits);
+            getLogger().error(message);
+            throw new IllegalStateException(message);
+        }
+    }
+
+    @Override
+    public List<FilterData> getSplitsData() {
+        ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder();
+        SplitRelatedTask.addAllFilterData(filterDataBuilder, splits, OutputFile.FilterType.ABI);
+        return filterDataBuilder.build();
+    }
+
+    private String getApkName(final String split) {
+        String archivesBaseName = (String)getProject().getProperties().get("archivesBaseName");
+        String apkName = archivesBaseName + "-" + getOutputBaseName() + "_" + split;
+        return apkName + (getSigningConfig() == null ? "-unsigned.apk" : "-unaligned.apk");
+    }
+
+    public void setOutputFiles(ImmutableList<ApkOutputFile> outputFiles) {
+        this.outputFiles = outputFiles;
+    }
+
+    @InputFiles
+    public Collection<File> getInputFiles() {
+        return inputFiles;
+    }
+
+    public void setInputFiles(Collection<File> inputFiles) {
+        this.inputFiles = inputFiles;
+    }
+
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    @Input
+    public Set<String> getSplits() {
+        return splits;
+    }
+
+    public void setSplits(Set<String> splits) {
+        this.splits = splits;
+    }
+
+    @Input
+    public String getOutputBaseName() {
+        return outputBaseName;
+    }
+
+    public void setOutputBaseName(String outputBaseName) {
+        this.outputBaseName = outputBaseName;
+    }
+
+    @Input
+    public boolean isJniDebuggable() {
+        return jniDebuggable;
+    }
+
+    public void setJniDebuggable(boolean jniDebuggable) {
+        this.jniDebuggable = jniDebuggable;
+    }
+
+    @Nested
+    @Optional
+    public SigningConfig getSigningConfig() {
+        return signingConfig;
+    }
+
+    public void setSigningConfig(SigningConfig signingConfig) {
+        this.signingConfig = signingConfig;
+    }
+
+    @Nested
+    public PackagingOptions getPackagingOptions() {
+        return packagingOptions;
+    }
+
+    public void setPackagingOptions(PackagingOptions packagingOptions) {
+        this.packagingOptions = packagingOptions;
+    }
+
+    @Input
+    public Collection<File> getJniFolders() {
+        return jniFolders;
+    }
+
+    public void setJniFolders(Collection<File> jniFolders) {
+        this.jniFolders = jniFolders;
+    }
+
+    public File getMergingFolder() {
+        return mergingFolder;
+    }
+
+    public void setMergingFolder(File mergingFolder) {
+        this.mergingFolder = mergingFolder;
+    }
+
+    public SignedJarBuilder.IZipEntryFilter getPackagingOptionsFilter() {
+        return packagingOptionsFilter;
+    }
+
+    public void setPackagingOptionsFilter(SignedJarBuilder.IZipEntryFilter packagingOptionsFilter) {
+        this.packagingOptionsFilter = packagingOptionsFilter;
+    }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.groovy
deleted file mode 100644
index d78279c..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.groovy
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2014 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.FilterData
-import com.android.build.OutputFile
-import com.android.build.gradle.api.ApkOutputFile
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.model.FilterDataImpl
-import com.google.common.collect.ImmutableList
-import com.google.common.util.concurrent.Callables
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-/**
- * Package each split resources into a specific signed apk file.
- */
-@ParallelizableTask
-class PackageSplitRes extends SplitRelatedTask {
-
-    @Input
-    Set<String> densitySplits
-
-    @Input
-    Set<String> languageSplits
-
-    @Input
-    String outputBaseName
-
-    @Nested @Optional
-    SigningConfig signingConfig
-
-    @InputFiles
-    List<File> getInputFiles() {
-        ImmutableList.Builder<File> builder = ImmutableList.builder();
-        forEachInputFile { split, file ->
-            builder.add(file)
-        }
-        return builder.build()
-    }
-
-    @OutputFiles
-    public List<File> getOutputFiles() {
-        getOutputSplitFiles()*.getOutputFile()
-    }
-
-    File getApkMetadataFile() {
-        return null
-    }
-
-    /**
-     * This directories are not officially input/output to the task as
-     * they are shared among tasks. To be parallelizable, we must only
-     * define our I/O in terms of files...
-     */
-    File inputDirectory
-    File outputDirectory
-
-    /**
-     * Calculates the list of output files, coming from the list of input files, mangling the
-     * output file name.
-     */
-    public List<ApkOutputFile> getOutputSplitFiles() {
-        ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
-        forEachInputFile { String split, File file ->
-            // find the split identification, if null, the split is not requested any longer.
-            FilterData filterData = null;
-            for (String density : densitySplits) {
-                if (split.startsWith(density)) {
-                    filterData = FilterDataImpl.build(
-                            OutputFile.FilterType.DENSITY.toString(), density)
-                }
-            }
-            if (languageSplits.contains(unMangleSplitName(split))) {
-                filterData = FilterDataImpl.build(
-                        OutputFile.FilterType.LANGUAGE.toString(), unMangleSplitName(split));
-            }
-            if (filterData != null) {
-                builder.add(new ApkOutputFile(OutputFile.OutputType.SPLIT,
-                        ImmutableList.of(filterData),
-                        Callables.<File>returning(
-                                new File(outputDirectory, this.getOutputFileNameForSplit(split)))))
-            }
-        }
-        return builder.build();
-    }
-
-    @TaskAction
-    protected void doFullTaskAction() {
-
-        forEachInputFile { String split, File file ->
-            File outFile = new File(outputDirectory, this.getOutputFileNameForSplit(split));
-            getBuilder().signApk(file, signingConfig, outFile)
-        }
-    }
-
-    /**
-     * Runs the closure for each task input file, providing the split identifier (possibly with
-     * a suffix generated by aapt) and the input file handle.
-     * @param closure groovy closure to run on each input file.
-     */
-    public void forEachInputFile(Closure closure) {
-        Pattern resourcePattern = Pattern.compile(
-                "resources-${outputBaseName}.ap__(.*)")
-
-        // make a copy of the expected densities and languages filters.
-        List<String> densitiesCopy = new ArrayList<>(densitySplits)
-        List<String> languagesCopy = new ArrayList<>(languageSplits)
-
-        // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
-        for (File file : inputDirectory.listFiles()) {
-            Matcher match = resourcePattern.matcher(file.getName())
-            // each time we match, we remove the associated filter from our copies.
-            if (match.matches() && !match.group(1).isEmpty()
-                    && isValidSplit(densitiesCopy, languagesCopy, match.group(1))) {
-                closure(match.group(1), file)
-            }
-        }
-        // manually invoke the closure for filters we did not find associated files, apply best
-        // guess on the actual file names.
-        for (String density : densitiesCopy) {
-            closure(density,
-                    new File(inputDirectory, "resources-${outputBaseName}.ap__${density}"));
-        }
-        for (String language : languagesCopy) {
-            closure(language,
-                    new File(inputDirectory, "resources-${outputBaseName}.ap__${language}"));
-
-        }
-    }
-
-    /**
-     * Returns true if the passed split identifier is a valid identifier (valid mean it is a
-     * requested split for this task). A density split identifier can be suffixed with characters
-     * added by aapt.
-     */
-    private static boolean isValidSplit(
-            List<String> densities,
-            List<String> languages,
-            @NonNull String splitWithOptionalSuffix) {
-        for (String density : densities) {
-            if (splitWithOptionalSuffix.startsWith(density)) {
-                densities.remove(density);
-                return true;
-            }
-        }
-        String mangledName = unMangleSplitName(splitWithOptionalSuffix);
-        if (languages.contains(mangledName)) {
-            languages.remove(mangledName);
-            return true;
-        }
-        return false;
-    }
-
-    String getOutputFileNameForSplit(String split) {
-        String apkName = "${project.archivesBaseName}-${outputBaseName}_${split}"
-        return apkName + (signingConfig == null ? "-unsigned.apk" : "-unaligned.apk")
-    }
-
-    @Override
-    List<FilterData> getSplitsData() {
-        ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder();
-        addAllFilterData(filterDataBuilder, densitySplits, OutputFile.FilterType.DENSITY);
-        addAllFilterData(filterDataBuilder, languageSplits, OutputFile.FilterType.LANGUAGE);
-        return filterDataBuilder.build();
-    }
-
-    /**
-     * Un-mangle a split name as created by the aapt tool to retrieve a split name as configured
-     * in the project's build.gradle.
-     *
-     * when dealing with several split language in a single split, each language (+ optional region)
-     * will be seperated by an underscore.
-     *
-     * note that there is currently an aapt bug, remove the 'r' in the region so for instance,
-     * fr-rCA becomes fr-CA, temporarily put it back until it is fixed.
-     *
-     * @param splitWithOptionalSuffix the mangled split name.
-     * @return
-     */
-    static String unMangleSplitName(String splitWithOptionalSuffix) {
-        String mangledName = splitWithOptionalSuffix.replaceAll('_', ',');
-        return mangledName.contains("-r") ? mangledName : mangledName.replace("-", "-r");
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.java
new file mode 100644
index 0000000..7f8bcfb
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2014 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.FilterData;
+import com.android.build.OutputFile;
+import com.android.build.gradle.api.ApkOutputFile;
+import com.android.build.gradle.internal.model.FilterDataImpl;
+import com.android.builder.model.SigningConfig;
+import com.android.builder.packaging.SigningException;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.ide.common.signing.KeytoolException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Callables;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Package each split resources into a specific signed apk file.
+ */
+@ParallelizableTask
+public class PackageSplitRes extends SplitRelatedTask {
+
+    private Set<String> densitySplits;
+
+    private Set<String> languageSplits;
+
+    private String outputBaseName;
+
+    private SigningConfig signingConfig;
+
+    /**
+     * This directories are not officially input/output to the task as they are shared among tasks.
+     * To be parallelizable, we must only define our I/O in terms of files...
+     */
+    private File inputDirectory;
+
+    private File outputDirectory;
+
+    @InputFiles
+    public List<File> getInputFiles() {
+        final ImmutableList.Builder<File> builder = ImmutableList.builder();
+        forEachInputFile(new SplitFileHandler() {
+            @Override
+            public void execute(String split, File file) {
+                builder.add(file);
+            }
+        });
+        return builder.build();
+    }
+
+    @OutputFiles
+    public List<File> getOutputFiles() {
+        ImmutableList.Builder<File> builder = ImmutableList.builder();
+        for (ApkOutputFile apk : getOutputSplitFiles()) {
+            builder.add(apk.getOutputFile());
+        }
+        return builder.build();
+    }
+
+    @Override
+    public File getApkMetadataFile() {
+        return null;
+    }
+
+    /**
+     * Calculates the list of output files, coming from the list of input files, mangling the output
+     * file name.
+     */
+    @Override
+    public List<ApkOutputFile> getOutputSplitFiles() {
+        final ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
+        forEachInputFile(new SplitFileHandler() {
+            @Override
+            public void execute(String split, File file) {
+                // find the split identification, if null, the split is not requested any longer.
+                FilterData filterData = null;
+                for (String density : densitySplits) {
+                    if (split.startsWith(density)) {
+                        filterData = FilterDataImpl.build(
+                                OutputFile.FilterType.DENSITY.toString(), density);
+                    }
+
+                }
+
+                if (languageSplits.contains(unMangleSplitName(split))) {
+                    filterData = FilterDataImpl.build(
+                            OutputFile.FilterType.LANGUAGE.toString(), unMangleSplitName(split));
+                }
+                if (filterData != null) {
+                    builder.add(new ApkOutputFile(
+                            OutputFile.OutputType.SPLIT,
+                            ImmutableList.of(filterData),
+                            Callables.returning(
+                                    new File(outputDirectory, getOutputFileNameForSplit(split)))));
+                }
+
+            }
+        });
+        return builder.build();
+    }
+
+    @TaskAction
+    protected void doFullTaskAction() {
+        forEachInputFile(
+                new SplitFileHandler() {
+                    @Override
+                    public void execute(String split, File file) {
+                            File outFile = new File(outputDirectory,
+                                    getOutputFileNameForSplit(split));
+                        try {
+                            getBuilder().signApk(file, signingConfig, outFile);
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        } catch (KeytoolException e) {
+                            throw new RuntimeException(e);
+                        } catch (SigningException e) {
+                            throw new RuntimeException(e);
+                        } catch (NoSuchAlgorithmException e) {
+                            throw new RuntimeException(e);
+                        } catch (SignedJarBuilder.IZipEntryFilter.ZipAbortException e) {
+                            throw new RuntimeException(e);
+                        } catch (com.android.builder.signing.SigningException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+                });
+    }
+
+    private interface SplitFileHandler {
+        void execute(String split, File file);
+    }
+
+    /**
+     * Runs the handler for each task input file, providing the split identifier (possibly with a
+     * suffix generated by aapt) and the input file handle.
+     */
+    private void forEachInputFile(SplitFileHandler handler) {
+        Pattern resourcePattern = Pattern.compile("resources-" + outputBaseName + ".ap__(.*)");
+
+        // make a copy of the expected densities and languages filters.
+        List<String> densitiesCopy = Lists.newArrayList(densitySplits);
+        List<String> languagesCopy = Lists.newArrayList(languageSplits);
+
+        // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
+        File[] fileLists = inputDirectory.listFiles();
+        if (fileLists != null) {
+            for (File file : fileLists) {
+                Matcher match = resourcePattern.matcher(file.getName());
+                // each time we match, we remove the associated filter from our copies.
+                if (match.matches() && !match.group(1).isEmpty()
+                        && isValidSplit(densitiesCopy, languagesCopy, match.group(1))) {
+                    handler.execute(match.group(1), file);
+                }
+            }
+        }
+        // manually invoke the handler for filters we did not find associated files, apply best
+        // guess on the actual file names.
+        for (String density : densitiesCopy) {
+            handler.execute(density,
+                    new File(inputDirectory, "resources-" + outputBaseName + ".ap__" + density));
+        }
+        for (String language : languagesCopy) {
+            handler.execute(language,
+                    new File(inputDirectory, "resources-" + outputBaseName + ".ap__" + language));
+
+        }
+    }
+
+    /**
+     * Returns true if the passed split identifier is a valid identifier (valid mean it is a
+     * requested split for this task). A density split identifier can be suffixed with characters
+     * added by aapt.
+     */
+    private static boolean isValidSplit(
+            List<String> densities,
+            List<String> languages,
+            @NonNull String splitWithOptionalSuffix) {
+        for (String density : densities) {
+            if (splitWithOptionalSuffix.startsWith(density)) {
+                densities.remove(density);
+                return true;
+            }
+        }
+        String mangledName = unMangleSplitName(splitWithOptionalSuffix);
+        if (languages.contains(mangledName)) {
+            languages.remove(mangledName);
+            return true;
+        }
+        return false;
+    }
+
+    public String getOutputFileNameForSplit(final String split) {
+        String archivesBaseName = (String)getProject().getProperties().get("archivesBaseName");
+        String apkName = archivesBaseName + "-" + outputBaseName + "_" + split;
+        return apkName + (signingConfig == null ? "-unsigned.apk" : "-unaligned.apk");
+    }
+
+    @Override
+    public List<FilterData> getSplitsData() {
+        ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder();
+        addAllFilterData(filterDataBuilder, densitySplits, OutputFile.FilterType.DENSITY);
+        addAllFilterData(filterDataBuilder, languageSplits, OutputFile.FilterType.LANGUAGE);
+        return filterDataBuilder.build();
+    }
+
+    /**
+     * Un-mangle a split name as created by the aapt tool to retrieve a split name as configured in
+     * the project's build.gradle.
+     *
+     * when dealing with several split language in a single split, each language (+ optional region)
+     * will be seperated by an underscore.
+     *
+     * note that there is currently an aapt bug, remove the 'r' in the region so for instance,
+     * fr-rCA becomes fr-CA, temporarily put it back until it is fixed.
+     *
+     * @param splitWithOptionalSuffix the mangled split name.
+     */
+    public static String unMangleSplitName(String splitWithOptionalSuffix) {
+        String mangledName = splitWithOptionalSuffix.replaceAll("_", ",");
+        return mangledName.contains("-r") ? mangledName : mangledName.replace("-", "-r");
+    }
+
+    @Input
+    public Set<String> getDensitySplits() {
+        return densitySplits;
+    }
+
+    public void setDensitySplits(Set<String> densitySplits) {
+        this.densitySplits = densitySplits;
+    }
+
+    @Input
+    public Set<String> getLanguageSplits() {
+        return languageSplits;
+    }
+
+    public void setLanguageSplits(Set<String> languageSplits) {
+        this.languageSplits = languageSplits;
+    }
+
+    @Input
+    public String getOutputBaseName() {
+        return outputBaseName;
+    }
+
+    public void setOutputBaseName(String outputBaseName) {
+        this.outputBaseName = outputBaseName;
+    }
+
+    @Nested
+    @Optional
+    public SigningConfig getSigningConfig() {
+        return signingConfig;
+    }
+
+    public void setSigningConfig(SigningConfig signingConfig) {
+        this.signingConfig = signingConfig;
+    }
+
+    public File getInputDirectory() {
+        return inputDirectory;
+    }
+
+    public void setInputDirectory(File inputDirectory) {
+        this.inputDirectory = inputDirectory;
+    }
+
+    public File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy
deleted file mode 100644
index 0f3b5ba..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy
+++ /dev/null
@@ -1,249 +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.build.gradle.tasks
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.TestVariantData
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.DexOptions
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.model.AndroidProject
-import com.android.ide.common.internal.WaitableExecutor
-import com.google.common.base.Charsets
-import com.google.common.collect.Lists
-import com.google.common.collect.Sets
-import com.google.common.hash.HashCode
-import com.google.common.hash.HashFunction
-import com.google.common.hash.Hashing
-import com.google.common.io.Files
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs
-
-import java.util.concurrent.Callable
-
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@ParallelizableTask
-public class PreDex extends BaseTask {
-
-    // ----- PUBLIC TASK API -----
-
-    // ----- PRIVATE TASK API -----
-    @Input
-    String getBuildToolsVersion() {
-        getBuildTools().getRevision()
-    }
-
-    // this is used automatically by Gradle, even though nothing
-    // in the class uses it.
-    @SuppressWarnings("GroovyUnusedDeclaration")
-    @InputFiles
-    Collection<File> inputFiles
-
-    @OutputDirectory
-    File outputFolder
-
-    @Nested
-    com.android.build.gradle.internal.dsl.DexOptions dexOptions
-
-    @Input
-    boolean multiDex
-
-    @TaskAction
-    void taskAction(IncrementalTaskInputs taskInputs) {
-
-        final boolean multiDexEnabled = getMultiDex()
-
-        final File outFolder = getOutputFolder()
-
-        boolean incremental = taskInputs.isIncremental()
-        // if we are not in incremental mode, then outOfDate will contain
-        // all the files, but first we need to delete the previous output
-        if (!incremental) {
-            emptyFolder(outFolder)
-        }
-
-        final Set<String> hashs = Sets.newHashSet()
-        final WaitableExecutor<Void> executor = new WaitableExecutor<Void>()
-        final List<File> inputFileDetails = Lists.newArrayList()
-
-        taskInputs.outOfDate { final change ->
-            inputFileDetails.add(change.file)
-        }
-
-        for (final File file : inputFileDetails) {
-            Callable<Void> action = new PreDexTask(outFolder, file, hashs,
-                    multiDexEnabled);
-            executor.execute(action);
-        }
-
-        if (incremental) {
-            taskInputs.removed { change ->
-                //noinspection GroovyAssignabilityCheck
-                File preDexedFile = getDexFileName(outFolder, change.file)
-                if (preDexedFile.isDirectory()) {
-                    logger.info("deleteDir(" + preDexedFile + ") returned: " + preDexedFile.deleteDir())
-                } else {
-                    logger.info("delete(" + preDexedFile + ") returned: " + preDexedFile.delete())
-                }
-            }
-        }
-
-        executor.waitForTasksWithQuickFail(false)
-    }
-
-    private final class PreDexTask implements Callable<Void> {
-        private final File outFolder
-        private final File fileToProcess
-        private final Set<String> hashs
-        private final boolean multiDexEnabled
-        private final DexOptions options = getDexOptions()
-        private final AndroidBuilder builder = getBuilder()
-
-
-        private PreDexTask(
-                File outFolder,
-                File file,
-                Set<String> hashs,
-                boolean multiDexEnabled) {
-            this.outFolder = outFolder
-            this.fileToProcess = file
-            this.hashs = hashs
-            this.multiDexEnabled = multiDexEnabled
-        }
-
-        @Override
-        Void call() throws Exception {
-            // TODO remove once we can properly add a library as a dependency of its test.
-            String hash = getFileHash(fileToProcess)
-
-            synchronized (hashs) {
-                if (hashs.contains(hash)) {
-                    return null
-                }
-
-                hashs.add(hash)
-            }
-
-            //noinspection GroovyAssignabilityCheck
-            File preDexedFile = getDexFileName(outFolder, fileToProcess)
-
-            if (multiDexEnabled) {
-                preDexedFile.mkdirs()
-            }
-
-            //noinspection GroovyAssignabilityCheck
-            builder.preDexLibrary(fileToProcess, preDexedFile, multiDexEnabled, options)
-
-            return null
-        }
-    }
-
-    /**
-     * Returns the hash of a file.
-     * @param file the file to hash
-     * @return
-     */
-    private static String getFileHash(@NonNull File file) {
-        HashCode hashCode = Files.hash(file, Hashing.sha1())
-        return hashCode.toString()
-    }
-
-    /**
-     * Returns a unique File for the pre-dexed library, even
-     * if there are 2 libraries with the same file names (but different
-     * paths)
-     *
-     * If multidex is enabled the return File is actually a folder.
-     *
-     * @param outFolder the output folder.
-     * @param inputFile the library
-     * @param multidex whether multidex is enabled.
-     * @return
-     */
-    @NonNull
-    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.
-        String input = inputFile.getAbsolutePath();
-        HashFunction hashFunction = Hashing.sha1()
-        HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE)
-
-        return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR)
-    }
-
-    public static class ConfigAction implements TaskConfigAction<PreDex> {
-
-        VariantScope scope;
-
-        TaskManager.PostCompilationData pcData
-
-        ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
-            this.scope = scope
-            this.pcData = pcData
-        }
-
-        @Override
-        String getName() {
-            return scope.getTaskName("preDex")
-        }
-
-        @Override
-        Class<PreDex> getType() {
-            return PreDex.class
-        }
-
-        @Override
-        void execute(PreDex preDexTask) {
-            ApkVariantData variantData = (ApkVariantData) scope.variantData;
-            VariantConfiguration config = variantData.variantConfiguration
-
-            boolean isTestForApp = config.type.isForTesting() &&
-                    (variantData as TestVariantData).testedVariantData.variantConfiguration.type ==
-                    VariantType.DEFAULT
-            boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp
-
-            variantData.preDexTask = preDexTask
-            preDexTask.androidBuilder = scope.globalScope.androidBuilder
-            preDexTask.dexOptions = scope.globalScope.getExtension().dexOptions
-            preDexTask.multiDex = isMultiDexEnabled
-
-            ConventionMappingHelper.map(preDexTask, "inputFiles", pcData.inputLibraries)
-            ConventionMappingHelper.map(preDexTask, "outputFolder") {
-                scope.getPreDexOutputDir();
-            }
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.java
new file mode 100644
index 0000000..24b7d25
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.java
@@ -0,0 +1,300 @@
+/*
+ * 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.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.BaseTask;
+import com.android.build.gradle.internal.variant.ApkVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.build.gradle.internal.PostCompilationData;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.DexOptions;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.core.VariantType;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+
+import org.gradle.api.Action;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class PreDex extends BaseTask {
+
+    @Input
+    public String getBuildToolsVersion() {
+        return getBuildTools().getRevision().toString();
+    }
+
+    private Collection<File> inputFiles;
+
+    private File outputFolder;
+
+    private com.android.build.gradle.internal.dsl.DexOptions dexOptions;
+
+    private boolean multiDex;
+
+    @TaskAction
+    void taskAction(IncrementalTaskInputs taskInputs)
+            throws IOException, LoggedErrorException, InterruptedException {
+
+        final boolean multiDexEnabled = isMultiDex();
+
+        final File outFolder = getOutputFolder();
+
+        boolean incremental = taskInputs.isIncremental();
+        // if we are not in incremental mode, then outOfDate will contain
+        // all the files, but first we need to delete the previous output
+        if (!incremental) {
+            FileUtils.emptyFolder(outFolder);
+        }
+
+        final Set<String> hashs = Sets.newHashSet();
+        final WaitableExecutor<Void> executor = new WaitableExecutor<Void>();
+        final List<File> inputFileDetails = Lists.newArrayList();
+
+        taskInputs.outOfDate(new Action<InputFileDetails>() {
+            @Override
+            public void execute(InputFileDetails change) {
+                inputFileDetails.add(change.getFile());
+            }
+        });
+
+        ProcessOutputHandler outputHandler = new LoggedProcessOutputHandler(getILogger());
+        for (final File file : inputFileDetails) {
+            Callable<Void> action = new PreDexTask(outFolder, file, hashs,
+                    multiDexEnabled, outputHandler);
+            executor.execute(action);
+        }
+
+        if (incremental) {
+            taskInputs.removed(new Action<InputFileDetails>() {
+                @Override
+                public void execute(InputFileDetails change) {
+                    File preDexedFile = getDexFileName(outFolder, change.getFile());
+
+                    try {
+                        FileUtils.deleteFolder(preDexedFile);
+                    } catch (IOException e) {
+                        getLogger().info("Could not delete {}\n{}",
+                                preDexedFile, Throwables.getStackTraceAsString(e));
+                    }
+                }
+            });
+        }
+
+        executor.waitForTasksWithQuickFail(false);
+    }
+
+    private final class PreDexTask implements Callable<Void> {
+        private final File outFolder;
+        private final File fileToProcess;
+        private final Set<String> hashs;
+        private final boolean multiDexEnabled;
+        private final DexOptions options = getDexOptions();
+        private final AndroidBuilder builder = getBuilder();
+        private final ProcessOutputHandler mOutputHandler;
+
+
+        private PreDexTask(
+                File outFolder,
+                File file,
+                Set<String> hashs,
+                boolean multiDexEnabled,
+                ProcessOutputHandler outputHandler) {
+            this.mOutputHandler = outputHandler;
+            this.outFolder = outFolder;
+            this.fileToProcess = file;
+            this.hashs = hashs;
+            this.multiDexEnabled = multiDexEnabled;
+        }
+
+        @Override
+        public Void call() throws Exception {
+            // TODO remove once we can properly add a library as a dependency of its test.
+            String hash = getFileHash(fileToProcess);
+
+            synchronized (hashs) {
+                if (hashs.contains(hash)) {
+                    return null;
+                }
+
+                hashs.add(hash);
+            }
+
+            File preDexedFile = getDexFileName(outFolder, fileToProcess);
+
+            if (multiDexEnabled) {
+                preDexedFile.mkdirs();
+            }
+
+            builder.preDexLibrary(
+                    fileToProcess, preDexedFile, multiDexEnabled, options, mOutputHandler);
+
+            return null;
+        }
+    }
+
+    // this is used automatically by Gradle, even though nothing
+    // in the class uses it.
+    @SuppressWarnings("unused")
+    @InputFiles
+    public Collection<File> getInputFiles() {
+        return inputFiles;
+    }
+
+    public void setInputFiles(Collection<File> inputFiles) {
+        this.inputFiles = inputFiles;
+    }
+
+    @OutputDirectory
+    public File getOutputFolder() {
+        return outputFolder;
+    }
+
+    public void setOutputFolder(File outputFolder) {
+        this.outputFolder = outputFolder;
+    }
+
+    @Nested
+    public com.android.build.gradle.internal.dsl.DexOptions getDexOptions() {
+        return dexOptions;
+    }
+
+    public void setDexOptions(com.android.build.gradle.internal.dsl.DexOptions dexOptions) {
+        this.dexOptions = dexOptions;
+    }
+
+    @Input
+    public boolean isMultiDex() {
+        return multiDex;
+    }
+
+    public void setMultiDex(boolean multiDex) {
+        this.multiDex = multiDex;
+    }
+
+    /**
+     * Returns the hash of a file.
+     * @param file the file to hash
+     */
+    private static String getFileHash(@NonNull File file) throws IOException {
+        HashCode hashCode = Files.hash(file, Hashing.sha1());
+        return hashCode.toString();
+    }
+
+    /**
+     * Returns a unique File for the pre-dexed library, even
+     * if there are 2 libraries with the same file names (but different
+     * paths)
+     *
+     * If multidex is enabled the return File is actually a folder.
+     *
+     * @param outFolder the output folder.
+     * @param inputFile the library.
+     */
+    @NonNull
+    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.
+        String input = inputFile.getAbsolutePath();
+        HashFunction hashFunction = Hashing.sha1();
+        HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE);
+
+        return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR);
+    }
+
+    public static class ConfigAction implements TaskConfigAction<PreDex> {
+
+        private VariantScope scope;
+
+        private Callable<List<File>> inputLibraries;
+
+        public ConfigAction(VariantScope scope, PostCompilationData pcData) {
+            this.scope = scope;
+            this.inputLibraries = pcData.getInputLibrariesCallable();
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName("preDex");
+        }
+
+        @Override
+        public Class<PreDex> getType() {
+            return PreDex.class;
+        }
+
+        @Override
+        public void execute(PreDex preDexTask) {
+            ApkVariantData variantData = (ApkVariantData) scope.getVariantData();
+            VariantConfiguration config = variantData.getVariantConfiguration();
+
+            boolean isTestForApp = config.getType().isForTesting() &&
+                    ((TestVariantData) variantData).getTestedVariantData()
+                            .getVariantConfiguration().getType() == VariantType.DEFAULT;
+            boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp;
+
+            variantData.preDexTask = preDexTask;
+            preDexTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            preDexTask.setVariantName(config.getFullName());
+            preDexTask.dexOptions = scope.getGlobalScope().getExtension().getDexOptions();
+            preDexTask.multiDex = isMultiDexEnabled;
+
+            ConventionMappingHelper.map(preDexTask, "inputFiles", inputLibraries);
+            ConventionMappingHelper.map(preDexTask, "outputFolder", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return scope.getPreDexOutputDir();
+                }
+            });
+        }
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreprocessResourcesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreprocessResourcesTask.java
deleted file mode 100644
index 668fcec..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreprocessResourcesTask.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2015 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 static com.google.common.base.Preconditions.checkNotNull;
-
-import com.android.annotations.NonNull;
-import com.android.build.gradle.internal.tasks.IncrementalTask;
-import com.android.builder.png.VectorDrawableRenderer;
-import com.android.ide.common.res2.FileStatus;
-import com.android.resources.Density;
-import com.google.common.base.Charsets;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.SetMultimap;
-import com.google.common.io.Files;
-import com.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
-
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.InputDirectory;
-import org.gradle.api.tasks.OutputDirectory;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Generates PNGs from Android vector drawable files.
- */
-public class PreprocessResourcesTask extends IncrementalTask {
-    public static final int MIN_SDK = VectorDrawableRenderer.MIN_SDK_WITH_VECTOR_SUPPORT;
-    private static final Type TYPE_TOKEN = new TypeToken<Map<String, Collection<String>>>() {}.getType();
-
-    private final VectorDrawableRenderer renderer = new VectorDrawableRenderer();
-    private File outputResDirectory;
-    private File generatedResDirectory;
-    private File mergedResDirectory;
-    private Collection<Density> densitiesToGenerate;
-
-    @Override
-    protected boolean isIncremental() {
-        return true;
-    }
-
-    @Override
-    protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
-        try {
-            File incrementalMarker = new File(getIncrementalFolder(), "build_was_incremental");
-            Files.touch(incrementalMarker);
-
-            // TODO: store and check the set of densities.
-
-            File stateFile = getStateFile();
-            if (!stateFile.exists()) {
-                doFullTaskAction();
-            }
-
-            SetMultimap<String, String> state = readState(stateFile);
-
-            for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
-                switch (entry.getValue()) {
-                    case NEW:
-                    case CHANGED:
-                        getLogger().debug("Incremental change to {}.",
-                                entry.getKey().getAbsolutePath());
-                        handleFile(entry.getKey(), state);
-                        break;
-                    case REMOVED:
-                        for (String path : state.get(entry.getKey().getAbsolutePath())) {
-                            File file = new File(path);
-                            getLogger().debug("Deleting {}.", file.getAbsolutePath());
-                            file.delete();
-                        }
-                        state.removeAll(entry.getKey());
-                        break;
-                    default:
-                        throw new RuntimeException("Unsupported operation " + entry.getValue());
-                }
-            }
-
-            saveState(state);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    protected void doFullTaskAction() {
-        SetMultimap<String, String> state = HashMultimap.create();
-        emptyFolder(getOutputResDirectory());
-        emptyFolder(getGeneratedResDirectory());
-        emptyFolder(getIncrementalFolder());
-
-        try {
-            for (File resourceFile : getProject().fileTree(getMergedResDirectory())) {
-                handleFile(resourceFile, state);
-            }
-            saveState(state);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @NonNull
-    private static SetMultimap<String, String> readState(@NonNull File stateFile) throws IOException {
-        String stateString = Files.toString(stateFile, Charsets.UTF_8);
-        Map<String, Collection<String>> stateMap = new Gson().fromJson(stateString, TYPE_TOKEN);
-
-        SetMultimap<String, String> state = HashMultimap.create();
-        for (Map.Entry<String, Collection<String>> entry : stateMap.entrySet()) {
-            state.putAll(entry.getKey(), entry.getValue());
-        }
-        return state;
-    }
-
-    private void saveState(@NonNull Multimap<String, String> state) throws IOException {
-        File stateFile = getStateFile();
-        Files.write(new Gson().toJson(state.asMap(), TYPE_TOKEN), stateFile, Charsets.UTF_8);
-    }
-
-    @NonNull
-    private File getStateFile() {
-        return new File(getIncrementalFolder(), "state.json");
-    }
-
-
-    private void handleFile(@NonNull File resourceFile, @NonNull SetMultimap<String, String> state)
-            throws IOException {
-        if (renderer.isVectorDrawable(resourceFile)) {
-            getLogger().debug("Generating files for {}.", resourceFile.getAbsolutePath());
-            Collection<File> generatedFiles = renderer.createPngFiles(
-                    resourceFile,
-                    getGeneratedResDirectory(),
-                    getDensitiesToGenerate());
-
-            for (File generatedFile : generatedFiles) {
-                getLogger().debug("Copying generated file: {}.", generatedFile.getAbsolutePath());
-                copyFile(generatedFile, resourceFile, getGeneratedResDirectory(), state);
-            }
-        } else {
-            getLogger().debug("Copying as-is: {}", resourceFile.getAbsolutePath());
-            copyFile(resourceFile, resourceFile, getMergedResDirectory(), state);
-        }
-    }
-
-    private void copyFile(
-            @NonNull File fileToUse,
-            @NonNull File originalFile,
-            @NonNull File resDir,
-            @NonNull SetMultimap<String, String> state) throws IOException {
-        checkNotNull(resDir);
-        String relativePath =
-                resDir.toURI().relativize(fileToUse.toURI()).getPath();
-
-        File finalFile = new File(getOutputResDirectory(), relativePath);
-        Files.createParentDirs(finalFile);
-        Files.copy(fileToUse, finalFile);
-        state.put(originalFile.getAbsolutePath(), finalFile.getAbsolutePath());
-    }
-
-    /**
-     * Directory in which to put generated files. They will be then copied to the final destination
-     * if this would not overwrite anything.
-     *
-     * @see #getOutputResDirectory()
-     */
-    @OutputDirectory
-    public File getGeneratedResDirectory() {
-        return generatedResDirectory;
-    }
-
-    public void setGeneratedResDirectory(File generatedResDirectory) {
-        this.generatedResDirectory = generatedResDirectory;
-    }
-
-    /**
-     * "Input" resource directory, with all resources for the current variant merged.
-     */
-    @InputDirectory
-    public File getMergedResDirectory() {
-        return mergedResDirectory;
-    }
-
-    public void setMergedResDirectory(File mergedResDirectory) {
-        this.mergedResDirectory = mergedResDirectory;
-    }
-
-    /**
-     * Resources directory that will be passed to aapt.
-     */
-    @OutputDirectory
-    public File getOutputResDirectory() {
-        return outputResDirectory;
-    }
-
-    public void setOutputResDirectory(File outputResDirectory) {
-        this.outputResDirectory = outputResDirectory;
-    }
-
-    public Collection<Density> getDensitiesToGenerate() {
-        return densitiesToGenerate;
-    }
-
-    public void setDensitiesToGenerate(
-            Collection<Density> densitiesToGenerate) {
-        this.densitiesToGenerate = densitiesToGenerate;
-    }
-
-    @NonNull
-    @Override
-    public Logger getLogger() {
-        return Logging.getLogger(getClass());
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
deleted file mode 100644
index dae7a52..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
+++ /dev/null
@@ -1,303 +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.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl
-import com.android.build.gradle.internal.dsl.AaptOptions
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantOutputScope
-import com.android.build.gradle.internal.tasks.IncrementalTask
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.AaptPackageProcessBuilder
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.dependency.LibraryDependency
-import com.google.common.collect.Iterators
-import com.google.common.collect.Lists
-import org.gradle.api.logging.Logging
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputDirectory
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.ParallelizableTask
-
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@ParallelizableTask
-public class ProcessAndroidResources extends IncrementalTask {
-
-    // ----- PUBLIC TASK API -----
-
-    @InputFile
-    File manifestFile
-
-    @InputDirectory
-    File resDir
-
-    @InputDirectory @Optional
-    File assetsDir
-
-    @OutputDirectory @Optional
-    File sourceOutputDir
-
-    @OutputDirectory @Optional
-    File textSymbolOutputDir
-
-    @OutputFile @Optional
-    File packageOutputFile
-
-    @OutputFile @Optional
-    File proguardOutputFile
-
-    @Input
-    Collection<String> resourceConfigs
-
-    @Input @Optional
-    String preferredDensity
-
-    // ----- PRIVATE TASK API -----
-    @Input
-    String getBuildToolsVersion() {
-        getBuildTools().getRevision()
-    }
-
-    @Nested @Optional
-    List<SymbolFileProviderImpl> libraries
-
-    @Input @Optional
-    String packageForR
-
-    @Nested @Optional
-    Collection<String> splits
-
-    @Input
-    boolean enforceUniquePackageName
-
-    // this doesn't change from one build to another, so no need to annotate
-    VariantType type
-
-    @Input
-    boolean debuggable
-
-    @Input
-    boolean pseudoLocalesEnabled
-
-    @Nested
-    AaptOptions aaptOptions
-
-    private boolean isSplitPackage(File file, File resBaseName) {
-        if (file.getName().startsWith(resBaseName.getName())) {
-            for (String split : splits) {
-                if (file.getName().contains(split)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    protected void doFullTaskAction() {
-        // we have to clean the source folder output in case the package name changed.
-        File srcOut = getSourceOutputDir()
-        if (srcOut != null) {
-            emptyFolder(srcOut)
-        }
-
-        File resOutBaseNameFile = getPackageOutputFile()
-
-        // we have to check the resource output folder in case some splits were removed, we should
-        // manually remove them.
-        File packageOutputFolder = getResDir()
-        if (resOutBaseNameFile != null) {
-            for (File file : packageOutputFolder.listFiles()) {
-                if (!isSplitPackage(file, resOutBaseNameFile)) {
-                    file.delete();
-                }
-            }
-        }
-
-        AaptPackageProcessBuilder aaptPackageCommandBuilder =
-                new AaptPackageProcessBuilder(getManifestFile(), getAaptOptions())
-                    .setAssetsFolder(getAssetsDir())
-                    .setResFolder(getResDir())
-                    .setLibraries(getLibraries())
-                    .setPackageForR(getPackageForR())
-                    .setSourceOutputDir(srcOut?.absolutePath)
-                    .setSymbolOutputDir(getTextSymbolOutputDir()?.absolutePath)
-                    .setResPackageOutput(resOutBaseNameFile?.absolutePath)
-                    .setProguardOutput(getProguardOutputFile()?.absolutePath)
-                    .setType(getType())
-                    .setDebuggable(getDebuggable())
-                    .setPseudoLocalesEnabled(getPseudoLocalesEnabled())
-                    .setResourceConfigs(getResourceConfigs())
-                    .setSplits(getSplits())
-                    .setPreferredDensity(getPreferredDensity())
-
-        getBuilder().processResources(
-                aaptPackageCommandBuilder,
-                getEnforceUniquePackageName())
-    }
-
-    public static class ConfigAction implements TaskConfigAction<ProcessAndroidResources> {
-
-        VariantOutputScope scope;
-        File symbolLocation;
-        boolean generateResourcePackage;
-
-        ConfigAction(VariantOutputScope scope, File symbolLocation, generateResourcePackage) {
-            this.scope = scope
-            this.symbolLocation = symbolLocation
-            this.generateResourcePackage = generateResourcePackage
-        }
-
-        @Override
-        String getName() {
-            return scope.getTaskName("process", "Resources")
-        }
-
-        @Override
-        Class<ProcessAndroidResources> getType() {
-            return ProcessAndroidResources.class;
-        }
-
-        @Override
-        void execute(ProcessAndroidResources processResources) {
-            BaseVariantOutputData variantOutputData = scope.variantOutputData
-            BaseVariantData variantData = scope.variantScope.variantData
-            variantOutputData.processResourcesTask = processResources
-            VariantConfiguration config = variantData.getVariantConfiguration()
-
-            processResources.androidBuilder = scope.globalScope.androidBuilder
-
-            if (variantData.getSplitHandlingPolicy() ==
-                    BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
-                Set<String> allFilters = new HashSet<>();
-                allFilters.addAll(variantData.getFilters(com.android.build.OutputFile.FilterType.DENSITY))
-                allFilters.addAll(variantData.getFilters(com.android.build.OutputFile.FilterType.LANGUAGE))
-                processResources.splits = allFilters;
-            }
-
-            // only generate code if the density filter is null, and if we haven't generated
-            // it yet (if you have abi + density splits, then several abi output will have no
-            // densityFilter)
-            if (variantOutputData.getMainOutputFile().getFilter(com.android.build.OutputFile.DENSITY) == null
-                    && variantData.generateRClassTask == null) {
-                variantData.generateRClassTask = processResources
-                processResources.enforceUniquePackageName = scope.globalScope.getExtension().getEnforceUniquePackageName()
-
-                ConventionMappingHelper.map(processResources, "libraries") {
-                    getTextSymbolDependencies(config.allLibraries)
-                }
-                ConventionMappingHelper.map(processResources, "packageForR") {
-                    config.originalApplicationId
-                }
-
-                // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
-                ConventionMappingHelper.map(processResources, "sourceOutputDir") {
-                    scope.getVariantScope().getRClassSourceOutputDir();
-                }
-
-                ConventionMappingHelper.map(processResources, "textSymbolOutputDir") {
-                    symbolLocation
-                }
-
-                if (config.buildType.isMinifyEnabled()) {
-                    if (config.buildType.shrinkResources && config.useJack) {
-                        LoggingUtil.displayWarning(Logging.getLogger(this.class), scope.globalScope.project,
-                                "shrinkResources does not yet work with useJack=true")
-                    }
-                    ConventionMappingHelper.map(processResources, "proguardOutputFile") {
-                        new File(
-                                "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/proguard-rules/${config.dirName}/aapt_rules.txt")
-                    }
-                } else if (config.buildType.shrinkResources) {
-                    LoggingUtil.displayWarning(Logging.getLogger(this.class), scope.globalScope.project,
-                            "To shrink resources you must also enable ProGuard")
-                }
-            }
-
-            ConventionMappingHelper.map(processResources, "manifestFile") {
-                variantOutputData.manifestProcessorTask.getOutputFile()
-            }
-
-            ConventionMappingHelper.map(processResources, "resDir") {
-                variantData.finalResourcesDir
-            }
-
-            ConventionMappingHelper.map(processResources, "assetsDir") {
-                variantData.mergeAssetsTask.outputDir
-            }
-
-            if (generateResourcePackage) {
-                ConventionMappingHelper.map(processResources, "packageOutputFile") {
-                    scope.getProcessResourcePackageOutputFile()
-                }
-            }
-
-            ConventionMappingHelper.map(processResources, "type") { config.type }
-            ConventionMappingHelper.map(processResources, "debuggable") { config.buildType.debuggable }
-            ConventionMappingHelper.map(processResources, "aaptOptions") { scope.globalScope.getExtension().aaptOptions }
-            ConventionMappingHelper.map(processResources, "pseudoLocalesEnabled") { config.buildType.pseudoLocalesEnabled }
-
-            ConventionMappingHelper.map(processResources, "resourceConfigs") {
-                Collection<String> resConfigs = config.mergedFlavor.resourceConfigurations;
-                if (resConfigs != null && resConfigs.size() == 1
-                        && Iterators.getOnlyElement(resConfigs.iterator()).equals("auto")) {
-
-                    return variantData.discoverListOfResourceConfigs();
-                }
-                return config.mergedFlavor.resourceConfigurations
-            }
-
-            ConventionMappingHelper.map(processResources, "preferredDensity") {
-                variantOutputData.getMainOutputFile().getFilter(com.android.build.OutputFile.DENSITY)
-            }
-
-        }
-
-        private static <T> Set<T> removeAllNullEntries(Set<T> input) {
-            HashSet<T> output = new HashSet<T>();
-            for (T element : input) {
-                if (element != null) {
-                    output.add(element);
-                }
-            }
-            return output;
-        }
-
-        @NonNull
-        private static List<SymbolFileProviderImpl> getTextSymbolDependencies(
-                List<LibraryDependency> libraries) {
-
-            List<SymbolFileProviderImpl> list = Lists.newArrayListWithCapacity(libraries.size())
-
-            for (LibraryDependency lib : libraries) {
-                list.add(new SymbolFileProviderImpl(lib.manifest, lib.symbolFile))
-            }
-
-            return list
-        }
-    }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.java
new file mode 100644
index 0000000..638286c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.java
@@ -0,0 +1,503 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantOutputScope;
+import com.android.build.gradle.internal.tasks.IncrementalTask;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.core.AaptPackageProcessBuilder;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantType;
+import com.android.builder.dependency.LibraryDependency;
+import com.android.ide.common.blame.ParsingProcessOutputHandler;
+import com.android.ide.common.blame.parser.ToolOutputParser;
+import com.android.ide.common.blame.parser.aapt.AaptOutputParser;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.utils.FileUtils;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.ParallelizableTask;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class ProcessAndroidResources extends IncrementalTask {
+
+    private File manifestFile;
+
+    private File resDir;
+
+    private File assetsDir;
+
+    private File sourceOutputDir;
+
+    private File textSymbolOutputDir;
+
+    private File packageOutputFile;
+
+    private File proguardOutputFile;
+
+    private Collection<String> resourceConfigs;
+
+    private String preferredDensity;
+
+    private List<SymbolFileProviderImpl> libraries;
+
+    private String packageForR;
+
+    private Collection<String> splits;
+
+    private boolean enforceUniquePackageName;
+
+    private VariantType type;
+
+    private boolean debuggable;
+
+    private boolean pseudoLocalesEnabled;
+
+    private AaptOptions aaptOptions;
+
+
+    @Override
+    protected void doFullTaskAction() throws IOException {
+        // we have to clean the source folder output in case the package name changed.
+        File srcOut = getSourceOutputDir();
+        if (srcOut != null) {
+            FileUtils.emptyFolder(srcOut);
+        }
+
+        File resOutBaseNameFile = getPackageOutputFile();
+
+        // we have to check the resource output folder in case some splits were removed, we should
+        // manually remove them.
+        File packageOutputFolder = getResDir();
+        if (resOutBaseNameFile != null) {
+            for (File file : packageOutputFolder.listFiles()) {
+                if (!isSplitPackage(file, resOutBaseNameFile)) {
+                    //noinspection ResultOfMethodCallIgnored
+                    file.delete();
+                }
+            }
+        }
+
+        AaptPackageProcessBuilder aaptPackageCommandBuilder =
+                new AaptPackageProcessBuilder(getManifestFile(), getAaptOptions())
+                        .setAssetsFolder(getAssetsDir())
+                        .setResFolder(getResDir())
+                        .setLibraries(getLibraries())
+                        .setPackageForR(getPackageForR())
+                        .setSourceOutputDir(absolutePath(srcOut))
+                        .setSymbolOutputDir(absolutePath(getTextSymbolOutputDir()))
+                        .setResPackageOutput(absolutePath(resOutBaseNameFile))
+                        .setProguardOutput(absolutePath(getProguardOutputFile()))
+                        .setType(getType())
+                        .setDebuggable(getDebuggable())
+                        .setPseudoLocalesEnabled(getPseudoLocalesEnabled())
+                        .setResourceConfigs(getResourceConfigs())
+                        .setSplits(getSplits())
+                        .setPreferredDensity(getPreferredDensity());
+
+        @NonNull
+        AndroidBuilder builder = getBuilder();
+        ProcessOutputHandler processOutputHandler = new ParsingProcessOutputHandler(
+                new ToolOutputParser(new AaptOutputParser(), getILogger()),
+                builder.getErrorReporter());
+        try {
+            builder.processResources(
+                    aaptPackageCommandBuilder,
+                    getEnforceUniquePackageName(),
+                    processOutputHandler);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ProcessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private boolean isSplitPackage(File file, File resBaseName) {
+        if (file.getName().startsWith(resBaseName.getName())) {
+            for (String split : splits) {
+                if (file.getName().contains(split)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Nullable
+    private static String absolutePath(@Nullable File file) {
+        return file == null ? null : file.getAbsolutePath();
+    }
+
+    public static class ConfigAction implements TaskConfigAction<ProcessAndroidResources> {
+
+        private VariantOutputScope scope;
+        private File symbolLocation;
+        private boolean generateResourcePackage;
+
+        public ConfigAction(
+                VariantOutputScope scope, File symbolLocation, boolean generateResourcePackage) {
+            this.scope = scope;
+            this.symbolLocation = symbolLocation;
+            this.generateResourcePackage = generateResourcePackage;
+        }
+
+        @Override
+        public String getName() {
+            return scope.getTaskName("process", "Resources");
+        }
+
+        @Override
+        public Class<ProcessAndroidResources> getType() {
+            return ProcessAndroidResources.class;
+        }
+
+        @Override
+        public void execute(ProcessAndroidResources processResources) {
+            final BaseVariantOutputData variantOutputData = scope.getVariantOutputData();
+            final BaseVariantData<? extends BaseVariantOutputData> variantData =
+                    scope.getVariantScope().getVariantData();
+            variantOutputData.processResourcesTask = processResources;
+            final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+
+            processResources.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            processResources.setVariantName(config.getFullName());
+
+            if (variantData.getSplitHandlingPolicy() ==
+                    BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
+                Set<String> allFilters = new HashSet<String>();
+                allFilters.addAll(
+                        variantData.getFilters(com.android.build.OutputFile.FilterType.DENSITY));
+                allFilters.addAll(
+                        variantData.getFilters(com.android.build.OutputFile.FilterType.LANGUAGE));
+                processResources.splits = allFilters;
+            }
+
+            // only generate code if the density filter is null, and if we haven't generated
+            // it yet (if you have abi + density splits, then several abi output will have no
+            // densityFilter)
+            if (variantOutputData.getMainOutputFile()
+                    .getFilter(com.android.build.OutputFile.DENSITY) == null
+                    && variantData.generateRClassTask == null) {
+                variantData.generateRClassTask = processResources;
+                processResources.enforceUniquePackageName = scope.getGlobalScope().getExtension()
+                        .getEnforceUniquePackageName();
+
+                ConventionMappingHelper.map(processResources, "libraries",
+                        new Callable<List<SymbolFileProviderImpl>>() {
+                            @Override
+                            public List<SymbolFileProviderImpl> call() throws Exception {
+                                return getTextSymbolDependencies(config.getAllLibraries());
+                            }
+                        });
+                ConventionMappingHelper.map(processResources, "packageForR",
+                        new Callable<String>() {
+                            @Override
+                            public String call() throws Exception {
+                                return config.getOriginalApplicationId();
+                            }
+                        });
+
+                // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
+                processResources
+                        .setSourceOutputDir(scope.getVariantScope().getRClassSourceOutputDir());
+                processResources.setTextSymbolOutputDir(symbolLocation);
+
+                if (config.getBuildType().isMinifyEnabled()) {
+                    if (config.getBuildType().isShrinkResources() && config.getUseJack()) {
+                        LoggingUtil.displayWarning(Logging.getLogger(getClass()),
+                                scope.getGlobalScope().getProject(),
+                                "shrinkResources does not yet work with useJack=true");
+                    }
+                    processResources.setProguardOutputFile(
+                            scope.getVariantScope().getProcessAndroidResourcesProguardOutputFile());
+
+                } else if (config.getBuildType().isShrinkResources()) {
+                    LoggingUtil.displayWarning(Logging.getLogger(getClass()),
+                            scope.getGlobalScope().getProject(),
+                            "To shrink resources you must also enable ProGuard");
+                }
+            }
+
+            ConventionMappingHelper.map(processResources, "manifestFile", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return variantOutputData.manifestProcessorTask.getOutputFile();
+                }
+            });
+
+            ConventionMappingHelper.map(processResources, "resDir", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return scope.getVariantScope().getFinalResourcesDir();
+                }
+            });
+
+            ConventionMappingHelper.map(processResources, "assetsDir", new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return variantData.mergeAssetsTask.getOutputDir();
+                }
+            });
+
+            if (generateResourcePackage) {
+                processResources.setPackageOutputFile(scope.getProcessResourcePackageOutputFile());
+            }
+
+            processResources.setType(config.getType());
+            processResources.setDebuggable(config.getBuildType().isDebuggable());
+            processResources.setAaptOptions(scope.getGlobalScope().getExtension().getAaptOptions());
+            processResources
+                    .setPseudoLocalesEnabled(config.getBuildType().isPseudoLocalesEnabled());
+
+            ConventionMappingHelper.map(processResources, "resourceConfigs",
+                    new Callable<Collection<String>>() {
+                        @Override
+                        public Collection<String> call() throws Exception {
+                            Collection<String> resConfigs =
+                                    config.getMergedFlavor().getResourceConfigurations();
+                            if (resConfigs.size() == 1 &&
+                                    Iterators.getOnlyElement(resConfigs.iterator())
+                                            .equals("auto")) {
+                                return variantData.discoverListOfResourceConfigs();
+                            }
+                            return config.getMergedFlavor().getResourceConfigurations();
+                        }
+                    });
+
+            ConventionMappingHelper.map(processResources, "preferredDensity",
+                    new Callable<String>() {
+                        @Override
+                        public String call() throws Exception {
+                            return variantOutputData.getMainOutputFile()
+                                    .getFilter(com.android.build.OutputFile.DENSITY);
+                        }
+                    });
+
+
+        }
+
+        @NonNull
+        private static List<SymbolFileProviderImpl> getTextSymbolDependencies(
+                List<LibraryDependency> libraries) {
+
+            List<SymbolFileProviderImpl> list = Lists.newArrayListWithCapacity(libraries.size());
+
+            for (LibraryDependency lib : libraries) {
+                list.add(new SymbolFileProviderImpl(lib));
+            }
+
+            return list;
+        }
+    }
+
+    @InputFile
+    public File getManifestFile() {
+        return manifestFile;
+    }
+
+    public void setManifestFile(File manifestFile) {
+        this.manifestFile = manifestFile;
+    }
+
+    @NonNull
+    @InputDirectory
+    public File getResDir() {
+        return resDir;
+    }
+
+    public void setResDir(@NonNull File resDir) {
+        this.resDir = resDir;
+    }
+
+    @OutputDirectory
+    @Optional
+    public File getAssetsDir() {
+        return assetsDir;
+    }
+
+    public void setAssetsDir(File assetsDir) {
+        this.assetsDir = assetsDir;
+    }
+
+    @OutputDirectory
+    @Optional
+    public File getSourceOutputDir() {
+        return sourceOutputDir;
+    }
+
+    public void setSourceOutputDir(File sourceOutputDir) {
+        this.sourceOutputDir = sourceOutputDir;
+    }
+
+    @OutputDirectory
+    @Optional
+    public File getTextSymbolOutputDir() {
+        return textSymbolOutputDir;
+    }
+
+    public void setTextSymbolOutputDir(File textSymbolOutputDir) {
+        this.textSymbolOutputDir = textSymbolOutputDir;
+    }
+
+    @OutputFile
+    @Optional
+    public File getPackageOutputFile() {
+        return packageOutputFile;
+    }
+
+    public void setPackageOutputFile(File packageOutputFile) {
+        this.packageOutputFile = packageOutputFile;
+    }
+
+    @OutputFile
+    @Optional
+    public File getProguardOutputFile() {
+        return proguardOutputFile;
+    }
+
+    public void setProguardOutputFile(File proguardOutputFile) {
+        this.proguardOutputFile = proguardOutputFile;
+    }
+
+    @Input
+    public Collection<String> getResourceConfigs() {
+        return resourceConfigs;
+    }
+
+    public void setResourceConfigs(Collection<String> resourceConfigs) {
+        this.resourceConfigs = resourceConfigs;
+    }
+
+    @Input
+    @Optional
+    public String getPreferredDensity() {
+        return preferredDensity;
+    }
+
+    public void setPreferredDensity(String preferredDensity) {
+        this.preferredDensity = preferredDensity;
+    }
+
+    @Input
+    String getBuildToolsVersion() {
+        return getBuildTools().getRevision().toString();
+    }
+
+    @Nested
+    @Optional
+    public List<SymbolFileProviderImpl> getLibraries() {
+        return libraries;
+    }
+
+    public void setLibraries(
+            List<SymbolFileProviderImpl> libraries) {
+        this.libraries = libraries;
+    }
+
+    @Input
+    @Optional
+    public String getPackageForR() {
+        return packageForR;
+    }
+
+    public void setPackageForR(String packageForR) {
+        this.packageForR = packageForR;
+    }
+
+    @Nested
+    @Optional
+    public Collection<String> getSplits() {
+        return splits;
+    }
+
+    public void setSplits(Collection<String> splits) {
+        this.splits = splits;
+    }
+
+    @Input
+    public boolean getEnforceUniquePackageName() {
+        return enforceUniquePackageName;
+    }
+
+    public void setEnforceUniquePackageName(boolean enforceUniquePackageName) {
+        this.enforceUniquePackageName = enforceUniquePackageName;
+    }
+
+    /** Does not change between incremental builds, so does not need to be @Input. */
+    public VariantType getType() {
+        return type;
+    }
+
+    public void setType(VariantType type) {
+        this.type = type;
+    }
+
+    @Input
+    public boolean getDebuggable() {
+        return debuggable;
+    }
+
+    public void setDebuggable(boolean debuggable) {
+        this.debuggable = debuggable;
+    }
+
+    @Input
+    public boolean getPseudoLocalesEnabled() {
+        return pseudoLocalesEnabled;
+    }
+
+    public void setPseudoLocalesEnabled(boolean pseudoLocalesEnabled) {
+        this.pseudoLocalesEnabled = pseudoLocalesEnabled;
+    }
+
+    @Nested
+    public AaptOptions getAaptOptions() {
+        return aaptOptions;
+    }
+
+    public void setAaptOptions(AaptOptions aaptOptions) {
+        this.aaptOptions = aaptOptions;
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy
index e738598..ce492fe 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.tasks
-
 import com.android.build.gradle.internal.TaskManager
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
@@ -23,7 +22,6 @@
 import com.android.build.gradle.internal.variant.BaseVariantOutputData
 import com.android.builder.core.AndroidBuilder
 import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
 import com.android.builder.model.ProductFlavor
 import com.android.manifmerger.ManifestMerger2
 import org.gradle.api.tasks.Input
@@ -31,7 +29,6 @@
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.ParallelizableTask
-
 /**
  * a Task that only merge a single manifest with its overlays.
  */
@@ -136,6 +133,7 @@
 
             variantOutputData.manifestProcessorTask = processManifest
             processManifest.androidBuilder = androidBuilder
+            processManifest.setVariantName(config.getFullName())
 
             processManifest.variantConfiguration = config
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
index 0144816..1ee359b 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
@@ -14,23 +14,19 @@
  * limitations under the License.
  */
 package com.android.build.gradle.tasks
-
 import com.android.build.gradle.internal.DependencyManager
 import com.android.build.gradle.internal.dependency.ManifestDependencyImpl
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantOutputScope
 import com.android.build.gradle.internal.scope.VariantScope
 import com.android.build.gradle.internal.variant.BaseVariantOutputData
 import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
 import com.google.common.collect.Lists
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.ParallelizableTask
-
 /**
  * A task that processes the manifest
  */
@@ -145,6 +141,7 @@
             variantOutputData.manifestProcessorTask = processTestManifestTask
 
             processTestManifestTask.androidBuilder = scope.globalScope.androidBuilder
+            processTestManifestTask.setVariantName(config.getFullName())
 
             ConventionMappingHelper.map(processTestManifestTask, "testApplicationId") {
                 config.applicationId
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
index 6946fad..18e581c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
@@ -15,21 +15,19 @@
  */
 
 package com.android.build.gradle.tasks
-
 import com.android.annotations.NonNull
 import com.android.build.gradle.internal.core.GradleVariantConfiguration
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantOutputScope
 import com.android.build.gradle.internal.scope.VariantScope
 import com.android.build.gradle.internal.tasks.NdkTask
 import com.android.build.gradle.internal.variant.BaseVariantData
 import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
 import com.android.builder.model.ApiVersion
 import com.android.builder.model.ProductFlavor
+import com.android.ide.common.process.LoggedProcessOutputHandler
 import com.android.sdklib.SdkVersionInfo
+import com.android.utils.FileUtils
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.OutputDirectory
@@ -37,7 +35,6 @@
 
 import static com.android.builder.model.AndroidProject.FD_GENERATED
 import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
 /**
  * Task to compile Renderscript files. Supports incremental update.
  */
@@ -86,19 +83,19 @@
     boolean ndkMode
 
     @TaskAction
-    void taskAction() {
+    void taskAction() throws IOException {
         // this is full run (always), clean the previous outputs
         File sourceDestDir = getSourceOutputDir()
-        emptyFolder(sourceDestDir)
+        FileUtils.emptyFolder(sourceDestDir)
 
         File resDestDir = getResOutputDir()
-        emptyFolder(resDestDir)
+        FileUtils.emptyFolder(resDestDir)
 
         File objDestDir = getObjOutputDir()
-        emptyFolder(objDestDir)
+        FileUtils.emptyFolder(objDestDir)
 
         File libDestDir = getLibOutputDir()
-        emptyFolder(libDestDir)
+        FileUtils.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.
@@ -117,7 +114,8 @@
                 getOptimLevel(),
                 getNdkMode(),
                 getSupportMode(),
-                getNdkConfig()?.abiFilters)
+                getNdkConfig()?.abiFilters,
+                new LoggedProcessOutputHandler(getILogger()))
     }
 
     // ----- ConfigAction -----
@@ -150,6 +148,7 @@
             ProductFlavor mergedFlavor = config.mergedFlavor
             boolean ndkMode = config.renderscriptNdkModeEnabled
             renderscriptTask.androidBuilder = scope.globalScope.androidBuilder
+            renderscriptTask.setVariantName(config.getFullName())
 
             ConventionMappingHelper.map(renderscriptTask, "targetApi") {
                 int targetApi = mergedFlavor.renderscriptTargetApi != null ?
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
index 7a81061..649a8d0 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
@@ -333,37 +333,16 @@
                             // compressed in the source .ap_ file must be left uncompressed
                             // here, since for example RAW files need to remain uncompressed in
                             // the APK such that they can be mmap'ed at runtime.
-                            //
-                            // Create the JarEntry manually rather than setting up
-                            // the JarEntry via new JarEntry(entry):
-                            // We don't know what the exact compressed size will
-                            // be for this file; it depends on the compression level,
-                            // and we don't have a way to know what it was for the
-                            // input file. Empirically it seems to be 9 (when it is
-                            // set to a different level the compressed sizes we produce
-                            // are larger than the ones we see in typical .ap_ input files).
-                            // However, we don't want to set the size specifically; we leave it
-                            // as the default value such that the actual compressed size
-                            // is used; without that we can hit errors like
-                            //   java.util.zip.ZipException: invalid entry compressed size
-                            //                     (expected 7344 but got 7352 bytes); ignoring
-                            // (see issue 80115 for more).
-                            //
-                            // However, for stored (not compressed) entries, we *do* have
-                            // to set the size and compressed size; without it, the zip
-                            // encoder will throw an exception. Luckily, for uncompressed files
-                            // we predictably know the compressed size. Therefore, we special case
-                            // this based on the entry's method.
-                            JarEntry outEntry = new JarEntry(entry.getName());
-                            if (entry.getTime() != -1L) {
-                                outEntry.setTime(entry.getTime());
-                            }
-                            int method = entry.getMethod();
-                            outEntry.setMethod(method);
-                            if (method == ZipEntry.STORED) {
-                                outEntry.setCompressedSize(entry.getCompressedSize());
-                                outEntry.setSize(entry.getSize());
-                                outEntry.setCrc(entry.getCrc());
+                            // Preserve the STORED method of the input entry.
+                            JarEntry outEntry;
+                            if (entry.getMethod() == JarEntry.STORED) {
+                                outEntry = new JarEntry(entry);
+                            } else {
+                                // Create a new entry so that the compressed len is recomputed.
+                                outEntry = new JarEntry(name);
+                                if (entry.getTime() != -1L) {
+                                    outEntry.setTime(entry.getTime());
+                                }
                             }
 
                             zos.putNextEntry(outEntry);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
index c45c5db..4031e6c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
@@ -20,13 +20,10 @@
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantOutputScope
 import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.build.gradle.internal.variant.ApkVariantOutputData
 import com.android.build.gradle.internal.variant.BaseVariantData
 import com.android.build.gradle.internal.variant.BaseVariantOutputData
 import com.android.builder.core.AaptPackageProcessBuilder
-import com.android.builder.model.AndroidProject
-import com.android.utils.StringHelper
-import org.codehaus.groovy.runtime.StringGroovyMethods
+import com.android.ide.common.process.LoggedProcessOutputHandler
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.OutputFile
@@ -35,8 +32,6 @@
 
 import java.util.concurrent.Callable
 
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
 /**
  * Task which strips out unused resources
  * <p>
@@ -83,13 +78,13 @@
         try {
             def processResourcesTask = variantData.generateRClassTask
             File sourceDir = processResourcesTask.sourceOutputDir
-            File resourceDir = variantData.finalResourcesDir
+            File resourceDir = variantData.getScope().getFinalResourcesDir()
             File mergedManifest = variantOutputData.manifestProcessorTask.manifestOutputFile
 
             // Analyze resources and usages and strip out unused
             def analyzer = new ResourceUsageAnalyzer(
                     sourceDir,
-                    variantData.obfuscatedClassesJar,
+                    variantData.getScope().getProguardOutputFile(),
                     mergedManifest,
                     variantData.getMappingFile(),
                     resourceDir)
@@ -133,6 +128,7 @@
                 getBuilder().processResources(
                         aaptPackageCommandBuilder,
                         processResourcesTask.getEnforceUniquePackageName(),
+                        new LoggedProcessOutputHandler(getBuilder().getLogger())
                 )
             } else {
                 // Just rewrite the .ap_ file to strip out the res/ files for unused resources
@@ -206,6 +202,7 @@
             BaseVariantData<? extends BaseVariantOutputData> variantData =
                     scope.variantScope.variantData
             task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+            task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
             task.variantOutputData = scope.variantOutputData;
 
             final String outputBaseName = scope.variantOutputData.getBaseName();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/SimpleWorkQueue.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/SimpleWorkQueue.java
new file mode 100644
index 0000000..f54f286
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/SimpleWorkQueue.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.builder.tasks.Job;
+import com.android.builder.tasks.JobContext;
+import com.android.builder.tasks.QueueThreadContextAdapter;
+import com.android.builder.tasks.WorkQueue;
+import com.android.utils.StdLogger;
+
+/**
+ * Common utilities to use a simple shared instance of {@link WorkQueue}.
+ * The context for job will be empty, and it is the responsibility of the
+ * {@link com.android.builder.tasks.WorkQueue.QueueTask} to have enough context to run.
+ */
+public class SimpleWorkQueue {
+
+    /**
+     * Simple {@link WorkQueue} context implementation that simply runs the proguard job.
+     */
+    private static class EmptyThreadContext extends
+            QueueThreadContextAdapter<Void> {
+
+        @Override
+        public void runTask(@NonNull Job<Void> job) throws Exception {
+            job.runTask(new JobContext<Void>(null /* payload */));
+            job.finished();
+        }
+    }
+
+    /**
+     * singleton work queue for all proguard invocations.
+     */
+    private static final WorkQueue<Void> WORK_QUEUE =
+            new WorkQueue<Void>(
+                    new StdLogger(StdLogger.Level.VERBOSE),
+                    new EmptyThreadContext(), "Tasks limiter", 4);
+
+
+    static void push(Job<Void> job) throws InterruptedException {
+        WORK_QUEUE.push(job);
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java
index 52ad9f2..136db9e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java
@@ -16,7 +16,11 @@
 
 package com.android.build.gradle.tasks;
 
+import com.android.builder.core.VariantConfiguration;
+
 import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
 import org.gradle.api.tasks.TaskAction;
 
 import java.io.IOException;
@@ -29,8 +33,9 @@
  * input files like the tested application classes and the tested application mapping file.
  */
 public class TestModuleProGuardTask extends ProGuardTask {
+    private Logger logger;
     private Configuration mappingConfiguration;
-    private Configuration classesConfiguration;
+    private VariantConfiguration variantConfiguration;
 
 
     /**
@@ -45,18 +50,36 @@
      * Sets the {@link Configuration} to later retrieve the test application classes jar file.
      */
     public void setClassesConfiguration(Configuration configuration) {
-        this.classesConfiguration = configuration;
         dependsOn(configuration);
     }
 
+
+    public void setVariantConfiguration(
+            VariantConfiguration variantConfiguration) {
+        this.variantConfiguration = variantConfiguration;
+    }
+
+    public void setLogger(Logger logger) {
+        this.logger = logger;
+    }
+
     @Override
     @TaskAction
     public void proguard() throws ParseException, IOException {
-        if (mappingConfiguration.getFiles().isEmpty() || classesConfiguration.getFiles().isEmpty()) {
-            return;
+        if (logger.isEnabled(LogLevel.INFO)) {
+            logger.info("test module mapping file " + mappingConfiguration.getSingleFile());
+            for (Object file : variantConfiguration.getPackagedJars()) {
+                logger.info("test module proguard input " + file);
+
+            }
+            for (Object file : variantConfiguration.getProvidedOnlyJars()) {
+                logger.info("test module proguard library " + file);
+            }
         }
-        applymapping(mappingConfiguration.getSingleFile());
-        libraryjars(classesConfiguration.getSingleFile());
+
+        if (mappingConfiguration.getSingleFile().isFile()) {
+            applymapping(mappingConfiguration.getSingleFile());
+        }
         super.proguard();
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java
index 15e910e..6df5096 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java
@@ -9,7 +9,6 @@
 import com.android.build.gradle.internal.scope.VariantOutputScope;
 import com.android.build.gradle.internal.tasks.FileSupplier;
 import com.android.build.gradle.internal.variant.ApkVariantOutputData;
-import com.android.utils.StringHelper;
 
 import org.gradle.api.Action;
 import org.gradle.api.DefaultTask;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
new file mode 100644
index 0000000..78ec03d
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2015 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.annotations;
+
+import static com.android.SdkConstants.DOT_JAVA;
+import static java.io.File.pathSeparator;
+import static java.io.File.pathSeparatorChar;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.EcjParser;
+import com.android.utils.Pair;
+import com.google.common.base.Charsets;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The extract annotations driver is a command line interface to extracting annotations
+ * from a source tree. It's similar to the gradle
+ * {@link com.android.build.gradle.tasks.ExtractAnnotations} task,
+ * but usable from the command line and outside Gradle, for example
+ * to extract annotations from the Android framework itself (which is not built with
+ * Gradle). It also allows other options only interesting for extracting
+ * platform annotations, such as filtering all APIs and constants through an
+ * API white-list (such that we for example can pull annotations from the master
+ * branch which has the latest metadata, but only expose APIs that are actually in
+ * a released platform), as well as translating android.annotation annotations into
+ * android.support.annotations.
+ */
+public class ExtractAnnotationsDriver {
+    public static void main(String[] args) {
+        new ExtractAnnotationsDriver().run(args);
+    }
+
+    private static void usage(PrintStream output) {
+        output.println("Usage: " + ExtractAnnotationsDriver.class.getSimpleName() + " <flags>");
+        output.println(" --sources <paths>       : Source directories to extract annotations from. ");
+        output.println("                           Separate paths with " + pathSeparator + ", and you can use @ ");
+        output.println("                           as a filename prefix to have the filenames fed from a file");
+        output.println("--classpath <paths>      : Directories and .jar files to resolve symbols from");
+        output.println("--output <zip path>      : The .zip file to write the extracted annotations to, if any");
+        output.println("--proguard <path>        : The proguard.cfg file to write the keep rules to, if any");
+        output.println();
+        output.println("Optional flags:");
+        output.println("--merge-zips <paths>     : Existing external annotation files to merge in");
+        output.println("--quiet                  : Don't print summary information");
+        output.println("--rmtypedefs <folder>    : Remove typedef classes found in the given folder");
+        output.println("--allow-missing-types    : Don't fail even if some types can't be resolved");
+        output.println("--allow-errors           : Don't fail even if there are some compiler errors");
+        output.println("--encoding <encoding>    : Encoding (defaults to utf-8)");
+        output.println("--language-level <level> : Java source language level, typically 1.6 (default) or 1.7");
+        output.println("--api-filter <api.txt>   : A framework API definition to restrict included APIs to");
+        output.println("--hide-filtered          : If filtering out non-APIs, supply this flag to hide listing matches");
+        output.println("--skip-class-retention   : Don't extract annotations that have class retention");
+        System.exit(-1);
+    }
+
+    @SuppressWarnings("MethodMayBeStatic")
+    public void run(@NonNull String[] args) {
+        List<String> classpath = Lists.newArrayList();
+        List<File> sources = Lists.newArrayList();
+        List<File> mergePaths = Lists.newArrayList();
+        List<File> apiFilters = null;
+        File rmTypeDefs = null;
+        boolean verbose = true;
+        boolean allowMissingTypes = false;
+        boolean allowErrors = false;
+        boolean listFiltered = true;
+        boolean skipClassRetention = false;
+
+        String encoding = Charsets.UTF_8.name();
+        File output = null;
+        File proguard = null;
+        long languageLevel = EcjParser.getLanguageLevel(1, 7);
+        if (args.length == 1 && "--help".equals(args[0])) {
+            usage(System.out);
+        }
+        if (args.length < 2) {
+            usage(System.err);
+        }
+        for (int i = 0, n = args.length; i < n; i++) {
+            String flag = args[i];
+
+            if (flag.equals("--quiet")) {
+                verbose = false;
+                continue;
+            } else if (flag.equals("--allow-missing-types")) {
+                allowMissingTypes = true;
+                continue;
+            } else if (flag.equals("--allow-errors")) {
+                allowErrors = true;
+                continue;
+            } else if (flag.equals("--hide-filtered")) {
+                listFiltered = false;
+                continue;
+            } else if (flag.equals("--skip-class-retention")) {
+                skipClassRetention = true;
+                continue;
+            }
+            if (i == n - 1) {
+                usage(System.err);
+            }
+            String value = args[i + 1];
+            i++;
+
+            if (flag.equals("--sources")) {
+                sources = getFiles(value);
+            } else if (flag.equals("--classpath")) {
+                classpath = getPaths(value);
+            } else if (flag.equals("--merge-zips")) {
+                mergePaths = getFiles(value);
+            } else if (flag.equals("--output")) {
+                output = new File(value);
+                if (output.exists()) {
+                    if (output.isDirectory()) {
+                        abort(output + " is a directory");
+                    }
+                    boolean deleted = output.delete();
+                    if (!deleted) {
+                        abort("Could not delete previous version of " + output);
+                    }
+                } else if (output.getParentFile() != null && !output.getParentFile().exists()) {
+                    abort(output.getParentFile() + " does not exist");
+                }
+            } else if (flag.equals("--proguard")) {
+                proguard = new File(value);
+                if (proguard.exists()) {
+                    if (proguard.isDirectory()) {
+                        abort(proguard + " is a directory");
+                    }
+                    boolean deleted = proguard.delete();
+                    if (!deleted) {
+                        abort("Could not delete previous version of " + proguard);
+                    }
+                } else if (proguard.getParentFile() != null && !proguard.getParentFile().exists()) {
+                    abort(proguard.getParentFile() + " does not exist");
+                }
+            } else if (flag.equals("--encoding")) {
+                encoding = value;
+            } else if (flag.equals("--api-filter")) {
+                if (apiFilters == null) {
+                    apiFilters = Lists.newArrayList();
+                }
+                for (String path : Splitter.on(",").omitEmptyStrings().split(value)) {
+                    File apiFilter = new File(path);
+                    if (!apiFilter.isFile()) {
+                        String message = apiFilter + " does not exist or is not a file";
+                        abort(message);
+                    }
+                    apiFilters.add(apiFilter);
+                }
+            } else if (flag.equals("--language-level")) {
+                if ("1.6".equals(value)) {
+                    languageLevel = EcjParser.getLanguageLevel(1, 6);
+                } else if ("1.7".equals(value)) {
+                    languageLevel = EcjParser.getLanguageLevel(1, 7);
+                } else {
+                    abort("Unsupported language level " + value);
+                }
+            } else if (flag.equals("--rmtypedefs")) {
+                rmTypeDefs = new File(value);
+                if (!rmTypeDefs.isDirectory()) {
+                    abort(rmTypeDefs + " is not a directory");
+                }
+            } else {
+                System.err.println("Unknown flag " + flag + ": Use --help for usage information");
+            }
+        }
+
+        if (sources.isEmpty()) {
+            abort("Must specify at least one source path");
+        }
+        if (classpath.isEmpty()) {
+            abort("Must specify classpath pointing to at least android.jar or the framework");
+        }
+        if (output == null && proguard == null) {
+            abort("Must specify output path with --output or a proguard path with --proguard");
+        }
+
+        // API definition files
+        ApiDatabase database = null;
+        if (apiFilters != null && !apiFilters.isEmpty()) {
+            try {
+                List<String> lines = Lists.newArrayList();
+                for (File file : apiFilters) {
+                    lines.addAll(Files.readLines(file, Charsets.UTF_8));
+                }
+                database = new ApiDatabase(lines);
+            } catch (IOException e) {
+                abort("Could not open API database " + apiFilters + ": " + e.getLocalizedMessage());
+            }
+        }
+
+        Extractor extractor = new Extractor(database, rmTypeDefs, verbose, !skipClassRetention,
+                true);
+        extractor.setListIgnored(listFiltered);
+
+        try {
+            Pair<Collection<CompilationUnitDeclaration>, INameEnvironment>
+                    pair = parseSources(sources, classpath, encoding, languageLevel);
+            Collection<CompilationUnitDeclaration> units = pair.getFirst();
+
+            boolean abort = false;
+            int errorCount = 0;
+            for (CompilationUnitDeclaration unit : units) {
+                // so maybe I don't need my map!!
+                IProblem[] problems = unit.compilationResult().getAllProblems();
+                if (problems != null) {
+                    for (IProblem problem : problems) {
+                        if (problem.isError()) {
+                            errorCount++;
+                            String message = problem.getMessage();
+                            if (allowMissingTypes) {
+                                if (message.contains("cannot be resolved")) {
+                                    continue;
+                                }
+                            }
+
+                            System.out.println("Error: " +
+                                    new String(problem.getOriginatingFileName()) + ":" +
+                                    problem.getSourceLineNumber() + ": " + message);
+                            abort = !allowErrors;
+                        }
+                    }
+                }
+            }
+            if (errorCount > 0) {
+                System.err.println("Found " + errorCount + " errors");
+            }
+            if (abort) {
+                abort("Not extracting annotations (compilation problems encountered)");
+            }
+
+            INameEnvironment environment = pair.getSecond();
+            extractor.extractFromProjectSource(units);
+
+            if (mergePaths != null) {
+                for (File jar : mergePaths) {
+                    extractor.mergeExisting(jar);
+                }
+            }
+
+            extractor.export(output, proguard);
+
+            // Remove typedefs?
+            //noinspection VariableNotUsedInsideIf
+            if (rmTypeDefs != null) {
+                extractor.removeTypedefClasses();
+            }
+
+            environment.cleanup();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void abort(@NonNull String message) {
+        System.err.println(message);
+        System.exit(-1);
+    }
+
+    private static List<File> getFiles(String value) {
+        List<File> files = Lists.newArrayList();
+        Splitter splitter = Splitter.on(pathSeparatorChar).omitEmptyStrings().trimResults();
+        for (String path : splitter.split(value)) {
+            if (path.startsWith("@")) {
+                // Special syntax for providing files in a list
+                File sourcePath = new File(path.substring(1));
+                if (!sourcePath.exists()) {
+                    abort(sourcePath + " does not exist");
+                }
+                try {
+                    for (String line : Files.readLines(sourcePath, Charsets.UTF_8)) {
+                        line = line.trim();
+                        if (!line.isEmpty()) {
+                            File file = new File(line);
+                            if (!file.exists()) {
+                                System.err.println("Warning: Could not find file " + line +
+                                        " listed in " + sourcePath);
+                            }
+                            files.add(file);
+                        }
+                    }
+                    continue;
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    System.exit(-1);
+                }
+            }
+            File file = new File(path);
+            if (!file.exists()) {
+                abort(file + " does not exist");
+            }
+            files.add(file);
+        }
+
+        return files;
+    }
+
+    private static List<String> getPaths(String value) {
+        List<File> files = getFiles(value);
+        List<String> paths = Lists.newArrayListWithExpectedSize(files.size());
+        for (File file : files) {
+            paths.add(file.getPath());
+        }
+        return paths;
+    }
+
+    private static void addJavaSources(List<File> list, File file) {
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            if (files != null) {
+                for (File child : files) {
+                    addJavaSources(list, child);
+                }
+            }
+        } else {
+            if (file.isFile() && file.getName().endsWith(DOT_JAVA)) {
+                list.add(file);
+            }
+        }
+    }
+
+    private static List<File> gatherJavaSources(List<File> sourcePath) {
+        List<File> sources = Lists.newArrayList();
+        for (File file : sourcePath) {
+            addJavaSources(sources, file);
+        }
+        return sources;
+    }
+
+    @NonNull
+    private static Pair<Collection<CompilationUnitDeclaration>,INameEnvironment> parseSources(
+            @NonNull List<File> sourcePaths,
+            @NonNull List<String> classpath,
+            @NonNull String encoding,
+            long languageLevel)
+            throws IOException {
+        List<ICompilationUnit> sourceUnits = Lists.newArrayListWithExpectedSize(100);
+
+        for (File source : gatherJavaSources(sourcePaths)) {
+            char[] contents = Util.getFileCharContent(source, encoding);
+            ICompilationUnit unit = new CompilationUnit(contents, source.getPath(), encoding);
+            sourceUnits.add(unit);
+        }
+
+        Map<ICompilationUnit, CompilationUnitDeclaration> outputMap = Maps.newHashMapWithExpectedSize(
+                sourceUnits.size());
+
+        CompilerOptions options = EcjParser.createCompilerOptions();
+        options.docCommentSupport = true; // So I can find @hide
+
+        // Note: We can *not* set options.ignoreMethodBodies=true because it disables
+        // type attribution!
+
+        options.sourceLevel = languageLevel;
+        options.complianceLevel = options.sourceLevel;
+        // We don't generate code, but just in case the parser consults this flag
+        // and makes sure that it's not greater than the source level:
+        options.targetJDK = options.sourceLevel;
+        options.originalComplianceLevel = options.sourceLevel;
+        options.originalSourceLevel = options.sourceLevel;
+        options.inlineJsrBytecode = true; // >= 1.5
+
+        INameEnvironment environment = EcjParser.parse(options, sourceUnits, classpath,
+                outputMap, null);
+        Collection<CompilationUnitDeclaration> parsedUnits = outputMap.values();
+        return Pair.of(parsedUnits, environment);
+    }
+}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
index 3be4cab..8cc6042 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
@@ -32,6 +32,7 @@
 import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
 import static com.android.SdkConstants.TYPE_DEF_VALUE_ATTRIBUTE;
 import static com.android.SdkConstants.VALUE_TRUE;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.INT_RANGE_ANNOTATION;
 import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
 
 import com.android.annotations.NonNull;
@@ -61,10 +62,21 @@
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.NameReference;
 import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
+import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
 import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
 import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
+import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
+import org.eclipse.jdt.internal.compiler.impl.CharConstant;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
+import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
+import org.eclipse.jdt.internal.compiler.impl.IntConstant;
+import org.eclipse.jdt.internal.compiler.impl.LongConstant;
 import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
+import org.eclipse.jdt.internal.compiler.impl.StringConstant;
 import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
 import org.eclipse.jdt.internal.compiler.lookup.Binding;
 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
@@ -85,13 +97,17 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
 
+import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.io.Writer;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -134,12 +150,12 @@
  * - Ignore annotations defined on @hide elements
  */
 public class Extractor {
-    /** Whether to sort annotation attributes (otherwise their declaration order is used) */
-    private static final boolean SORT_ANNOTATIONS = false;
-
     /** Whether we should include type args like &lt;T*gt; in external annotations signatures */
     private static final boolean INCLUDE_TYPE_ARGS = false;
 
+    /** Whether to sort annotation attributes (otherwise their declaration order is used) */
+    private final boolean sortAnnotations;
+
     /**
      * Whether we should include class-retention annotations into the extracted file;
      * we don't need {@code android.support.annotation.Nullable} to be in the extracted XML
@@ -159,11 +175,15 @@
     public static final String ANDROID_ANNOTATIONS_PREFIX = "android.annotation.";
     public static final String ANDROID_NULLABLE = "android.annotation.Nullable";
     public static final String SUPPORT_NULLABLE = "android.support.annotation.Nullable";
+    public static final String SUPPORT_KEEP = "android.support.annotation.Keep";
     public static final String RESOURCE_TYPE_ANNOTATIONS_SUFFIX = "Res";
     public static final String ANDROID_NOTNULL = "android.annotation.NonNull";
     public static final String SUPPORT_NOTNULL = "android.support.annotation.NonNull";
     public static final String ANDROID_INT_DEF = "android.annotation.IntDef";
+    public static final String ANDROID_INT_RANGE = "android.annotation.IntRange";
     public static final String ANDROID_STRING_DEF = "android.annotation.StringDef";
+    public static final String REQUIRES_PERMISSION = "android.support.annotation.RequiresPermission";
+    public static final String ANDROID_REQUIRES_PERMISSION = "android.annotation.RequiresPermission";
     public static final String IDEA_NULLABLE = "org.jetbrains.annotations.Nullable";
     public static final String IDEA_NOTNULL = "org.jetbrains.annotations.NotNull";
     public static final String IDEA_MAGIC = "org.intellij.lang.annotations.MagicConstant";
@@ -172,7 +192,7 @@
     public static final String ATTR_VAL = "val";
 
     @NonNull
-    private final Map<String, AnnotationData> types = Maps.newHashMap();
+    private final Map<String, List<AnnotationData>> types = Maps.newHashMap();
 
     @NonNull
     private final Set<String> irrelevantAnnotations = Sets.newHashSet();
@@ -180,37 +200,39 @@
     private final File classDir;
 
     @NonNull
-    private Map<String, Map<String, List<Item>>> itemMap = Maps.newHashMap();
+    private final Map<String, Map<String, List<Item>>> itemMap = Maps.newHashMap();
 
     @Nullable
     private final ApiDatabase apiFilter;
 
     private final boolean displayInfo;
 
-    private Map<String,Integer> stats = Maps.newHashMap();
+    private final Map<String,Integer> stats = Maps.newHashMap();
     private int filteredCount;
     private int mergedCount;
-    private Set<CompilationUnitDeclaration> processedFiles = Sets.newHashSetWithExpectedSize(100);
-    private Set<String> ignoredAnnotations = Sets.newHashSet();
+    private final Set<CompilationUnitDeclaration> processedFiles = Sets.newHashSetWithExpectedSize(100);
+    private final Set<String> ignoredAnnotations = Sets.newHashSet();
     private boolean listIgnored;
-    private Map<String,Annotation> typedefs;
-    private List<File> classFiles;
+    private Map<String,List<Annotation>> typedefs;
+    private List<String> typedefClasses;
     private Map<String,Boolean> sourceRetention;
+    private final List<Item> keepItems = Lists.newArrayList();
 
     public Extractor(@Nullable ApiDatabase apiFilter, @Nullable File classDir, boolean displayInfo,
-            boolean includeClassRetentionAnnotations) {
+            boolean includeClassRetentionAnnotations, boolean sortAnnotations) {
         this.apiFilter = apiFilter;
         this.listIgnored = apiFilter != null;
         this.classDir = classDir;
         this.displayInfo = displayInfo;
         this.includeClassRetentionAnnotations = includeClassRetentionAnnotations;
+        this.sortAnnotations = sortAnnotations;
     }
 
     public void extractFromProjectSource(Collection<CompilationUnitDeclaration> units) {
         TypedefCollector collector = new TypedefCollector(units, false /*requireHide*/,
                 true /*requireSourceRetention*/);
         typedefs = collector.getTypedefs();
-        classFiles = collector.getNonPublicTypedefClassFiles();
+        typedefClasses = collector.getNonPublicTypedefClasses();
 
         for (CompilationUnitDeclaration unit : units) {
             analyze(unit);
@@ -218,35 +240,40 @@
     }
 
     public void removeTypedefClasses() {
-        if (classDir != null && classFiles != null && !classFiles.isEmpty()) {
-            int count = 0;
-            for (File file : classFiles) {
-                if (!file.isAbsolute()) {
-                    file = new File(classDir, file.getPath());
-                }
-                if (file.exists()) {
-                    boolean deleted = file.delete();
-                    if (deleted) {
-                        count++;
-                    } else {
-                        warning("Could not delete typedef class " + file.getPath());
-                    }
-                }
-            }
-            info("Deleted " + count + " typedef annotation classes");
+        if (classDir != null && typedefClasses != null && !typedefClasses.isEmpty()) {
+            boolean quiet = false;
+            boolean verbose = false;
+            boolean dryRun = false;
+            //noinspection ConstantConditions
+            TypedefRemover remover = new TypedefRemover(this, quiet, verbose, dryRun);
+            remover.remove(classDir, typedefClasses);
         }
     }
 
-    public void export(@NonNull File output) {
-        if (itemMap.isEmpty()) {
-            if (output.exists()) {
-                //noinspection ResultOfMethodCallIgnored
-                output.delete();
+    public void export(@Nullable File annotationsZip, @Nullable File proguardCfg) {
+        if (proguardCfg != null) {
+            if (keepItems.isEmpty()) {
+                if (proguardCfg.exists()) {
+                    //noinspection ResultOfMethodCallIgnored
+                    proguardCfg.delete();
+                }
+            } else if (writeKeepRules(proguardCfg)) {
+                info("ProGuard keep rules written to " + proguardCfg);
             }
-        } else if (writeOutputFile(output)) {
-            writeStats();
-            info("Annotations written to " + output);
         }
+
+        if (annotationsZip != null) {
+            if (itemMap.isEmpty()) {
+                if (annotationsZip.exists()) {
+                    //noinspection ResultOfMethodCallIgnored
+                    annotationsZip.delete();
+                }
+            } else if (writeExternalAnnotations(annotationsZip)) {
+                writeStats();
+                info("Annotations written to " + annotationsZip);
+            }
+        }
+
     }
 
     public void writeStats() {
@@ -486,36 +513,46 @@
         if (annotations != null) {
             for (Annotation annotation : annotations) {
                 if (isRelevantAnnotation(annotation)) {
-                    AnnotationData annotationData = createAnnotation(annotation);
-                    if (annotationData != null) {
-                        item.annotations.add(annotationData);
+                    String fqn = getFqn(annotation);
+                    if (SUPPORT_KEEP.equals(fqn)) {
+                        // Put keep rules in a different place; we don't want to write
+                        // these out into the external annotations database, they go
+                        // into a special proguard file
+                        keepItems.add(item);
+                    } else {
+                        addAnnotation(annotation, fqn, item.annotations);
                     }
                 }
             }
         }
     }
 
-    @Nullable
-    private AnnotationData createAnnotation(@NonNull Annotation annotation) {
-        String fqn = getFqn(annotation);
+    private void addAnnotation(@NonNull Annotation annotation, @Nullable String fqn,
+            @NonNull List<AnnotationData> list) {
         if (fqn == null) {
-            return null;
+            return;
         }
+
         if (fqn.equals(ANDROID_NULLABLE) || fqn.equals(SUPPORT_NULLABLE)) {
             recordStats(fqn);
-            return new AnnotationData(SUPPORT_NULLABLE);
+            list.add(new AnnotationData(SUPPORT_NULLABLE));
+            return;
         }
 
         if (fqn.equals(ANDROID_NOTNULL) || fqn.equals(SUPPORT_NOTNULL)) {
             recordStats(fqn);
-            return new AnnotationData(SUPPORT_NOTNULL);
+            list.add(new AnnotationData(SUPPORT_NOTNULL));
+            return;
         }
 
         if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
                 && fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
             recordStats(fqn);
-            return new AnnotationData(fqn);
-        } else if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
+            list.add(new AnnotationData(fqn));
+            return;
+        }
+
+        if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
             // System annotations: translate to support library annotations
             if (fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
                 // Translate e.g. android.annotation.DrawableRes to
@@ -524,10 +561,11 @@
                         fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
                 if (!includeClassRetentionAnnotations
                         && !hasSourceRetention(resAnnotation, null)) {
-                    return null;
+                    return;
                 }
                 recordStats(resAnnotation);
-                return new AnnotationData(resAnnotation);
+                list.add(new AnnotationData(resAnnotation));
+                return;
             } else if (isRelevantFrameworkAnnotation(fqn)) {
                 // Translate other android.annotation annotations into corresponding
                 // support annotations
@@ -535,23 +573,25 @@
                         fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
                 if (!includeClassRetentionAnnotations
                         && !hasSourceRetention(supportAnnotation, null)) {
-                    return null;
+                    return;
                 }
                 recordStats(supportAnnotation);
-                return createData(supportAnnotation, annotation);
+                list.add(createData(supportAnnotation, annotation));
             }
         }
 
         if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
             recordStats(fqn);
-            return createData(fqn, annotation);
+            list.add(createData(fqn, annotation));
+            return;
         }
 
         if (isMagicConstant(fqn)) {
-            return types.get(fqn);
+            List<AnnotationData> indirect = types.get(fqn);
+            if (indirect != null) {
+                list.addAll(indirect);
+            }
         }
-
-        return null;
     }
 
     private void recordStats(String fqn) {
@@ -582,14 +622,12 @@
             return false;
         }
         if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
-            //noinspection PointlessBooleanExpression,ConstantConditions,RedundantIfStatement
-            if (!includeClassRetentionAnnotations && !hasSourceRetention(fqn, annotation)) {
-                return false;
+            if (fqn.equals(SUPPORT_KEEP)) {
+                return true; // even with class file retention we want to process these
             }
 
-            //noinspection RedundantIfStatement
-            if (fqn.endsWith(".Keep")) {
-                // TODO: Extract into a proguard file
+            //noinspection PointlessBooleanExpression,ConstantConditions,RedundantIfStatement
+            if (!includeClassRetentionAnnotations && !hasSourceRetention(fqn, annotation)) {
                 return false;
             }
 
@@ -624,39 +662,88 @@
         if (types.containsKey(typeName) ||
                 typeName.equals(INT_DEF_ANNOTATION) ||
                 typeName.equals(STRING_DEF_ANNOTATION) ||
+                typeName.equals(INT_RANGE_ANNOTATION) ||
+                typeName.equals(ANDROID_INT_RANGE) ||
                 typeName.equals(ANDROID_INT_DEF) ||
                 typeName.equals(ANDROID_STRING_DEF)) {
             return true;
         }
 
-        Annotation typeDef = typedefs.get(typeName);
+        List<Annotation> typeDefs = typedefs.get(typeName);
         // We only support a single level of IntDef type annotations, not arbitrary nesting
-        if (typeDef != null) {
-            String fqn = getFqn(typeDef);
-            if (fqn != null &&
-                    (fqn.equals(INT_DEF_ANNOTATION) ||
-                            fqn.equals(STRING_DEF_ANNOTATION) ||
-                            fqn.equals(ANDROID_INT_DEF) ||
-                            fqn.equals(ANDROID_STRING_DEF))) {
-                AnnotationData a = createAnnotation(typeDef);
-                if (a != null) {
-                    types.put(typeName, a);
-                    return true;
+        if (typeDefs != null) {
+            boolean match = false;
+            for (Annotation typeDef : typeDefs) {
+                String fqn = getFqn(typeDef);
+                if (isNestedAnnotation(fqn)) {
+                    List<AnnotationData> list = types.get(typeName);
+                    if (list == null) {
+                        list = new ArrayList<AnnotationData>(2);
+                        types.put(typeName, list);
+                    }
+                    addAnnotation(typeDef, fqn, list);
+                    match = true;
                 }
             }
-        }
 
+            return match;
+        }
 
         irrelevantAnnotations.add(typeName);
 
         return false;
     }
 
-    private boolean writeOutputFile(File dest) {
-        try {
-            FileOutputStream fileOutputStream = new FileOutputStream(dest);
-            JarOutputStream zos = new JarOutputStream(fileOutputStream);
+    static boolean isNestedAnnotation(@Nullable String fqn) {
+        return (fqn != null &&
+                (fqn.equals(INT_DEF_ANNOTATION) ||
+                        fqn.equals(STRING_DEF_ANNOTATION) ||
+                        fqn.equals(REQUIRES_PERMISSION) ||
+                        fqn.equals(ANDROID_REQUIRES_PERMISSION) ||
+                        fqn.equals(INT_RANGE_ANNOTATION) ||
+                        fqn.equals(ANDROID_INT_RANGE) ||
+                        fqn.equals(ANDROID_INT_DEF) ||
+                        fqn.equals(ANDROID_STRING_DEF)));
+    }
+
+    private boolean writeKeepRules(@NonNull File proguardCfg) {
+        if (!keepItems.isEmpty()) {
             try {
+                Writer writer = new BufferedWriter(new FileWriter(proguardCfg));
+                try {
+                    Collections.sort(keepItems);
+                    for (Item item : keepItems) {
+                        writer.write(item.getKeepRule());
+                        writer.write('\n');
+                    }
+                } finally {
+                    writer.close();
+                }
+            } catch (IOException ioe) {
+                error(ioe.toString());
+                return true;
+            }
+
+            // Now that we've handled these items, remove them from the list
+            // such that we don't accidentally also emit them into the annotations.zip
+            // file, where they are not needed
+            for (Item item : keepItems) {
+                removeItem(item.getQualifiedClassName(), item);
+            }
+        } else if (proguardCfg.exists()) {
+            //noinspection ResultOfMethodCallIgnored
+            proguardCfg.delete();
+        }
+        return false;
+    }
+
+    private boolean writeExternalAnnotations(@NonNull File annotationsZip) {
+        try {
+            FileOutputStream fileOutputStream = new FileOutputStream(annotationsZip);
+            JarOutputStream zos = new JarOutputStream(fileOutputStream);
+
+            try {
+                // TODO: Extract to share with keep rules
                 List<String> sortedPackages = new ArrayList<String>(itemMap.keySet());
                 Collections.sort(sortedPackages);
                 for (String pkg : sortedPackages) {
@@ -741,15 +828,15 @@
         items.add(item);
     }
 
-    private void removeItem(@NonNull String fqn, @NonNull Item item) {
-        String pkg = getPackage(fqn);
+    private void removeItem(@NonNull String classFqn, @NonNull Item item) {
+        String pkg = getPackage(classFqn);
         Map<String, List<Item>> classMap = itemMap.get(pkg);
         if (classMap != null) {
-            List<Item> items = classMap.get(fqn);
+            List<Item> items = classMap.get(classFqn);
             if (items != null) {
                 items.remove(item);
                 if (items.isEmpty()) {
-                    classMap.remove(fqn);
+                    classMap.remove(classFqn);
                     if (classMap.isEmpty()) {
                         itemMap.remove(pkg);
                     }
@@ -849,7 +936,12 @@
             Document document = XmlUtils.parseDocument(xml, false);
             mergeDocument(document);
         } catch (Exception e) {
-            warning("Failed to merge " + path + ": " + e.toString());
+            String message = "Failed to merge " + path + ": " + e.toString();
+            if (e instanceof SAXParseException) {
+                SAXParseException spe = (SAXParseException)e;
+                message = "Line " + spe.getLineNumber() + ":" + spe.getColumnNumber() + ": " + message;
+            }
+            error(message);
             if (!(e instanceof IOException)) {
                 e.printStackTrace();
             }
@@ -857,6 +949,7 @@
     }
 
     private void mergeDocument(@NonNull Document document) {
+        @SuppressWarnings("SpellCheckingInspection")
         final Pattern XML_SIGNATURE = Pattern.compile(
                 // Class (FieldName | Type? Name(ArgList) Argnum?)
                 //"(\\S+) (\\S+|(.*)\\s+(\\S+)\\((.*)\\)( \\d+)?)");
@@ -933,7 +1026,7 @@
             }
             filteredCount++;
         } else {
-            FieldItem fieldItem = new FieldItem(containingClass, fieldName);
+            FieldItem fieldItem = new FieldItem(containingClass, ClassKind.CLASS, fieldName, null);
             Item existing = findItem(containingClass, fieldItem);
             if (existing != null) {
                 mergedCount += mergeAnnotations(item, existing);
@@ -961,7 +1054,7 @@
         String argNum = matcher.group(7);
         if (argNum != null) {
             argNum = argNum.trim();
-            ParameterItem parameterItem = new ParameterItem(containingClass, type,
+            ParameterItem parameterItem = new ParameterItem(containingClass, ClassKind.CLASS, type,
                     methodName, parameters, constructor, argNum);
             Item existing = findItem(containingClass, parameterItem);
 
@@ -979,8 +1072,8 @@
                 mergedCount += addAnnotations(item, parameterItem);
             }
         } else {
-            MethodItem methodItem = new MethodItem(containingClass, type, methodName,
-                    parameters, constructor);
+            MethodItem methodItem = new MethodItem(containingClass, ClassKind.CLASS, type,
+                    methodName, parameters, constructor);
             Item existing = findItem(containingClass, methodItem);
             if (existing != null) {
                 mergedCount += mergeAnnotations(item, existing);
@@ -1236,8 +1329,9 @@
 
             annotation = new AnnotationData(
                     valName.equals("stringValues") ? STRING_DEF_ANNOTATION : INT_DEF_ANNOTATION,
-                    TYPE_DEF_VALUE_ATTRIBUTE, value,
-                    flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null);
+                    new String[] {
+                            TYPE_DEF_VALUE_ATTRIBUTE, value,
+                            flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null });
         } else if (STRING_DEF_ANNOTATION.equals(name) || ANDROID_STRING_DEF.equals(name) ||
                 INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name)) {
             List<Element> children = getChildren(annotationElement);
@@ -1254,14 +1348,14 @@
             boolean intDef = INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name);
             annotation = new AnnotationData(
                     intDef ? INT_DEF_ANNOTATION : STRING_DEF_ANNOTATION,
-                    TYPE_DEF_VALUE_ATTRIBUTE, value,
-                    flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null);
+                    new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value,
+                    flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null});
         } else if (IDEA_CONTRACT.equals(name)) {
             List<Element> children = getChildren(annotationElement);
             assert children.size() == 1 : children.size();
             Element valueElement = children.get(0);
             String value = valueElement.getAttribute(ATTR_VAL);
-            annotation = new AnnotationData(name, TYPE_DEF_VALUE_ATTRIBUTE, value, null, null);
+            annotation = new AnnotationData(name, new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value });
         } else if (isNonNull(name)) {
             annotation = new AnnotationData(SUPPORT_NOTNULL);
         } else if (isNullable(name)) {
@@ -1271,7 +1365,17 @@
             }
             annotation = new AnnotationData(SUPPORT_NULLABLE);
         } else {
-            annotation = new AnnotationData(name, null, null);
+            List<Element> children = getChildren(annotationElement);
+            if (children.isEmpty()) {
+                return new AnnotationData(name);
+            }
+            List<String> attributeStrings = Lists.newArrayList();
+            for (Element valueElement : children) {
+                attributeStrings.add(valueElement.getAttribute(ATTR_NAME));
+                attributeStrings.add(valueElement.getAttribute(ATTR_VAL));
+            }
+            annotation = new AnnotationData(name, attributeStrings.toArray(
+                    new String[attributeStrings.size()]));
         }
         return annotation;
     }
@@ -1354,43 +1458,25 @@
         public final String name;
 
         @Nullable
-        public final String attributeName1;
-
-        @Nullable
-        public final String attributeValue1;
-
-        @Nullable
-        public final String attributeName2;
-
-        @Nullable
-        public final String attributeValue2;
+        public String[] attributeStrings;
 
         @Nullable
         public MemberValuePair[] attributes;
 
         private AnnotationData(@NonNull String name) {
-            this(name, null, null, null, null);
+            this.name = name;
         }
 
         private AnnotationData(@NonNull String name, @Nullable MemberValuePair[] pairs) {
-            this(name, null, null, null, null);
+            this(name);
             attributes = pairs;
             assert attributes == null || attributes.length > 0;
         }
 
-        private AnnotationData(@NonNull String name, @Nullable String attributeName,
-                @Nullable String attributeValue) {
-            this(name, attributeName, attributeValue, null, null);
-        }
-
-        private AnnotationData(@NonNull String name,
-                @Nullable String attributeName1, @Nullable String attributeValue1,
-                @Nullable String attributeName2, @Nullable String attributeValue2) {
-            this.name = name;
-            this.attributeName1 = attributeName1;
-            this.attributeValue1 = attributeValue1;
-            this.attributeName2 = attributeName2;
-            this.attributeValue2 = attributeValue2;
+        private AnnotationData(@NonNull String name, @Nullable String[] attributeStrings) {
+            this(name);
+            this.attributeStrings = attributeStrings;
+            assert attributeStrings != null && attributeStrings.length > 0;
         }
 
         void write(PrintWriter writer) {
@@ -1401,7 +1487,7 @@
                 writer.print("\">");
                 writer.println();
                 //noinspection PointlessBooleanExpression,ConstantConditions
-                if (attributes.length > 1 && SORT_ANNOTATIONS) {
+                if (attributes.length > 1 && sortAnnotations) {
                     // Ensure that the value attribute is written first
                     Arrays.sort(attributes, new Comparator<MemberValuePair>() {
                         private String getName(MemberValuePair pair) {
@@ -1429,6 +1515,25 @@
                     });
                 }
 
+                MemberValuePair[] attributes = this.attributes;
+
+                if (attributes.length == 1
+                        && name.startsWith(REQUIRES_PERMISSION)
+                        && name.length() > REQUIRES_PERMISSION.length()
+                        && attributes[0].value instanceof SingleMemberAnnotation) {
+                    // The external annotations format does not allow for nested/complex annotations.
+                    // However, these special annotations (@RequiresPermission.Read,
+                    // @RequiresPermission.Write, etc) are known to only be simple containers with a
+                    // single permission child, so instead we "inline" the content:
+                    //  @Read(@RequiresPermission(allOf={P1,P2},conditional=true)
+                    //     =>
+                    //      @RequiresPermission.Read(allOf({P1,P2},conditional=true)
+                    // That's setting attributes that don't actually exist on the container permission,
+                    // but we'll counteract that on the read-annotations side.
+                    SingleMemberAnnotation annotation = (SingleMemberAnnotation)attributes[0].value;
+                    attributes = annotation.memberValuePairs();
+                }
+
                 for (MemberValuePair pair : attributes) {
                     writer.print("      <val name=\"");
                     if (pair.name != null) {
@@ -1442,19 +1547,19 @@
                 }
                 writer.println("    </annotation>");
 
-            } else if (attributeValue1 != null) {
+            } else if (attributeStrings != null) {
                 writer.print("\">");
                 writer.println();
-                writer.print("      <val name=\"");
-                writer.print(attributeName1);
-                writer.print("\" val=\"");
-                writer.print(escapeXml(attributeValue1));
-                writer.println("\" />");
-                if (attributeValue2 != null) {
+                for (int i = 0; i < attributeStrings.length; i += 2) {
+                    String name = attributeStrings[i];
+                    String value = attributeStrings[i + 1];
+                    if (name == null) {
+                        continue;
+                    }
                     writer.print("      <val name=\"");
-                    writer.print(attributeName2);
+                    writer.print(name);
                     writer.print("\" val=\"");
-                    writer.print(escapeXml(attributeValue2));
+                    writer.print(escapeXml(value));
                     writer.println("\" />");
                 }
                 writer.println("    </annotation>");
@@ -1519,6 +1624,39 @@
                 if (reference.binding != null) {
                     if (reference.binding instanceof FieldBinding) {
                         FieldBinding fb = (FieldBinding)reference.binding;
+                        Constant constant = fb.constant();
+                        if (constant != null && constant != Constant.NotAConstant &&
+                                !(name.equals(INT_DEF_ANNOTATION)) &&
+                                !(name.equals(STRING_DEF_ANNOTATION))) {
+                            if (constant instanceof StringConstant) {
+                                sb.append('"').append(constant.stringValue()).append('"');
+                                return true;
+                            } else if (constant instanceof IntConstant) {
+                                sb.append(Integer.toString(constant.intValue()));
+                                return true;
+                            } else if (constant instanceof BooleanConstant) {
+                                sb.append(Boolean.toString(constant.booleanValue()));
+                                return true;
+                            } else if (constant instanceof LongConstant) {
+                                sb.append(Long.toString(constant.longValue()));
+                                return true;
+                            } else if (constant instanceof DoubleConstant) {
+                                sb.append(Double.toString(constant.doubleValue()));
+                                return true;
+                            } else if (constant instanceof CharConstant) {
+                                sb.append('\'').append(Character.toString(constant.charValue())).append('\'');
+                                return true;
+                            } else if (constant instanceof FloatConstant) {
+                                sb.append(Float.toString(constant.floatValue()));
+                                return true;
+                            } else if (constant instanceof ShortConstant) {
+                                sb.append(Short.toString(constant.shortValue()));
+                                return true;
+                            } else if (constant instanceof ByteConstant) {
+                                sb.append(Byte.toString(constant.byteValue()));
+                                return true;
+                            }
+                        }
                         if (fb.declaringClass != null) {
                             if (apiFilter != null &&
                                     !apiFilter.hasField(
@@ -1588,16 +1726,62 @@
         }
     }
 
+    public enum ClassKind {
+        CLASS,
+        INTERFACE,
+        ENUM,
+        ANNOTATION;
+
+        @NonNull
+        public static ClassKind forType(@Nullable TypeDeclaration declaration) {
+            if (declaration == null) {
+                return CLASS;
+            }
+            switch (TypeDeclaration.kind(declaration.modifiers)) {
+                case TypeDeclaration.INTERFACE_DECL:
+                    return INTERFACE;
+                case TypeDeclaration.ANNOTATION_TYPE_DECL:
+                    return ANNOTATION;
+                case TypeDeclaration.ENUM_DECL:
+                    return ENUM;
+                default:
+                    return CLASS;
+            }
+        }
+
+        public String getKeepType() {
+            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+            switch (this) {
+                case INTERFACE:
+                    return "interface";
+                case ENUM:
+                    return "enum";
+
+                case ANNOTATION:
+                case CLASS:
+                default:
+                    return "class";
+            }
+        }
+    }
+
     /**
      * An item in the XML file: this corresponds to a method, a field, or a method parameter, and
      * has an associated set of annotations
      */
     private abstract static class Item implements Comparable<Item> {
+        @NonNull public final String containingClass;
+        @NonNull public final ClassKind classKind;
+
+        public Item(@NonNull String containingClass, @NonNull ClassKind classKind) {
+            this.containingClass = containingClass;
+            this.classKind = classKind;
+        }
 
         public final List<AnnotationData> annotations = Lists.newArrayList();
 
         void write(PrintWriter writer) {
-            if (!isValid() || annotations.isEmpty()) {
+            if (annotations.isEmpty()) {
                 return;
             }
             writer.print("  <item name=\"");
@@ -1611,10 +1795,9 @@
             writer.println();
         }
 
-        abstract boolean isValid();
-
         abstract boolean isFiltered(@NonNull ApiDatabase database);
 
+        @NonNull
         abstract String getSignature();
 
         @Override
@@ -1631,41 +1814,52 @@
 
             return signature1.compareTo(signature2);
         }
+
+        @NonNull
+        public abstract String getKeepRule();
+
+        @NonNull
+        public abstract String getQualifiedClassName();
     }
 
     private static class ClassItem extends Item {
-
-        @NonNull
-        public final String className;
-
-        private ClassItem(@NonNull String containingClass) {
-            this.className = containingClass;
+        private ClassItem(@NonNull String containingClass, @NonNull ClassKind classKind) {
+            super(containingClass, classKind);
         }
 
         @NonNull
-        static ClassItem create(@NonNull String classFqn) {
+        static ClassItem create(@NonNull String classFqn, @NonNull ClassKind kind) {
             classFqn = ApiDatabase.getRawClass(classFqn);
-            return new ClassItem(classFqn);
-        }
-
-        @Override
-        boolean isValid() {
-            return true;
+            return new ClassItem(classFqn, kind);
         }
 
         @Override
         boolean isFiltered(@NonNull ApiDatabase database) {
-            return !database.hasClass(className);
+            return !database.hasClass(containingClass);
         }
 
+        @NonNull
         @Override
         String getSignature() {
-            return escapeXml(className);
+            return escapeXml(containingClass);
+        }
+
+        @NonNull
+        @Override
+        public String getKeepRule() {
+            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+            return "-keep " + classKind.getKeepType() + " " + containingClass + "\n";
+        }
+
+        @NonNull
+        @Override
+        public String getQualifiedClassName() {
+            return containingClass;
         }
 
         @Override
         public String toString() {
-            return "Class " + className;
+            return "Class " + containingClass;
         }
 
         @Override
@@ -1679,12 +1873,12 @@
 
             ClassItem that = (ClassItem) o;
 
-            return className.equals(that.className);
+            return containingClass.equals(that.containingClass);
         }
 
         @Override
         public int hashCode() {
-            return className.hashCode();
+            return containingClass.hashCode();
         }
     }
 
@@ -1693,23 +1887,30 @@
         @NonNull
         public final String fieldName;
 
-        @NonNull
-        public final String containingClass;
+        @Nullable
+        public final String fieldType;
 
-        private FieldItem(@NonNull String containingClass, @NonNull String fieldName) {
-            this.containingClass = containingClass;
+        private FieldItem(@NonNull String containingClass, @NonNull ClassKind classKind,
+                @NonNull String fieldName, @Nullable String fieldType) {
+            super(containingClass, classKind);
             this.fieldName = fieldName;
+            this.fieldType = fieldType;
         }
 
         @Nullable
-        static FieldItem create(String classFqn, FieldBinding field) {
+        static FieldItem create(String classFqn, @NonNull ClassKind classKind, FieldBinding field) {
             String name = new String(field.name);
-            return classFqn != null ? new FieldItem(classFqn, name) : null;
+            String type = getFieldType(field);
+            return classFqn != null ? new FieldItem(classFqn, classKind, name, type) : null;
         }
 
-        @Override
-        boolean isValid() {
-            return true;
+        @Nullable
+        private static String getFieldType(FieldBinding binding) {
+            if (binding.type != null) {
+                return new String(binding.type.readableName());
+            }
+
+            return null;
         }
 
         @Override
@@ -1717,11 +1918,29 @@
             return !database.hasField(containingClass, fieldName);
         }
 
+        @NonNull
         @Override
         String getSignature() {
             return escapeXml(containingClass) + ' ' + fieldName;
         }
 
+        @NonNull
+        @Override
+        public String getKeepRule() {
+            if (fieldType == null) {
+                return ""; // imported item; these can't have keep rules
+            }
+            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+            return "-keep " + classKind.getKeepType() + " " + containingClass +
+                    " {\n    " + fieldType + " " + fieldName + "\n}\n";
+        }
+
+        @NonNull
+        @Override
+        public String getQualifiedClassName() {
+            return containingClass;
+        }
+
         @Override
         public String toString() {
             return "Field " + containingClass + "#" + fieldName;
@@ -1756,9 +1975,6 @@
         public final String methodName;
 
         @NonNull
-        public final String containingClass;
-
-        @NonNull
         public final String parameterList;
 
         @Nullable
@@ -1766,9 +1982,14 @@
 
         public final boolean isConstructor;
 
-        private MethodItem(@NonNull String containingClass, @Nullable String returnType,
-                @NonNull String methodName, @NonNull String parameterList, boolean isConstructor) {
-            this.containingClass = containingClass;
+        private MethodItem(
+                @NonNull String containingClass,
+                @NonNull ClassKind classKind,
+                @Nullable String returnType,
+                @NonNull String methodName,
+                @NonNull String parameterList,
+                boolean isConstructor) {
+            super(containingClass, classKind);
             this.returnType = returnType;
             this.methodName = methodName;
             this.parameterList = parameterList;
@@ -1782,6 +2003,7 @@
 
         @Nullable
         static MethodItem create(@Nullable String classFqn,
+                @NonNull ClassKind classKind,
                 @NonNull AbstractMethodDeclaration declaration,
                 @Nullable MethodBinding binding) {
             if (classFqn == null || binding == null) {
@@ -1801,16 +2023,12 @@
                 classFqn = ApiDatabase.getRawClass(classFqn);
                 methodName = ApiDatabase.getRawMethod(methodName);
             }
-            return new MethodItem(classFqn, returnType,
+            return new MethodItem(classFqn, classKind, returnType,
                     methodName, parameterList,
                     binding.isConstructor());
         }
 
-        @Override
-        boolean isValid() {
-            return true;
-        }
-
+        @NonNull
         @Override
         String getSignature() {
             StringBuilder sb = new StringBuilder(100);
@@ -1893,6 +2111,38 @@
         public String toString() {
             return "Method " + containingClass + "#" + methodName;
         }
+
+        @NonNull
+        @Override
+        public String getKeepRule() {
+            // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+            StringBuilder sb = new StringBuilder();
+            sb.append("-keep ");
+            sb.append(classKind.getKeepType());
+            sb.append(" ");
+            sb.append(containingClass);
+            sb.append(" {\n");
+            sb.append("    ");
+            if (isConstructor) {
+                sb.append("<init>");
+            } else {
+                sb.append(returnType);
+                sb.append(" ");
+                sb.append(methodName);
+            }
+            sb.append("(");
+            sb.append(parameterList); // TODO: Strip generics?
+            sb.append(")\n");
+            sb.append("}\n");
+
+            return sb.toString();
+        }
+
+        @NonNull
+        @Override
+        public String getQualifiedClassName() {
+            return containingClass;
+        }
     }
 
     @Nullable
@@ -1948,18 +2198,27 @@
 
     private static class ParameterItem extends MethodItem {
         @NonNull
-        public String argIndex;
+        public final String argIndex;
 
-        private ParameterItem(@NonNull String containingClass, @Nullable String returnType,
-                @NonNull String methodName, @NonNull String parameterList, boolean isConstructor,
+        private ParameterItem(
+                @NonNull String containingClass,
+                @NonNull ClassKind classKind,
+                @Nullable String returnType,
+                @NonNull String methodName,
+                @NonNull String parameterList,
+                boolean isConstructor,
                 @NonNull String argIndex) {
-            super(containingClass, returnType, methodName, parameterList, isConstructor);
+            super(containingClass, classKind, returnType, methodName, parameterList, isConstructor);
             this.argIndex = argIndex;
         }
 
         @Nullable
-        static ParameterItem create(AbstractMethodDeclaration methodDeclaration, Argument argument,
-                String classFqn, MethodBinding methodBinding,
+        static ParameterItem create(
+                AbstractMethodDeclaration methodDeclaration,
+                Argument argument,
+                String classFqn,
+                ClassKind classKind,
+                MethodBinding methodBinding,
                 LocalVariableBinding parameterBinding) {
             if (classFqn == null || methodBinding == null || parameterBinding == null) {
                 return null;
@@ -1996,11 +2255,12 @@
                 classFqn = ApiDatabase.getRawClass(classFqn);
                 methodName = ApiDatabase.getRawMethod(methodName);
             }
-            return new ParameterItem(classFqn, returnType, methodName, parameterList,
+            return new ParameterItem(classFqn, classKind, returnType, methodName, parameterList,
                     methodBinding.isConstructor(), argNum);
         }
 
 
+        @NonNull
         @Override
         String getSignature() {
             return super.getSignature() + ' ' + argIndex;
@@ -2035,9 +2295,15 @@
         public String toString() {
             return "Parameter #" + argIndex + " in " + super.toString();
         }
+
+        @NonNull
+        @Override
+        public String getKeepRule() {
+            return "";
+        }
     }
 
-    class AnnotationVisitor extends ASTVisitor {
+    private class AnnotationVisitor extends ASTVisitor {
         @Override
         public boolean visit(Argument argument, BlockScope scope) {
             Annotation[] annotations = argument.annotations;
@@ -2045,9 +2311,14 @@
                 ReferenceContext referenceContext = scope.referenceContext();
                 if (referenceContext instanceof AbstractMethodDeclaration) {
                     MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
-                    String fqn = getFqn(scope);
+                    ClassScope classScope = findClassScope(scope);
+                    if (classScope == null) {
+                        return false;
+                    }
+                    String fqn = getFqn(classScope);
+                    ClassKind kind = ClassKind.forType(classScope.referenceContext);
                     Item item = ParameterItem.create(
-                            (AbstractMethodDeclaration) referenceContext, argument, fqn,
+                            (AbstractMethodDeclaration) referenceContext, argument, fqn, kind,
                             binding, argument.binding);
                     if (item != null) {
                         addItem(fqn, item);
@@ -2068,7 +2339,8 @@
                 }
 
                 String fqn = getFqn(scope);
-                Item item = MethodItem.create(fqn, constructorDeclaration, constructorBinding);
+                ClassKind kind = ClassKind.forType(scope.referenceContext);
+                Item item = MethodItem.create(fqn, kind, constructorDeclaration, constructorBinding);
                 if (item != null) {
                     addItem(fqn, item);
                     addAnnotations(annotations, item);
@@ -2094,8 +2366,11 @@
                 }
 
                 String fqn = getFqn(scope);
-                Item item = FieldItem.create(fqn, fieldBinding);
-                if (item != null) {
+                ClassKind kind = scope.referenceContext instanceof TypeDeclaration ?
+                        ClassKind.forType((TypeDeclaration)scope.referenceContext) :
+                        ClassKind.CLASS;
+                Item item = FieldItem.create(fqn, kind, fieldBinding);
+                if (item != null && fqn != null) {
                     addItem(fqn, item);
                     addAnnotations(annotations, item);
                 }
@@ -2111,9 +2386,9 @@
                 if (methodBinding == null) {
                     return false;
                 }
-
                 String fqn = getFqn(scope);
-                MethodItem item = MethodItem.create(fqn, methodDeclaration,
+                ClassKind kind = ClassKind.forType(scope.referenceContext);
+                MethodItem item = MethodItem.create(fqn, kind, methodDeclaration,
                         methodDeclaration.binding);
                 if (item != null) {
                     addItem(fqn, item);
@@ -2161,7 +2436,7 @@
                 if (fqn == null) {
                     fqn = new String(localTypeDeclaration.binding.readableName());
                 }
-                Item item = ClassItem.create(fqn);
+                Item item = ClassItem.create(fqn, ClassKind.forType(localTypeDeclaration));
                 addItem(fqn, item);
                 addAnnotations(annotations, item);
 
@@ -2174,7 +2449,7 @@
             Annotation[] annotations = memberTypeDeclaration.annotations;
             if (hasRelevantAnnotations(annotations)) {
                 SourceTypeBinding binding = memberTypeDeclaration.binding;
-                if (binding == null || !(binding instanceof MemberTypeBinding)) {
+                if (!(binding instanceof MemberTypeBinding)) {
                     return true;
                 }
                 if (binding.isAnnotationType() || binding.isAnonymousType()) {
@@ -2182,7 +2457,7 @@
                 }
 
                 String fqn = new String(memberTypeDeclaration.binding.readableName());
-                Item item = ClassItem.create(fqn);
+                Item item = ClassItem.create(fqn, ClassKind.forType(memberTypeDeclaration));
                 addItem(fqn, item);
                 addAnnotations(annotations, item);
             }
@@ -2198,7 +2473,7 @@
                     return true;
                 }
                 String fqn = new String(typeDeclaration.binding.readableName());
-                Item item = ClassItem.create(fqn);
+                Item item = ClassItem.create(fqn, ClassKind.forType(typeDeclaration));
                 addItem(fqn, item);
                 addAnnotations(annotations, item);
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
index 51e519c..5924f4c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
@@ -16,10 +16,6 @@
 
 package com.android.build.gradle.tasks.annotations;
 
-import static com.android.SdkConstants.DOT_CLASS;
-import static com.android.SdkConstants.INT_DEF_ANNOTATION;
-import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
-
 import com.android.annotations.NonNull;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -35,18 +31,19 @@
 import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
 /** Gathers information about typedefs (@IntDef and @StringDef */
 public class TypedefCollector extends ASTVisitor {
-    private Map<String,Annotation> mMap = Maps.newHashMap();
+    private Map<String,List<Annotation>> mMap = Maps.newHashMap();
 
-    private boolean mRequireHide;
-    private boolean mRequireSourceRetention;
+    private final boolean mRequireHide;
+    private final boolean mRequireSourceRetention;
     private CompilationUnitDeclaration mCurrentUnit;
-    private List<File> mClassFiles = Lists.newArrayList();
+    private List<String> mTypedefClasses = Lists.newArrayList();
 
     public TypedefCollector(
             @NonNull Collection<CompilationUnitDeclaration> units,
@@ -62,11 +59,11 @@
         }
     }
 
-    public List<File> getNonPublicTypedefClassFiles() {
-        return mClassFiles;
+    public List<String> getNonPublicTypedefClasses() {
+        return mTypedefClasses;
     }
 
-    public Map<String,Annotation> getTypedefs() {
+    public Map<String,List<Annotation>> getTypedefs() {
         return mMap;
     }
 
@@ -95,12 +92,16 @@
                         continue;
                     }
 
-                    if (typeName.equals(INT_DEF_ANNOTATION) ||
-                            typeName.equals(STRING_DEF_ANNOTATION) ||
-                            typeName.equals(Extractor.ANDROID_INT_DEF) ||
-                            typeName.equals(Extractor.ANDROID_STRING_DEF)) {
+                    if (Extractor.isNestedAnnotation(typeName)) {
                         String fqn = new String(binding.readableName());
-                        mMap.put(fqn, annotation);
+
+                        List<Annotation> list = mMap.get(fqn);
+                        if (list == null) {
+                            list = new ArrayList<Annotation>(2);
+                            mMap.put(fqn, list);
+                        }
+                        list.add(annotation);
+
                         if (mRequireHide) {
                             Javadoc javadoc = declaration.javadoc;
                             if (javadoc != null) {
@@ -125,7 +126,7 @@
                             StringBuilder sb = new StringBuilder(100);
                             for (char c : declaration.binding.qualifiedPackageName()) {
                                 if (c == '.') {
-                                    sb.append(File.separatorChar);
+                                    sb.append('/');
                                 } else {
                                     sb.append(c);
                                 }
@@ -138,9 +139,7 @@
                                     sb.append(c);
                                 }
                             }
-                            sb.append(DOT_CLASS);
-                            File file = new File(sb.toString());
-                            mClassFiles.add(file);
+                            mTypedefClasses.add(sb.toString());
                         }
                     }
                 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
new file mode 100644
index 0000000..758f125
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 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.annotations;
+
+import static com.android.SdkConstants.DOT_CLASS;
+import static org.objectweb.asm.Opcodes.ASM5;
+
+import com.android.annotations.NonNull;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Finds and deletes typedef annotation classes (and also warns if their
+ * retention is wrong, such that usages of the annotation embeds data
+ * into the .class file.)
+ * <p>
+ * (Based on the similar class in {@code development/tools/rmtypedefs/})
+ */
+@SuppressWarnings("SpellCheckingInspection")
+public class TypedefRemover {
+    private final Extractor mExtractor;
+    private final boolean mQuiet;
+    private final boolean mVerbose;
+    private final boolean mDryRun;
+
+    public TypedefRemover(
+            @NonNull Extractor extractor,
+            boolean quiet,
+            boolean verbose,
+            boolean dryRun) {
+        mExtractor = extractor;
+        mQuiet = quiet;
+        mVerbose = verbose;
+        mDryRun = dryRun;
+    }
+
+    private Set<String> mAnnotationNames = Sets.newHashSet();
+    private List<File> mAnnotationClassFiles = Lists.newArrayList();
+    private Set<File> mAnnotationOuterClassFiles = Sets.newHashSet();
+
+    public void remove(@NonNull File classDir, @NonNull List<String> owners) {
+        if (!mQuiet) {
+            mExtractor.info("Deleting @IntDef and @StringDef annotation class files");
+        }
+
+        // Record typedef annotation names and files
+        for (String owner : owners) {
+            File file = new File(classDir, owner.replace('/', File.separatorChar) + DOT_CLASS);
+            addTypeDef(owner, file);
+        }
+
+        // Rewrite the .class files for any classes that *contain* typedefs as innerclasses
+        rewriteOuterClasses();
+
+        // Removes the actual .class files for the typedef annotations
+        deleteAnnotationClasses();
+    }
+
+    /**
+     * Records the given class name (internal name) and class file path as corresponding to a
+     * typedef annotation
+     * */
+    private void addTypeDef(String name, File file) {
+        mAnnotationClassFiles.add(file);
+        mAnnotationNames.add(name);
+
+        String fileName = file.getName();
+        int index = fileName.lastIndexOf('$');
+        if (index != -1) {
+            File parentFile = file.getParentFile();
+            assert parentFile != null : file;
+            File container = new File(parentFile, fileName.substring(0, index) + ".class");
+            if (container.exists()) {
+                mAnnotationOuterClassFiles.add(container);
+            } else {
+                Extractor.error("Warning: Could not find outer class " + container
+                        + " for typedef " + file);
+            }
+        }
+    }
+
+    /**
+     * Rewrites the outer classes containing the typedefs such that they no longer refer to
+     * the (now removed) typedef annotation inner classes
+     */
+    private void rewriteOuterClasses() {
+        for (File file : mAnnotationOuterClassFiles) {
+            byte[] bytes;
+            try {
+                bytes = Files.toByteArray(file);
+            } catch (IOException e) {
+                Extractor.error("Could not read " + file + ": " + e.getLocalizedMessage());
+                continue;
+            }
+
+            ClassWriter classWriter = new ClassWriter(ASM5);
+            ClassVisitor classVisitor = new ClassVisitor(ASM5, classWriter) {
+                @Override
+                public void visitInnerClass(String name, String outerName, String innerName,
+                        int access) {
+                    if (!mAnnotationNames.contains(name)) {
+                        super.visitInnerClass(name, outerName, innerName, access);
+                    }
+                }
+            };
+            ClassReader reader = new ClassReader(bytes);
+            reader.accept(classVisitor, 0);
+            byte[] rewritten = classWriter.toByteArray();
+            try {
+                Files.write(rewritten, file);
+            } catch (IOException e) {
+                Extractor.error("Could not write " + file + ": " + e.getLocalizedMessage());
+                //noinspection UnnecessaryContinue
+                continue;
+            }
+        }
+    }
+
+    /**
+     * Performs the actual deletion (or display, if in dry-run mode) of the typedef annotation
+     * files
+     */
+    private void deleteAnnotationClasses() {
+        for (File mFile : mAnnotationClassFiles) {
+            if (mVerbose) {
+                if (mDryRun) {
+                    mExtractor.info("Would delete " + mFile);
+                } else {
+                    mExtractor.info("Deleting " + mFile);
+                }
+            }
+            if (!mDryRun) {
+                boolean deleted = mFile.delete();
+                if (!deleted) {
+                    Extractor.warning("Could not delete " + mFile);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/AbstractCompilesUtil.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/AbstractCompilesUtil.java
new file mode 100644
index 0000000..1009f97
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/AbstractCompilesUtil.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.factory;
+
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.api.tasks.compile.AbstractCompile;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Common code for configuring {@link AbstractCompile} instances.
+ */
+public class AbstractCompilesUtil {
+
+    /**
+     * Determines the java language level to use and sets it on the given task and
+     * {@link CompileOptions}. The latter is to propagate the information to Studio.
+     */
+    public static void configureLanguageLevel(
+            AbstractCompile compileTask,
+            final CompileOptions compileOptions,
+            String compileSdkVersion) {
+        final AndroidVersion hash = AndroidTargetHash.getVersionFromHash(compileSdkVersion);
+        Integer compileSdkLevel = (hash == null ? null : hash.getApiLevel());
+
+        JavaVersion javaVersionToUse;
+        if (compileSdkLevel == null || (0 <= compileSdkLevel && compileSdkLevel <= 20)) {
+            javaVersionToUse = JavaVersion.VERSION_1_6;
+        } else {
+            javaVersionToUse = JavaVersion.VERSION_1_7;
+        }
+
+        JavaVersion jdkVersion =
+                JavaVersion.toVersion(System.getProperty("java.specification.version"));
+        if (jdkVersion.compareTo(javaVersionToUse) < 0) {
+            compileTask.getLogger().warn(
+                    "Default language level for compileSdkVersion '{}' is " +
+                            "{}, but the JDK used is {}, so the JDK language level will be used.",
+                    compileSdkVersion,
+                    javaVersionToUse,
+                    jdkVersion);
+            javaVersionToUse = jdkVersion;
+        }
+
+        compileOptions.setDefaultJavaVersion(javaVersionToUse);
+
+        compileTask.setSourceCompatibility(compileOptions.getSourceCompatibility().toString());
+        compileTask.setTargetCompatibility(compileOptions.getTargetCompatibility().toString());
+    }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java
index 502937b..69c93ca 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java
@@ -2,27 +2,21 @@
 
 import static com.android.builder.core.VariantType.LIBRARY;
 import static com.android.builder.core.VariantType.UNIT_TEST;
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
 
 import com.android.build.gradle.internal.CompileOptions;
 import com.android.build.gradle.internal.scope.ConventionMappingHelper;
 import com.android.build.gradle.internal.scope.TaskConfigAction;
 import com.android.build.gradle.internal.scope.VariantScope;
 import com.android.build.gradle.internal.variant.BaseVariantData;
-import com.android.sdklib.AndroidTargetHash;
-import com.android.sdklib.AndroidVersion;
+import com.android.builder.dependency.LibraryDependency;
+import com.google.common.base.Joiner;
 
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.gradle.api.Action;
-import org.gradle.api.JavaVersion;
-import org.gradle.api.Task;
+import org.gradle.api.Project;
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.tasks.compile.AbstractCompile;
 import org.gradle.api.tasks.compile.JavaCompile;
 
 import java.io.File;
-
-import groovy.lang.Closure;
+import java.util.concurrent.Callable;
 
 /**
  * Configuration Action for a JavaCompile task.
@@ -37,7 +31,7 @@
 
     @Override
     public String getName() {
-        return scope.getTaskName("compile", "Java");
+        return scope.getTaskName("compile", "JavaWithJavac");
     }
 
     @Override
@@ -46,146 +40,64 @@
     }
 
     @Override
-    public void execute(final JavaCompile javaCompileTask) {
+    public void execute(final JavaCompile javacTask) {
         final BaseVariantData testedVariantData = scope.getTestedVariantData();
-        scope.getVariantData().javaCompileTask = javaCompileTask;
+        scope.getVariantData().javacTask = javacTask;
 
-        javaCompileTask.setSource(scope.getVariantData().getJavaSources());
+        javacTask.setSource(scope.getVariantData().getJavaSources());
 
-        ConventionMappingHelper
-                .map(javaCompileTask, "classpath", new Closure<FileCollection>(this, this) {
-                    public FileCollection doCall(Object it) {
-                        FileCollection classpath = scope.getJavaClasspath();
+        ConventionMappingHelper.map(javacTask, "classpath", new Callable<FileCollection>() {
+            @Override
+            public FileCollection call() {
+                FileCollection classpath = scope.getJavaClasspath();
+                Project project = scope.getGlobalScope().getProject();
 
-                        if (testedVariantData != null) {
-                            // For libraries, the classpath from androidBuilder includes the library output
-                            // (bundle/classes.jar) as a normal dependency. In unit tests we don't want to package
-                            // the jar at every run, so we use the *.class files instead.
-                            if (!testedVariantData.getType().equals(LIBRARY) || scope
-                                    .getVariantData().getType().equals(UNIT_TEST)) {
-                                classpath = classpath
-                                        .plus(testedVariantData.getScope().getJavaClasspath())
-                                        .plus(scope.getGlobalScope().getProject().files(
-                                                testedVariantData.getScope().getJavaOutputDir(),
-                                                testedVariantData.getScope().getJavaDependencyCache()));
-                            }
+                if (testedVariantData != null) {
+                    // For libraries, the classpath from androidBuilder includes the library
+                    // output (bundle/classes.jar) as a normal dependency. In unit tests we
+                    // don't want to package the jar at every run, so we use the *.class
+                    // files instead.
+                    if (!testedVariantData.getType().equals(LIBRARY)
+                            || scope.getVariantData().getType().equals(UNIT_TEST)) {
+                        classpath = classpath.plus(project.files(
+                                        testedVariantData.getScope().getJavaClasspath(),
+                                        testedVariantData.getScope().getJavaOutputDir(),
+                                        testedVariantData.getScope().getJavaDependencyCache()));
+                    }
 
-                            if (scope.getVariantData().getType().equals(UNIT_TEST)
-                                    && testedVariantData.getType().equals(LIBRARY)) {
-                                // The bundled classes.jar may exist, but it's probably old. Don't use it, we
-                                // already have the *.class files in the classpath.
-                                classpath = classpath.minus(scope.getGlobalScope().getProject()
-                                        .files(testedVariantData.getVariantConfiguration()
-                                                .getOutput().getJarFile()));
-                            }
-
+                    if (scope.getVariantData().getType().equals(UNIT_TEST)
+                            && testedVariantData.getType().equals(LIBRARY)) {
+                        // The bundled classes.jar may exist, but it's probably old. Don't
+                        // use it, we already have the *.class files in the classpath.
+                        LibraryDependency libraryDependency =
+                                testedVariantData.getVariantConfiguration().getOutput();
+                        if (libraryDependency != null) {
+                            File jarFile = libraryDependency.getJarFile();
+                            classpath = classpath.minus(project.files(jarFile));
                         }
-
-                        return classpath;
                     }
+                }
 
-                    public FileCollection doCall() {
-                        return doCall(null);
-                    }
-
-                });
-
-        ConventionMappingHelper
-                .map(javaCompileTask, "destinationDir", new Closure<File>(this, this) {
-                    public File doCall(Object it) {
-                        return scope.getJavaOutputDir();
-                    }
-
-                    public File doCall() {
-                        return doCall(null);
-                    }
-
-                });
-        ConventionMappingHelper
-                .map(javaCompileTask, "dependencyCacheDir", new Closure<File>(this, this) {
-                    public File doCall(Object it) {
-                        return scope.getJavaDependencyCache();
-                    }
-
-                    public File doCall() {
-                        return doCall(null);
-                    }
-
-                });
-
-        configureLanguageLevel(javaCompileTask);
-        javaCompileTask.getOptions().setEncoding(
-                scope.getGlobalScope().getExtension().getCompileOptions().getEncoding());
-
-        // setup the boot classpath just before the task actually runs since this will
-        // force the sdk to be parsed.
-        javaCompileTask.doFirst(new Closure<String>(this, this) {
-            public String doCall(Task it) {
-                return setBootClasspath(javaCompileTask.getOptions(), DefaultGroovyMethods
-                        .join(scope.getGlobalScope().getAndroidBuilder()
-                                .getBootClasspathAsStrings(), File.pathSeparator));
+                return classpath;
             }
-
-            public String doCall() {
-                return doCall(null);
-            }
-
         });
-    }
 
-    private void configureLanguageLevel(AbstractCompile compileTask) {
-        final CompileOptions compileOptions = scope.getGlobalScope().getExtension()
-                .getCompileOptions();
-        JavaVersion javaVersionToUse;
+        javacTask.setDestinationDir(scope.getJavaOutputDir());
 
-        final AndroidVersion hash = AndroidTargetHash
-                .getVersionFromHash(scope.getGlobalScope().getExtension().getCompileSdkVersion());
-        Integer compileSdkLevel = (hash == null ? null : hash.getApiLevel());
-        if (compileSdkLevel == null || (0 <= compileSdkLevel && compileSdkLevel <= 20)) {
-            javaVersionToUse = JavaVersion.VERSION_1_6;
-        } else {
-            javaVersionToUse = JavaVersion.VERSION_1_7;
-        }
+        javacTask.setDependencyCacheDir(scope.getJavaDependencyCache());
 
-        JavaVersion jdkVersion = JavaVersion
-                .toVersion(System.getProperty("java.specification.version"));
-        if (jdkVersion.compareTo(javaVersionToUse) < 0) {
-//            logger.info(
-//                    "Default language level for 'compileSdkVersion $compileSdkLevel' is " +
-//                            "$javaVersionToUse, but the JDK used is $jdkVersion, so the JDK " +
-//                            "language level will be used.")
-            javaVersionToUse = jdkVersion;
-        }
+        CompileOptions compileOptions = scope.getGlobalScope().getExtension().getCompileOptions();
 
-        compileOptions.setDefaultJavaVersion(javaVersionToUse);
+        AbstractCompilesUtil.configureLanguageLevel(
+                javacTask,
+                compileOptions,
+                scope.getGlobalScope().getExtension().getCompileSdkVersion()
+        );
 
-        ConventionMappingHelper
-                .map(compileTask, "sourceCompatibility", new Closure<String>(this, this) {
-                    public String doCall(Object it) {
-                        return compileOptions.getSourceCompatibility().toString();
-                    }
+        javacTask.getOptions().setEncoding(compileOptions.getEncoding());
 
-                    public String doCall() {
-                        return doCall(null);
-                    }
-
-                });
-        ConventionMappingHelper
-                .map(compileTask, "targetCompatibility", new Closure<String>(this, this) {
-                    public String doCall(Object it) {
-                        return compileOptions.getTargetCompatibility().toString();
-                    }
-
-                    public String doCall() {
-                        return doCall(null);
-                    }
-
-                });
-    }
-
-    private static <Value extends String> Value setBootClasspath(
-            org.gradle.api.tasks.compile.CompileOptions propOwner, Value bootClasspath) {
-        propOwner.setBootClasspath(bootClasspath);
-        return bootClasspath;
+        javacTask.getOptions().setBootClasspath(
+                Joiner.on(File.pathSeparator).join(
+                        scope.getGlobalScope().getAndroidBuilder().getBootClasspathAsStrings()));
     }
 }
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java
index bb366d8..10a13b9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java
@@ -2,19 +2,16 @@
 
 import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
 
-import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.PostCompilationData;
 import com.android.build.gradle.internal.scope.TaskConfigAction;
 import com.android.build.gradle.internal.scope.VariantScope;
-import com.android.utils.StringHelper;
 import com.google.common.base.Preconditions;
 
-import org.codehaus.groovy.runtime.StringGroovyMethods;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
+import java.util.concurrent.Callable;
 
-import groovy.lang.Closure;
 import proguard.ParseException;
 import proguard.gradle.ProGuardTask;
 
@@ -25,11 +22,11 @@
 
     private VariantScope scope;
 
-    private Closure<List<File>> inputFiles;
+    private Callable<List<File>> inputFiles;
 
-    public ProGuardTaskConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+    public ProGuardTaskConfigAction(VariantScope scope, PostCompilationData pcData) {
         this.scope = scope;
-        this.inputFiles = pcData.getInputFiles();
+        this.inputFiles = pcData.getInputFilesCallable();
     }
 
     @Override
@@ -53,8 +50,9 @@
         try {
             proguardComponentsTask.configuration(scope.getManifestKeepListFile());
 
-            proguardComponentsTask.libraryjars(new Closure<File>(this, this) {
-                public File doCall(Object it) {
+            proguardComponentsTask.libraryjars(new Callable<File>() {
+                @Override
+                public File call() throws Exception {
                     Preconditions.checkNotNull(
                             scope.getGlobalScope().getAndroidBuilder().getTargetInfo());
                     File shrinkedAndroid = new File(
@@ -74,14 +72,14 @@
 
                     return shrinkedAndroid;
                 }
-
-                public File doCall() {
-                    return doCall(null);
-                }
-
             });
 
-            proguardComponentsTask.injars(inputFiles.call().iterator().next());
+            proguardComponentsTask.injars(new Callable<File>() {
+                @Override
+                public File call() throws Exception {
+                    return inputFiles.call().iterator().next();
+                }
+            });
 
             proguardComponentsTask.outjars(scope.getProguardComponentsJarFile());
 
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy
index d6cbc0f..a3d5ae2 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy
@@ -20,19 +20,15 @@
 import com.android.build.gradle.internal.scope.ConventionMappingHelper
 import com.android.build.gradle.internal.scope.TaskConfigAction
 import com.android.build.gradle.internal.scope.VariantScope
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.VariantType
-import com.android.builder.model.AndroidProject
 import com.android.builder.model.SourceProvider
-import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Sync
 
 import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
 
 /**
  * Configuration Action for a ProcessJavaRes task.
  */
-class ProcessJavaResConfigAction implements TaskConfigAction<Copy> {
+class ProcessJavaResConfigAction implements TaskConfigAction<Sync> {
 
     VariantScope scope;
 
@@ -46,12 +42,12 @@
     }
 
     @Override
-    Class<Copy> getType() {
-        return Copy.class
+    Class<Sync> getType() {
+        return Sync.class
     }
 
     @Override
-    void execute(Copy processResources) {
+    void execute(Sync processResources) {
         scope.variantData.processJavaResourcesTask = processResources
 
         // set the input
@@ -71,7 +67,7 @@
         }
 
         ConventionMappingHelper.map(processResources, "destinationDir") {
-            scope.getJavaResourcesDestinationDir()
+            new File(scope.getSourceFoldersJavaResDestinationDir(), "src");
         }
 
     }
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/CompileOptionsTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/CompileOptionsTest.java
new file mode 100644
index 0000000..37e7a86
--- /dev/null
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/CompileOptionsTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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 static org.junit.Assert.assertEquals;
+
+import org.gradle.api.JavaVersion;
+import org.junit.Test;
+
+/**
+ * Tests for {@link CompileOptions}
+ */
+public class CompileOptionsTest {
+
+    @Test
+    public void sourceCompatibilityTest() {
+        CompileOptions options = new CompileOptions();
+
+        assertEquals(options.getDefaultJavaVersion(), options.getSourceCompatibility());
+
+        options.setSourceCompatibility("1.6");
+        assertEquals(JavaVersion.VERSION_1_6, options.getSourceCompatibility());
+
+        options.setSourceCompatibility(1.6);
+        assertEquals(JavaVersion.VERSION_1_6, options.getSourceCompatibility());
+
+        options.setSourceCompatibility(JavaVersion.VERSION_1_7);
+        assertEquals(JavaVersion.VERSION_1_7, options.getSourceCompatibility());
+
+        options.setSourceCompatibility("Version_1_7");
+        assertEquals(JavaVersion.VERSION_1_7, options.getSourceCompatibility());
+
+        options.setSourceCompatibility("VERSION_1_7");
+        assertEquals(JavaVersion.VERSION_1_7, options.getSourceCompatibility());
+    }
+
+    @Test
+    public void targetCompatibilityTest() {
+        CompileOptions options = new CompileOptions();
+
+        assertEquals(options.getDefaultJavaVersion(), options.getTargetCompatibility());
+
+        options.setTargetCompatibility("1.6");
+        assertEquals(JavaVersion.VERSION_1_6, options.getTargetCompatibility());
+
+        options.setTargetCompatibility(1.6);
+        assertEquals(JavaVersion.VERSION_1_6, options.getTargetCompatibility());
+
+        options.setTargetCompatibility(JavaVersion.VERSION_1_7);
+        assertEquals(JavaVersion.VERSION_1_7, options.getTargetCompatibility());
+
+        options.setTargetCompatibility("Version_1_7");
+        assertEquals(JavaVersion.VERSION_1_7, options.getTargetCompatibility());
+
+        options.setTargetCompatibility("VERSION_1_7");
+        assertEquals(JavaVersion.VERSION_1_7, options.getTargetCompatibility());
+    }
+}
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java
index f125bce..297779d 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java
@@ -20,81 +20,86 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.builder.model.DimensionAware;
 import com.google.common.collect.ImmutableList;
 
-import org.gradle.api.Project;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.testfixtures.ProjectBuilder;
+import org.gradle.api.Named;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Collections;
 import java.util.List;
 
 public class ProductFlavorComboTest {
-    private Instantiator instantiator = new DirectInstantiator();
-    private Project project = ProjectBuilder.builder().build();
-    private Logger logger = Logging.getLogger(this.getClass());
 
     private static final String DIMENSION1 = "dimension1";
     private static final String DIMENSION2 = "dimension2";
 
-    private GroupableProductFlavor f1;
-    private GroupableProductFlavor f2;
-    private GroupableProductFlavor f1d1;
-    private GroupableProductFlavor f2d1;
-    private GroupableProductFlavor f1d2;
-    private GroupableProductFlavor f2d2;
+    @Mock
+    private CoreProductFlavor f1;
+    @Mock
+    private CoreProductFlavor f2;
+    @Mock
+    private CoreProductFlavor f1d1;
+    @Mock
+    private CoreProductFlavor f2d1;
+    @Mock
+    private CoreProductFlavor f1d2;
+    @Mock
+    private CoreProductFlavor f2d2;
 
     @Before
     public void setup() {
-        f1 = new GroupableProductFlavor("flavor1", project, instantiator, logger);
-        f2 = new GroupableProductFlavor("flavor2", project, instantiator, logger);
+        MockitoAnnotations.initMocks(this);
+        Mockito.when(f1.getName()).thenReturn("flavor1");
+        Mockito.when(f1.getDimension()).thenReturn(null);
+        Mockito.when(f2.getName()).thenReturn("flavor2");
+        Mockito.when(f2.getDimension()).thenReturn(null);
 
-        f1d1 = new GroupableProductFlavor("flavor1", project, instantiator, logger);
-        f1d1.setFlavorDimension(DIMENSION1);
-        f2d1 = new GroupableProductFlavor("flavor2", project, instantiator, logger);
-        f2d1.setFlavorDimension(DIMENSION1);
+        Mockito.when(f1d1.getName()).thenReturn("flavor1");
+        Mockito.when(f1d1.getDimension()).thenReturn(DIMENSION1);
+        Mockito.when(f2d1.getName()).thenReturn("flavor2");
+        Mockito.when(f2d1.getDimension()).thenReturn(DIMENSION1);
 
-        f1d2 = new GroupableProductFlavor("flavor1", project, instantiator, logger);
-        f1d2.setFlavorDimension(DIMENSION2);
-        f2d2 = new GroupableProductFlavor("flavor2", project, instantiator, logger);
-        f2d2.setFlavorDimension(DIMENSION2);
+        Mockito.when(f1d2.getName()).thenReturn("flavor1");
+        Mockito.when(f1d2.getDimension()).thenReturn(DIMENSION2);
+        Mockito.when(f2d2.getName()).thenReturn("flavor2");
+        Mockito.when(f2d2.getDimension()).thenReturn(DIMENSION2);
     }
 
     @Test
     public void getNameEmpty() {
-        assertEquals("", new ProductFlavorCombo().getName());
+        assertEquals("", new ProductFlavorCombo<CoreProductFlavor>().getName());
     }
 
     @Test
     public void getNameSingleFlavor() {
-        assertEquals("flavor1", new ProductFlavorCombo(f1).getName());
+        assertEquals("flavor1", new ProductFlavorCombo<CoreProductFlavor>(f1).getName());
     }
 
     @Test
     public void getNameMultiFlavor() {
-        assertEquals("flavor1Flavor2", new ProductFlavorCombo(f1, f2).getName());
+        assertEquals("flavor1Flavor2", new ProductFlavorCombo<CoreProductFlavor>(f1, f2).getName());
     }
 
     @Test
     public void createGroupListEmpty() throws Exception {
         assertEqualGroupList(
-                ImmutableList.<ProductFlavorCombo>of(),
+                ImmutableList.<ProductFlavorCombo<CoreProductFlavor>>of(),
                 ProductFlavorCombo.createCombinations(
                         Collections.<String>emptyList(),
-                        Collections.<GroupableProductFlavor>emptyList()));
+                        Collections.<CoreProductFlavor>emptyList()));
     }
 
     @Test
     public void createGroupListNullDimension() throws Exception {
         assertEqualGroupList(
                 ImmutableList.of(
-                        new ProductFlavorCombo(f1)
+                        new ProductFlavorCombo<CoreProductFlavor>(f1)
                 ),
                 ProductFlavorCombo.createCombinations(
                         null,
@@ -105,8 +110,8 @@
     public void createGroupListEmptyDimension() throws Exception {
         assertEqualGroupList(
                 ImmutableList.of(
-                        new ProductFlavorCombo(f1),
-                        new ProductFlavorCombo(f2)
+                        new ProductFlavorCombo<CoreProductFlavor>(f1),
+                        new ProductFlavorCombo<CoreProductFlavor>(f2)
                 ),
                 ProductFlavorCombo.createCombinations(
                         ImmutableList.<String>of(),
@@ -116,7 +121,7 @@
     @Test
     public void createGroupListSingleDimension() throws Exception {
         assertEqualGroupList(
-                ImmutableList.of(new ProductFlavorCombo(f1d1)),
+                ImmutableList.of(new ProductFlavorCombo<CoreProductFlavor>(f1d1)),
                 ProductFlavorCombo.createCombinations(
                         ImmutableList.of(DIMENSION1),
                         ImmutableList.of(f1d1)));
@@ -126,10 +131,10 @@
     public void createGroupListMultiDimensions() throws Exception {
         assertEqualGroupList(
                 ImmutableList.of(
-                        new ProductFlavorCombo(f1d1, f1d2),
-                        new ProductFlavorCombo(f1d1, f2d2),
-                        new ProductFlavorCombo(f2d1, f1d2),
-                        new ProductFlavorCombo(f2d1, f2d2)),
+                        new ProductFlavorCombo<CoreProductFlavor>(f1d1, f1d2),
+                        new ProductFlavorCombo<CoreProductFlavor>(f1d1, f2d2),
+                        new ProductFlavorCombo<CoreProductFlavor>(f2d1, f1d2),
+                        new ProductFlavorCombo<CoreProductFlavor>(f2d1, f2d2)),
                 ProductFlavorCombo.createCombinations(
                         ImmutableList.of(DIMENSION1, DIMENSION2),
                         ImmutableList.of(f1d1, f1d2, f2d1, f2d2)));
@@ -168,7 +173,9 @@
         }
     }
 
-    private static void assertEqualGroupList(List<ProductFlavorCombo> expected, List<ProductFlavorCombo> actual) {
+    private static <S extends DimensionAware & Named> void assertEqualGroupList(
+            List<ProductFlavorCombo<S>> expected,
+            List<ProductFlavorCombo<S>> actual) {
         assertEquals(expected.size(), actual.size());
         for (int i = 0; i < expected.size(); i++) {
             assertArrayEquals(
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java
index 00147a2..a9636d7 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java
@@ -20,9 +20,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.build.OutputFile;
-import com.android.resources.Density;
-
 import org.junit.Test;
 
 import java.util.Set;
@@ -35,29 +32,7 @@
 
         Set<String> values = options.getApplicableFilters();
 
-        assertEquals(1, values.size());
-        assertTrue(values.contains(OutputFile.NO_FILTER));
-    }
-
-    @Test
-    public void testNonUniversal() {
-        AbiSplitOptions options = new AbiSplitOptions();
-        options.setEnable(true);
-
-        Set<String> values = options.getApplicableFilters();
-
-        assertFalse(values.contains(OutputFile.NO_FILTER));
-    }
-
-    @Test
-    public void testUniversal() {
-        AbiSplitOptions options = new AbiSplitOptions();
-        options.setEnable(true);
-        options.setUniversalApk(true);
-
-        Set<String> values = options.getApplicableFilters();
-
-        assertTrue(values.contains(OutputFile.NO_FILTER));
+        assertEquals(0, values.size());
     }
 
     @Test
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java
index 25b921a..637d4e4 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.build.OutputFile;
 import com.android.resources.Density;
 
 import org.junit.Test;
@@ -35,8 +34,7 @@
 
         Set<String> values = options.getApplicableFilters();
 
-        assertEquals(1, values.size());
-        assertTrue(values.contains(OutputFile.NO_FILTER));
+        assertEquals(0, values.size());
     }
 
     @Test
@@ -45,9 +43,8 @@
         options.setEnable(true);
 
         Set<String> values = options.getApplicableFilters();
-
-        // test first release is the universal filter
-        assertEquals(OutputFile.NO_FILTER, values.iterator().next());
+        // at this time we have 6 densities, maybe more later.
+        assertTrue(values.size() >= 6);
     }
 
     @Test
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTaskTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTaskTest.java
new file mode 100644
index 0000000..712c949
--- /dev/null
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTaskTest.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2015 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.PackagingOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Files;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+/**
+ * Tests for {@link MergeJavaResourcesTaskTest}
+ */
+public class MergeJavaResourcesTaskTest {
+
+    @Mock
+    PackagingOptions packagingOptions;
+
+    @Mock
+    MergeJavaResourcesTask task;
+
+    MergeJavaResourcesTask.FileFilter fileFilter;
+
+    private static File sPackagedJarExpansionFolder, sMergedFolder;
+    private static File sExpandedJar1, sExpandedJar2, sExpandedJar3;
+
+    /**
+     * Create temporary folders to simulate expanded folders with java resources embedded
+     */
+    @BeforeClass
+    public static void prepareFolders() throws IOException {
+        sPackagedJarExpansionFolder = createTmpFolder(null /* parent */);
+
+        sExpandedJar1 = createTmpFolder(sPackagedJarExpansionFolder);
+        sExpandedJar2 = createTmpFolder(sPackagedJarExpansionFolder);
+        sExpandedJar3 = createTmpFolder(sPackagedJarExpansionFolder);
+
+        sMergedFolder = createTmpFolder(null /* parent */);
+    }
+
+    /**
+     * After each test, all folders are cleaned.
+     */
+    @After
+    public void cleanUp() {
+        cleanContents(sExpandedJar1);
+        cleanContents(sExpandedJar2);
+        cleanContents(sExpandedJar3);
+        cleanContents(sMergedFolder);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(task.getExpandedFolders()).thenReturn(ImmutableList.of(sPackagedJarExpansionFolder));
+        assertTrue(listFiles(sMergedFolder).length == 0);
+    }
+
+    @Test
+    public void testSimpleCopy() throws IOException {
+
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        assertFalse(new File(sMergedFolder, "foo/text.properties").exists());
+        File changedFile = createFile(sExpandedJar1, "foo/text.properties");
+        fileFilter.handleChanged(sMergedFolder, changedFile);
+        assertTrue(new File(sMergedFolder, "foo/text.properties").exists());
+    }
+
+    @Test
+    public void testSimpleExclusion() throws IOException {
+
+        when(packagingOptions.getExcludes()).thenReturn(
+                ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+        assertTrue(listFiles(sMergedFolder).length == 0);
+    }
+
+    @Test
+    public void testExclusionFromMultipleFiles() throws IOException {
+        when(packagingOptions.getExcludes()).thenReturn(
+                ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "foo/text.properties"));
+        assertTrue(listFiles(sMergedFolder).length == 0);
+    }
+
+    @Test
+    public void testMultipleExclusions() throws IOException {
+        when(packagingOptions.getExcludes()).thenReturn(
+                ImmutableSet.of("foo/text.properties", "bar/other.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "bar/other.properties"));
+        assertTrue(listFiles(sMergedFolder).length == 0);
+    }
+
+    @Test
+    public void textNonExclusion() throws IOException {
+        when(packagingOptions.getExcludes()).thenReturn(
+                ImmutableSet.of("foo/text.properties", "bar/other.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "bar/other.properties"));
+        // this one should be copied over.
+        fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "bar/foo.properties"));
+        assertTrue(listFiles(sMergedFolder).length == 1);
+        assertTrue(new File(sMergedFolder, "bar/foo.properties").exists());
+    }
+
+    @Test
+    public void testSingleMerge() throws IOException {
+        when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+
+        // one has changed...
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "two"));
+    }
+
+    @Test
+    public void testMultipleMerges() throws IOException {
+        when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+        createFile(sExpandedJar3, "foo/text.properties", "three");
+
+        // one has changed...
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "two", "three"));
+    }
+
+    @Test
+    public void testMergeAddon() throws IOException {
+        when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+
+        // simulate one has changed to create initial version
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "two"));
+
+        // add a new one.
+        File thirdFile = createFile(sExpandedJar3, "foo/text.properties", "three");
+        fileFilter.handleChanged(sMergedFolder, thirdFile);
+
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "two", "three"));
+    }
+
+    @Test
+    public void testMergeUpdate() throws IOException {
+        when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+        createFile(sExpandedJar3, "foo/text.properties", "three");
+
+        // simulate one has changed to create initial version
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "two", "three"));
+
+        // change one...
+        assertTrue(secondFile.delete());
+        secondFile = createFile(sExpandedJar2, "foo/text.properties", "deux");
+
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "deux", "three"));
+    }
+
+    @Test
+    public void testMergeRemoval() throws IOException {
+        when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+        createFile(sExpandedJar3, "foo/text.properties", "three");
+
+        // simulate one has changed to create initial version
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "two", "three"));
+
+        // remove one...
+        assertTrue(secondFile.delete());
+
+        fileFilter.handleRemoved(sMergedFolder, secondFile);
+
+        assertTrue(mergedFile.exists());
+        assertContentInAnyOrder(
+                Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+                , ImmutableList.of("one", "three"));
+    }
+
+    @Test
+    public void testPickFirst() throws IOException {
+        when(packagingOptions.getPickFirsts()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        // simulate the three elements were added.
+        fileFilter.handleChanged(sMergedFolder,
+                createFile(sExpandedJar1, "foo/text.properties", "one"));
+        fileFilter.handleChanged(sMergedFolder,
+                createFile(sExpandedJar2, "foo/text.properties", "two"));
+        fileFilter.handleChanged(sMergedFolder,
+                createFile(sExpandedJar3, "foo/text.properties", "three"));
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        String mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+        assertTrue(mergedContent.equals("one")
+                || mergedContent.equals("two")
+                || mergedContent.equals("three"));
+    }
+
+    @Test
+    public void testPickFirstUpdate() throws IOException {
+        when(packagingOptions.getPickFirsts()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        File firstFile = createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+        File thirdFile = createFile(sExpandedJar3, "foo/text.properties", "three");
+
+        // simulate the three elements were added.
+        fileFilter.handleChanged(sMergedFolder, firstFile);
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+        fileFilter.handleChanged(sMergedFolder, thirdFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        String mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+        if (mergedContent.equals("one")) {
+            assertTrue(firstFile.delete());
+            createFile(sExpandedJar1, "foo/text.properties", "un");
+            fileFilter.handleChanged(sMergedFolder, firstFile);
+            assertEquals("un", Files.asCharSource(mergedFile, Charset.defaultCharset()).read());
+        }
+        if (mergedContent.equals("two")) {
+            assertTrue(thirdFile.delete());
+            createFile(sExpandedJar2, "foo/text.properties", "deux");
+            fileFilter.handleChanged(sMergedFolder, secondFile);
+            assertEquals("deux", Files.asCharSource(mergedFile, Charset.defaultCharset()).read());
+        }
+        if (mergedContent.equals("three")) {
+            assertTrue(thirdFile.delete());
+            createFile(sExpandedJar3, "foo/text.properties", "trois");
+            fileFilter.handleChanged(sMergedFolder, thirdFile);
+            assertEquals("trois", Files.asCharSource(mergedFile, Charset.defaultCharset()).read());
+        }
+    }
+
+    @Test
+    public void testPickFirstRemoval() throws IOException {
+        when(packagingOptions.getPickFirsts()).thenReturn(ImmutableSet.of("foo/text.properties"));
+        fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+        File firstFile = createFile(sExpandedJar1, "foo/text.properties", "one");
+        File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+        File thirdFile = createFile(sExpandedJar3, "foo/text.properties", "three");
+
+        // simulate the three elements were added.
+        fileFilter.handleChanged(sMergedFolder, firstFile);
+        fileFilter.handleChanged(sMergedFolder, secondFile);
+        fileFilter.handleChanged(sMergedFolder, thirdFile);
+
+        File mergedFile = new File(sMergedFolder, "foo/text.properties");
+        assertTrue(mergedFile.exists());
+        String mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+        if (mergedContent.equals("one")) {
+            assertTrue(firstFile.delete());
+            fileFilter.handleRemoved(sMergedFolder, firstFile);
+            mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+            assertTrue(mergedContent.equals("two") || mergedContent.equals("three"));
+        }
+        if (mergedContent.equals("two")) {
+            assertTrue(thirdFile.delete());
+            fileFilter.handleRemoved(sMergedFolder, secondFile);
+            mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+            assertTrue(mergedContent.equals("one") || mergedContent.equals("three"));
+        }
+        if (mergedContent.equals("three")) {
+            assertTrue(thirdFile.delete());
+            fileFilter.handleRemoved(sMergedFolder, thirdFile);
+            mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+            assertTrue(mergedContent.equals("one") || mergedContent.equals("two"));
+        }
+    }
+
+    private static void assertContentInAnyOrder(String content, Iterable<String> subStrings) {
+        int length = 0;
+        for (String subString : subStrings) {
+            length += subString.length();
+            assertTrue(content.contains(subString));
+        }
+        assertEquals(length, content.length());
+    }
+
+    private static File createTmpFolder(@Nullable File parent) throws IOException {
+        File folder = File.createTempFile("tmp", "dir");
+        assertTrue(folder.delete());
+        if (parent != null) {
+            folder = new File(parent, folder.getName());
+        }
+        assertTrue(folder.mkdirs());
+        return folder;
+    }
+
+    @NonNull private static File[] listFiles(@NonNull File folder) {
+        File[] files = folder.listFiles();
+        if (files == null) {
+            return new File[0];
+        }
+        return files;
+    }
+
+    private static void cleanContents(File folder) {
+        for (File f : listFiles(folder)) {
+            if (f.isDirectory()) {
+                cleanContents(f);
+            }
+            assertTrue(f.delete());
+        }
+    }
+
+    @NonNull private static File createFile(
+            @NonNull File parent,
+            @NonNull String archivePath) throws IOException {
+        return createFile(parent, archivePath, null /* content */);
+    }
+
+    @NonNull private static File createFile(
+            @NonNull File parent,
+            @NonNull String archivePath,
+            @Nullable String content) throws IOException {
+
+        File newFile = new File(parent, archivePath);
+        if (!newFile.getParentFile().exists()) {
+            assertTrue(newFile.getParentFile().mkdirs());
+        }
+        String fileContent = content == null ? "test!" : content;
+        Files.append(fileContent, newFile, Charset.defaultCharset());
+        return newFile;
+    }
+}
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy
index 830bca1..b949f5c 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy
@@ -74,6 +74,12 @@
         }
 
         @Override
+        def <T> T record(@NonNull ExecutionType executionType, @NonNull Recorder.Block<T> block,
+                @NonNull List<Recorder.Property> properties) {
+            throw new UnsupportedOperationException("record method was not supposed to be called.")
+        }
+
+        @Override
         long allocationRecordId() {
             return recordId.incrementAndGet()
         }
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriverTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriverTest.java
new file mode 100644
index 0000000..f6340e3
--- /dev/null
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriverTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2015 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.annotations;
+
+import static com.android.utils.SdkUtils.fileToUrlString;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.testutils.SdkTestCase;
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+
+public class ExtractAnnotationsDriverTest extends SdkTestCase {
+    private static boolean isValidEcj() {
+        // When this test is run from within Gradle's testing infrastructure, e.g.
+        // via "./gradlew :base:gradle-core:test", Gradle's own *internal* dependencies
+        // somehow end up on the classpath, and in particular, an ancient version of
+        // (ECJ (3.x)) take priority over our explicit lint dependency which should
+        // bring in ECJ 4.
+        //
+        // This causes the test to fail. For now, make the test only run when a valid
+        // ECJ is present.
+        try {
+            CompilerOptions.class.getField("originalComplianceLevel");
+            return true;
+        } catch (Throwable t) {
+            return false;
+        }
+    }
+
+    public void testProGuard() throws Exception {
+        if (!isValidEcj()) {
+            return;
+        }
+
+        File androidJar = findAndroidJar(false);
+        if (androidJar == null) {
+            System.err.println("Skipping test " + this.getClass() + "#" + getName()
+                    + ": No android.jar found");
+            return;
+        }
+
+        File project = createProject(mKeepTest, mKeepAnnotation);
+
+        File output = File.createTempFile("proguard", ".cfg");
+        output.deleteOnExit();
+
+        List<String> list = java.util.Arrays.asList(
+                "--sources",
+                new File(project, "src").getPath(),
+                "--classpath",
+                androidJar.getPath(),
+
+                "--quiet",
+                "--language-level",
+                "1.6",
+                "--proguard",
+                output.getPath()
+        );
+        String[] args = list.toArray(new String[list.size()]);
+        assertNotNull(args);
+
+        new ExtractAnnotationsDriver().run(args);
+        assertEquals(""
+                        + "-keep class test.pkg.KeepTest {\n"
+                        + "    java.lang.Object myField\n"
+                        + "}\n"
+                        + "\n"
+                        + "-keep class test.pkg.KeepTest {\n"
+                        + "    void foo()\n"
+                        + "}\n"
+                        + "\n"
+                        + "-keep class test.pkg.KeepTest.MyClass\n"
+                        + "\n"
+                        + "-keep enum test.pkg.KeepTest.MyEnum\n"
+                        + "\n"
+                        + "-keep interface test.pkg.KeepTest.MyInterface\n"
+                        + "\n"
+                        + "-keep interface test.pkg.KeepTest.MyInterface2 {\n"
+                        + "    void paint2()\n"
+                        + "}\n"
+                        + "\n",
+                Files.toString(output, Charsets.UTF_8));
+        deleteFile(project);
+    }
+
+    public void testIncludeClassRetention() throws Exception {
+        if (!isValidEcj()) {
+            return;
+        }
+
+        File androidJar = findAndroidJar(false);
+        if (androidJar == null) {
+            System.err.println("Skipping test " + this.getClass() + "#" + getName()
+                    + ": No android.jar found");
+            return;
+        }
+
+        File project = createProject(
+                mIntDefTest,
+                mPermissionsTest,
+                mManifest,
+                mKeepAnnotation,
+                mIntDefAnnotation,
+                mIntRangeAnnotation,
+                mPermissionAnnotation);
+
+        File output = File.createTempFile("annotations", ".zip");
+        File proguard = File.createTempFile("proguard", ".cfg");
+        output.deleteOnExit();
+        proguard.deleteOnExit();
+
+        List<String> list = java.util.Arrays.asList(
+                "--sources",
+                new File(project, "src").getPath(),
+                "--classpath",
+                androidJar.getPath(),
+
+                "--quiet",
+                "--language-level",
+                "1.6",
+                "--output",
+                output.getPath(),
+                "--proguard",
+                proguard.getPath()
+        );
+        String[] args = list.toArray(new String[list.size()]);
+        assertNotNull(args);
+
+        new ExtractAnnotationsDriver().run(args);
+
+        // Check proguard rules
+        assertEquals(""
+                        + "-keep class test.pkg.IntDefTest {\n"
+                        + "    void testIntDef(int)\n"
+                        + "}\n"
+                        + "\n",
+                Files.toString(proguard, Charsets.UTF_8));
+
+        // Check extracted annotations
+        checkPackageXml("test.pkg", output, ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1\">\n"
+                + "    <annotation name=\"android.support.annotation.IntDef\">\n"
+                + "      <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+                + "      <val name=\"flag\" val=\"true\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.IntDefTest void setStyle(int, int) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.IntDef\">\n"
+                + "      <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+                + "    </annotation>\n"
+                + "    <annotation name=\"android.support.annotation.IntRange\">\n"
+                + "      <val name=\"from\" val=\"20\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.PermissionsTest CONTENT_URI\">\n"
+                + "    <annotation name=\"android.support.annotation.RequiresPermission.Read\">\n"
+                + "      <val name=\"value\" val=\"&quot;android.permission.MY_READ_PERMISSION_STRING&quot;\" />\n"
+                + "    </annotation>\n"
+                + "    <annotation name=\"android.support.annotation.RequiresPermission.Write\">\n"
+                + "      <val name=\"value\" val=\"&quot;android.permission.MY_WRITE_PERMISSION_STRING&quot;\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.PermissionsTest void myMethod()\">\n"
+                + "    <annotation name=\"android.support.annotation.RequiresPermission\">\n"
+                + "      <val name=\"value\" val=\"&quot;android.permission.MY_PERMISSION_STRING&quot;\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "</root>\n"
+                + "\n");
+
+        deleteFile(project);
+    }
+
+    public void testSkipClassRetention() throws Exception {
+        if (!isValidEcj()) {
+            return;
+        }
+
+        File androidJar = findAndroidJar(false);
+        if (androidJar == null) {
+            System.err.println("Skipping test " + this.getClass() + "#" + getName()
+                    + ": No android.jar found");
+            return;
+        }
+
+        File project = createProject(
+                mIntDefTest,
+                mPermissionsTest,
+                mManifest,
+                mKeepAnnotation,
+                mIntDefAnnotation,
+                mIntRangeAnnotation,
+                mPermissionAnnotation);
+
+        File output = File.createTempFile("annotations", ".zip");
+        File proguard = File.createTempFile("proguard", ".cfg");
+        output.deleteOnExit();
+        proguard.deleteOnExit();
+
+        List<String> list = java.util.Arrays.asList(
+                "--sources",
+                new File(project, "src").getPath(),
+                "--classpath",
+                androidJar.getPath(),
+
+                "--quiet",
+                "--skip-class-retention",
+                "--language-level",
+                "1.6",
+                "--output",
+                output.getPath(),
+                "--proguard",
+                proguard.getPath()
+        );
+        String[] args = list.toArray(new String[list.size()]);
+        assertNotNull(args);
+
+        new ExtractAnnotationsDriver().run(args);
+
+        // Check external annotations
+        checkPackageXml("test.pkg", output, ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1\">\n"
+                + "    <annotation name=\"android.support.annotation.IntDef\">\n"
+                + "      <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+                + "      <val name=\"flag\" val=\"true\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.IntDefTest void setStyle(int, int) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.IntDef\">\n"
+                + "      <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+                + "    </annotation>\n"
+                + "    <annotation name=\"android.support.annotation.IntRange\">\n"
+                + "      <val name=\"from\" val=\"20\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "</root>\n"
+                + "\n");
+
+
+        deleteFile(project);
+    }
+
+    @SuppressWarnings("SpellCheckingInspection")
+    @NonNull
+    private TestFile mksrc(@NonNull String to, @NonNull String source) {
+        return new TestFile().to(to).withSource(source);
+    }
+
+    private File createProject(TestFile... files) throws IOException {
+        File dir = Files.createTempDir();
+
+        for (TestFile fp : files) {
+            File file = fp.createFile(dir);
+            assertNotNull(file);
+        }
+
+        return dir;
+    }
+
+    private final TestFile mKeepAnnotation = mksrc("src/android/support/annotation/Keep.java", ""
+            + "package android.support.annotation;\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "import static java.lang.annotation.ElementType.*;\n"
+            + "import static java.lang.annotation.RetentionPolicy.*;\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({PACKAGE,TYPE,ANNOTATION_TYPE,CONSTRUCTOR,METHOD,FIELD})\n"
+            + "public @interface Keep {\n"
+            + "}\n");
+
+    private final TestFile mIntDefAnnotation = mksrc("src/android/support/annotation/IntDef.java", ""
+            + "package android.support.annotation;\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.RetentionPolicy;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "import static java.lang.annotation.ElementType.*;\n"
+            + "import static java.lang.annotation.RetentionPolicy.SOURCE;\n"
+            + "@Retention(SOURCE)\n"
+            + "@Target({ANNOTATION_TYPE})\n"
+            + "public @interface IntDef {\n"
+            + "    long[] value() default {};\n"
+            + "    boolean flag() default false;\n"
+            + "}\n");
+
+    private final TestFile mIntRangeAnnotation = mksrc("src/android/support/annotation/IntRange.java", ""
+            + "package android.support.annotation;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "\n"
+            + "import static java.lang.annotation.ElementType.*;\n"
+            + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+            + "\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({CONSTRUCTOR,METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})\n"
+            + "public @interface IntRange {\n"
+            + "    long from() default Long.MIN_VALUE;\n"
+            + "    long to() default Long.MAX_VALUE;\n"
+            + "}\n");
+
+    private final TestFile mPermissionAnnotation = mksrc(
+            "src/android/support/annotation/RequiresPermission.java", ""
+            + "package android.support.annotation;\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.RetentionPolicy;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "import static java.lang.annotation.ElementType.*;\n"
+            + "import static java.lang.annotation.RetentionPolicy.*;\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})\n"
+            + "public @interface RequiresPermission {\n"
+            + "    String value() default \"\";\n"
+            + "    String[] allOf() default {};\n"
+            + "    String[] anyOf() default {};\n"
+            + "    boolean conditional() default false;\n"
+            + "    @Target(FIELD)\n"
+            + "    @interface Read {\n"
+            + "        RequiresPermission value();\n"
+            + "    }\n"
+            + "    @Target(FIELD)\n"
+            + "    @interface Write {\n"
+            + "        RequiresPermission value();\n"
+            + "    }\n"
+            + "}");
+
+    private final TestFile mIntDefTest = mksrc("src/test/pkg/IntDefTest.java", ""
+            + "package test.pkg;\n"
+            + "\n"
+            + "import android.content.Context;\n"
+            + "import android.support.annotation.IntDef;\n"
+            + "import android.support.annotation.IntRange;\n"
+            + "import android.support.annotation.Keep;\n"
+            + "import android.view.View;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.RetentionPolicy;\n"
+            + "\n"
+            + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+            + "public class IntDefTest {\n"
+            + "    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+            + "    @IntRange(from = 20)\n"
+            + "    @Retention(RetentionPolicy.SOURCE)\n"
+            + "    private @interface DialogStyle {}\n"
+            + "\n"
+            + "    public static final int STYLE_NORMAL = 0;\n"
+            + "    public static final int STYLE_NO_TITLE = 1;\n"
+            + "    public static final int STYLE_NO_FRAME = 2;\n"
+            + "    public static final int STYLE_NO_INPUT = 3;\n"
+            + "    public static final int UNRELATED = 3;\n"
+            + "\n"
+            + "    public void setStyle(@DialogStyle int style, int theme) {\n"
+            + "    }\n"
+            + "\n"
+            + "    @Keep"
+            + "    public void testIntDef(int arg) {\n"
+            + "    }\n"
+            + "    @IntDef(value = {STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT}, flag=true)\n"
+            + "    @Retention(RetentionPolicy.SOURCE)\n"
+            + "    private @interface DialogFlags {}\n"
+            + "\n"
+            + "    public void setFlags(Object first, @DialogFlags int flags) {\n"
+            + "    }\n"
+            + "\n"
+            + "    public static final String TYPE_1 = \"type1\";\n"
+            + "    public static final String TYPE_2 = \"type2\";\n"
+            + "    public static final String UNRELATED_TYPE = \"other\";\n"
+            + "}");
+
+    private final TestFile mPermissionsTest = mksrc("src/test/pkg/PermissionsTest.java", ""
+            + "package test.pkg;\n"
+            + "\n"
+            + "import android.support.annotation.RequiresPermission;\n"
+            + "\n"
+            + "public class PermissionsTest {\n"
+            + "    @RequiresPermission(Manifest.permission.MY_PERMISSION)\n"
+            + "    public void myMethod() {\n"
+            + "    }\n"
+            + "\n"
+            + "    @RequiresPermission.Read(@RequiresPermission(Manifest.permission.MY_READ_PERMISSION))\n"
+            + "    @RequiresPermission.Write(@RequiresPermission(Manifest.permission.MY_WRITE_PERMISSION))\n"
+            + "    public static final String CONTENT_URI = \"\";\n"
+            + "}\n");
+
+    private final TestFile mManifest = mksrc("src/test/pkg/Manifest.java", ""
+            + "package test.pkg;\n"
+            + "\n"
+            + "public class Manifest {\n"
+            + "    public static final class permission {\n"
+            + "        public static final String MY_PERMISSION = \"android.permission.MY_PERMISSION_STRING\";\n"
+            + "        public static final String MY_READ_PERMISSION = \"android.permission.MY_READ_PERMISSION_STRING\";\n"
+            + "        public static final String MY_WRITE_PERMISSION = \"android.permission.MY_WRITE_PERMISSION_STRING\";\n"
+            + "    }\n"
+            + "}\n");
+
+    private final TestFile mKeepTest = mksrc("src/test/pkg/KeepTest.java", ""
+            + "package test.pkg;\n"
+            + "import android.support.annotation.Keep;\n"
+            + "\n"
+            + "public class KeepTest {\n"
+            + "    @Keep\n"
+            + "    public void foo() {\n"
+            + "    }\n"
+            + "\n"
+            + "    @Keep\n"
+            + "    public static class MyClass {\n"
+            + "        public void paint() {\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    @Keep\n"
+            + "    public static interface MyInterface {\n"
+            + "        public void paint();\n"
+            + "    }\n"
+            + "\n"
+            + "    public static interface MyInterface2 {\n"
+            + "        @Keep\n"
+            + "        public void paint2();\n"
+            + "    }\n"
+            + "\n"
+            + "    @Keep\n"
+            + "    public static enum MyEnum {\n"
+            + "        TYPE1, TYPE2;\n"
+            + "    }\n"
+            + "\n"
+            + "    @Keep\n"
+            + "    public static @interface MyAnnotation {\n"
+            + "    }\n"
+            + "\n"
+            + "    @Keep\n"
+            + "    public Object myField = null;"
+            + "}\n");
+
+    private static void checkPackageXml(String pkg, File output, String expected)
+            throws IOException {
+        assertNotNull(output);
+        assertTrue(output.exists());
+        URL url = new URL("jar:" + fileToUrlString(output) + "!/" + pkg.replace('.','/') +
+                "/annotations.xml");
+        InputStream stream = url.openStream();
+        try {
+            byte[] bytes = ByteStreams.toByteArray(stream);
+            assertNotNull(bytes);
+            String xml = new String(bytes, Charsets.UTF_8);
+            assertEquals(expected, xml);
+        } finally {
+            Closeables.closeQuietly(stream);
+        }
+    }
+
+    @Nullable
+    private static File findAndroidJar(boolean requireExists) {
+        String androidHomePath = System.getenv("ANDROID_HOME");
+        assertNotNull("Must set $ANDROID_HOME to run this test", androidHomePath);
+        File androidHome = new File(androidHomePath);
+        if (!androidHome.exists()) {
+            if (!requireExists) {
+                return null;
+            }
+            fail(androidHomePath + " does not exist");
+        }
+        File androidJar = new File(androidHome, "platforms/android-22/android.jar");
+        assertTrue(
+                androidJar + " does not exist: make sure you have Lollipop installed in your SDK",
+                androidJar.exists());
+        return androidJar;
+    }
+
+    public void testGetRaw() throws Exception {
+        assertEquals("", ApiDatabase.getRawClass(""));
+        assertEquals("Foo", ApiDatabase.getRawClass("Foo"));
+        assertEquals("Foo", ApiDatabase.getRawClass("Foo<T>"));
+        assertEquals("Foo", ApiDatabase.getRawMethod("Foo<T>"));
+        assertEquals("Foo", ApiDatabase.getRawClass("Foo<A,B>"));
+        assertEquals("Foo", ApiDatabase.getRawParameterList("Foo<? extends java.util.List>"));
+        assertEquals("Object,java.util.List,List,int[],Object[]",
+                ApiDatabase.getRawParameterList("Object<? extends java.util.List>,java.util.List<String>,"
+                        + "List<? super Number>,int[],Object..."));
+    }
+}
\ No newline at end of file
diff --git a/build-system/gradle-experimental/build.gradle b/build-system/gradle-experimental/build.gradle
index 7a31a62..9e9154a 100644
--- a/build-system/gradle-experimental/build.gradle
+++ b/build-system/gradle-experimental/build.gradle
@@ -1,20 +1,11 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'groovy'
 apply plugin: 'clone-artifacts'
 apply plugin: 'idea'
 apply plugin: 'jacoco'
 
 // Extract gradle libraries to ensure gradle-core is compatible with older version.
-String gradleVersion = "2.4-20150322230018+0000"
-File gradleBinary = file("$rootProject.projectDir/external/gradle/gradle-$gradleVersion-bin.zip")
+String gradleVersion = "2.5"
+File gradleBinary = file("$rootProject.projectDir/external/gradle/gradle-$gradleVersion-all.zip")
 File gradleLib = file("$rootProject.ext.androidHostOut/alternate-gradle/gradle-$gradleVersion/lib")
 
 task extractGradleLibs(type: Copy) {
@@ -38,12 +29,13 @@
 
 group = 'com.android.tools.build'
 archivesBaseName = 'gradle-experimental'
-version = rootProject.ext.buildVersion
+version = rootProject.ext.experimentalVersion
 
 project.ext.pomName = 'Gradle Plug-in for Android Using Component Model'
 project.ext.pomDesc = 'Gradle plug-in to build Android applications.'
 
 apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
 
 jar.manifest.attributes("Plugin-Version": version)
 jar.manifest.attributes("Inception-Date":"${Calendar.getInstance().get(Calendar.YEAR)}:" +
diff --git a/build-system/gradle-experimental/gradle-experimental.iml b/build-system/gradle-experimental/gradle-experimental.iml
index 2747ef5..9ab34f7 100644
--- a/build-system/gradle-experimental/gradle-experimental.iml
+++ b/build-system/gradle-experimental/gradle-experimental.iml
@@ -43,7 +43,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-core-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-core-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -52,7 +52,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-model-core-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-model-core-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -61,7 +61,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-model-groovy-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-model-groovy-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -70,7 +70,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/asm-all-5.0.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/asm-all-5.0.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -79,7 +79,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/ant-1.9.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/ant-1.9.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -88,7 +88,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-collections-3.2.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-collections-3.2.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -97,7 +97,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-io-1.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-io-1.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -106,7 +106,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-lang-2.6.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-lang-2.6.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -115,7 +115,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/logback-core-1.0.13.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/logback-core-1.0.13.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -124,7 +124,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/logback-classic-1.0.13.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/logback-classic-1.0.13.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -133,7 +133,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/guava-jdk5-17.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/guava-jdk5-17.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -142,7 +142,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jcip-annotations-1.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jcip-annotations-1.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -151,7 +151,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jul-to-slf4j-1.7.7.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jul-to-slf4j-1.7.7.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -160,7 +160,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jarjar-1.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jarjar-1.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -169,7 +169,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/javax.inject-1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/javax.inject-1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -178,7 +178,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/slf4j-api-1.7.7.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/slf4j-api-1.7.7.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -187,7 +187,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/log4j-over-slf4j-1.7.7.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/log4j-over-slf4j-1.7.7.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -196,7 +196,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jcl-over-slf4j-1.7.7.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jcl-over-slf4j-1.7.7.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -205,7 +205,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/ant-launcher-1.9.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/ant-launcher-1.9.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -214,7 +214,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-collections-3.2.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-collections-3.2.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -223,7 +223,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-io-1.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-io-1.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -232,7 +232,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-lang-2.6.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-lang-2.6.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -241,7 +241,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-docs-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-docs-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -250,7 +250,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-launcher-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-launcher-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -259,7 +259,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-base-services-groovy-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-base-services-groovy-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -268,7 +268,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-base-services-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-base-services-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -277,7 +277,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-resources-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-resources-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -286,7 +286,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-cli-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-cli-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -295,7 +295,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-native-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-native-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -304,7 +304,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-open-api-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-open-api-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -313,7 +313,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-ui-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-ui-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -322,7 +322,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jna-3.2.7.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jna-3.2.7.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -331,7 +331,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -340,7 +340,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jansi-1.2.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jansi-1.2.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -349,7 +349,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-osx-i386-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-osx-i386-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -358,7 +358,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-osx-amd64-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-osx-amd64-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -367,7 +367,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-linux-amd64-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-linux-amd64-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -376,7 +376,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-linux-i386-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-linux-i386-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -385,7 +385,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-windows-amd64-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-windows-amd64-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -394,7 +394,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-windows-i386-0.10.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-windows-i386-0.10.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -403,7 +403,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-messaging-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-messaging-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -412,7 +412,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/kryo-2.20.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/kryo-2.20.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -421,7 +421,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/reflectasm-1.07-shaded.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/reflectasm-1.07-shaded.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -430,7 +430,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/minlog-1.2.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/minlog-1.2.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -439,7 +439,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/objenesis-1.2.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/objenesis-1.2.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -448,7 +448,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-settings-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-settings-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -457,7 +457,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-repository-metadata-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-repository-metadata-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -466,7 +466,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-container-default-1.5.5.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-container-default-1.5.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -475,7 +475,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-aether-provider-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-aether-provider-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -484,7 +484,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-wagon-provider-api-2.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-wagon-provider-api-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -493,7 +493,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-cipher-1.7.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-cipher-1.7.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -502,7 +502,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-interpolation-1.14.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-interpolation-1.14.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -511,7 +511,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-utils-2.0.6.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-utils-2.0.6.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -520,7 +520,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-classworlds-2.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-classworlds-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -529,7 +529,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-plugin-api-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-plugin-api-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -538,7 +538,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-model-builder-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-model-builder-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -547,7 +547,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-sec-dispatcher-1.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-sec-dispatcher-1.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -556,7 +556,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-component-annotations-1.5.5.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-component-annotations-1.5.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -565,7 +565,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-connector-wagon-1.13.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-connector-wagon-1.13.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -574,7 +574,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-compat-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-compat-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -583,7 +583,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-wagon-http-2.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-wagon-http-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -592,7 +592,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-api-1.13.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-api-1.13.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -601,7 +601,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-settings-builder-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-settings-builder-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -610,7 +610,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-spi-1.13.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-spi-1.13.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -619,7 +619,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-core-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-core-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -628,7 +628,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-wagon-http-shared4-2.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-wagon-http-shared4-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -637,7 +637,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-util-1.13.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-util-1.13.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -646,7 +646,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-artifact-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-artifact-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -655,7 +655,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-model-3.0.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-model-3.0.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -664,7 +664,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-impl-1.13.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-impl-1.13.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -673,7 +673,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/maven-ant-tasks-2.1.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/maven-ant-tasks-2.1.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -682,7 +682,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/nekohtml-1.9.14.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/nekohtml-1.9.14.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -691,7 +691,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/xbean-reflect-3.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/xbean-reflect-3.4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -700,7 +700,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/xml-apis-1.3.04.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/xml-apis-1.3.04.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -709,7 +709,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/xercesImpl-2.9.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/xercesImpl-2.9.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -718,7 +718,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-tooling-api-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-tooling-api-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -727,7 +727,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-plugins-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-plugins-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -736,7 +736,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/junit-4.11.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/junit-4.11.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -745,7 +745,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/testng-6.3.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/testng-6.3.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -754,7 +754,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/commons-cli-1.2.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/commons-cli-1.2.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -763,7 +763,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/bsh-2.0b4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/bsh-2.0b4.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -772,7 +772,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/snakeyaml-1.6.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/snakeyaml-1.6.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -781,7 +781,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/hamcrest-core-1.3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/hamcrest-core-1.3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -790,7 +790,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-code-quality-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-code-quality-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -799,7 +799,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-jetty-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-jetty-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -808,7 +808,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-6.1.25.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-6.1.25.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -817,7 +817,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-util-6.1.25.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-util-6.1.25.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -826,7 +826,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/servlet-api-2.5-20081211.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/servlet-api-2.5-20081211.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -835,7 +835,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-plus-6.1.25.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-plus-6.1.25.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -844,7 +844,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jsp-2.1-6.1.14.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jsp-2.1-6.1.14.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -853,7 +853,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-annotations-6.1.25.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-annotations-6.1.25.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -862,7 +862,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/geronimo-annotation_1.0_spec-1.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/geronimo-annotation_1.0_spec-1.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -871,7 +871,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-naming-6.1.25.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-naming-6.1.25.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -880,7 +880,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/core-3.1.1.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/core-3.1.1.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -889,7 +889,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jsp-api-2.1-6.1.14.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jsp-api-2.1-6.1.14.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -898,7 +898,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-antlr-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-antlr-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -907,7 +907,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-dependency-management-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-dependency-management-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -916,7 +916,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ide-native-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ide-native-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -925,7 +925,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-groovy-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-groovy-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -934,7 +934,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-java-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-java-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -943,7 +943,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-jvm-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-jvm-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -952,7 +952,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-native-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-native-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -961,7 +961,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-platform-base-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-platform-base-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -970,7 +970,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-platform-jvm-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-platform-jvm-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -979,7 +979,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-platform-native-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-platform-native-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -988,7 +988,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-plugin-development-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-plugin-development-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -997,7 +997,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-plugin-use-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-plugin-use-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1006,7 +1006,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-wrapper-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-wrapper-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1015,7 +1015,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-osgi-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-osgi-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1024,7 +1024,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/bndlib-2.1.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/bndlib-2.1.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1033,7 +1033,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-maven-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-maven-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1042,7 +1042,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/pmaven-common-0.8-20100325.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/pmaven-common-0.8-20100325.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1051,7 +1051,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/pmaven-groovy-0.8-20100325.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/pmaven-groovy-0.8-20100325.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1060,7 +1060,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/plexus-component-annotations-1.5.2.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/plexus-component-annotations-1.5.2.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1069,7 +1069,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ide-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ide-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1078,7 +1078,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-announce-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-announce-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1087,7 +1087,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-scala-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-scala-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1096,7 +1096,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-sonar-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-sonar-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1105,7 +1105,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/sonar-batch-bootstrapper-2.9.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/sonar-batch-bootstrapper-2.9.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1114,7 +1114,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-signing-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-signing-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1123,7 +1123,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ear-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ear-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1132,7 +1132,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-javascript-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-javascript-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1141,7 +1141,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/rhino-1.7R3.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/rhino-1.7R3.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1150,7 +1150,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gson-2.2.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gson-2.2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1159,7 +1159,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/simple-4.1.21.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/simple-4.1.21.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1168,7 +1168,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-build-comparison-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-build-comparison-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1177,7 +1177,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-diagnostics-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-diagnostics-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1186,7 +1186,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-reporting-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-reporting-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1195,7 +1195,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jatl-0.2.2.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jatl-0.2.2.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1204,7 +1204,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-publish-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-publish-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1213,7 +1213,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ivy-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ivy-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1222,7 +1222,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-jacoco-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-jacoco-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1231,7 +1231,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-build-init-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-build-init-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
@@ -1240,7 +1240,7 @@
     <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-jvm-2.4-20150322230018+0000.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-jvm-2.5.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/AndroidConfigHelper.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/AndroidConfigHelper.java
new file mode 100644
index 0000000..fa03d57
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/AndroidConfigHelper.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * Utility functions for initializing an AndroidConfig.
+ */
+public class AndroidConfigHelper {
+    public static void configure(
+            @NonNull AndroidConfig model,
+            @NonNull Instantiator instantiator) {
+        model.setDefaultPublishConfig(BuilderConstants.RELEASE);
+        model.setPublishNonDefault(false);
+        model.setGeneratePureSplits(false);
+        model.setPreProcessingOptions(instantiator.newInstance(PreprocessingOptions.class));
+        model.setDeviceProviders(Lists.<DeviceProvider>newArrayList());
+        model.setTestServers(Lists.<TestServer>newArrayList());
+        model.setAaptOptions(instantiator.newInstance(AaptOptions.class));
+        model.setDexOptions(instantiator.newInstance(DexOptions.class));
+        model.setLintOptions(instantiator.newInstance(LintOptions.class));
+        model.setTestOptions(instantiator.newInstance(TestOptions.class));
+        model.setCompileOptions(instantiator.newInstance(CompileOptions.class));
+        model.setPackagingOptions(instantiator.newInstance(PackagingOptions.class));
+        model.setJacoco(instantiator.newInstance(JacocoExtension.class));
+        model.setAdbOptions(instantiator.newInstance(AdbOptions.class));
+        model.setSplits(instantiator.newInstance(Splits.class, instantiator));
+        model.setLibraryRequests(Lists.<LibraryRequest>newArrayList());
+    }
+
+
+    public static NamedDomainObjectContainer<AndroidSourceSet> createSourceSetsContainer(
+            @NonNull final Project project,
+            @NonNull Instantiator instantiator,
+            final boolean isLibrary) {
+        NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer = project.container(
+                AndroidSourceSet.class,
+                new AndroidSourceSetFactory(instantiator, project, isLibrary));
+
+        sourceSetsContainer.whenObjectAdded(new Action<AndroidSourceSet>() {
+            @Override
+            public void execute(AndroidSourceSet sourceSet) {
+                ConfigurationContainer configurations = project.getConfigurations();
+
+                createConfiguration(
+                        configurations,
+                        sourceSet.getCompileConfigurationName(),
+                        "Classpath for compiling the ${sourceSet.name} sources.");
+
+                String packageConfigDescription;
+                if (isLibrary) {
+                    packageConfigDescription
+                            = "Classpath only used when publishing '${sourceSet.name}'.";
+                } else {
+                    packageConfigDescription
+                            = "Classpath packaged with the compiled '${sourceSet.name}' classes.";
+                }
+                createConfiguration(
+                        configurations,
+                        sourceSet.getPackageConfigurationName(),
+                        packageConfigDescription);
+
+                createConfiguration(
+                        configurations,
+                        sourceSet.getProvidedConfigurationName(),
+                        "Classpath for only compiling the ${sourceSet.name} sources.");
+
+                createConfiguration(
+                        configurations,
+                        sourceSet.getWearAppConfigurationName(),
+                        "Link to a wear app to embed for object '${sourceSet.name}'.");
+
+                sourceSet.setRoot(String.format("src/%s", sourceSet.getName()));
+
+            }
+        });
+        return sourceSetsContainer;
+    }
+
+    private static void createConfiguration(
+            @NonNull ConfigurationContainer configurations,
+            @NonNull String configurationName,
+            @NonNull String configurationDescription) {
+        Configuration configuration = configurations.findByName(configurationName);
+        if (configuration == null) {
+            configuration = configurations.create(configurationName);
+        }
+
+        // Disable modification to configurations as this causes issues when accessed through the
+        // tooling-api.  Check that it works with Studio's ImportProjectAction before re-enabling
+        // them.
+        //configuration.setVisible(false);
+        //configuration.setDescription(configurationDescription);
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/NdkOptionsHelper.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/NdkOptionsHelper.java
new file mode 100644
index 0000000..ee22699
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/NdkOptionsHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 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.build.gradle.managed.NdkBuildType;
+import com.android.build.gradle.managed.NdkOptions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * Helper functions for configuring an NdkOptions.
+ */
+public class NdkOptionsHelper {
+
+    /**
+     * Initialize an NdkOptions.
+     *
+     * Should be unnecessary when Gradle supports managed List of String.
+     */
+    public static void init(NdkOptions ndk) {
+        ndk.setCFlags(Lists.<String>newArrayList());
+        ndk.setCppFlags(Lists.<String>newArrayList());
+        ndk.setLdFlags(Lists.<String>newArrayList());
+        ndk.setLdLibs(Lists.<String>newArrayList());
+        ndk.setAbiFilters(Sets.<String>newHashSet());
+    }
+
+    /**
+     * Merge one NdkOptions to another.
+     * @param base NdkOptions to merge to.  base is mutated to contain the merged result.
+     * @param other NdkOptions to merge.  Options in other has priority over base if both are set.
+     */
+    public static void merge(NdkOptions base, NdkOptions other) {
+        if (other.getModuleName() != null) {
+            base.setModuleName(other.getModuleName());
+        }
+
+        base.getAbiFilters().addAll(other.getAbiFilters());
+        base.getCFlags().addAll(other.getCFlags());
+        base.getCppFlags().addAll(other.getCppFlags());
+        base.getLdFlags().addAll(other.getLdFlags());
+        base.getLdLibs().addAll(other.getLdLibs());
+
+        if (other.getStl() != null) {
+            base.setStl(other.getStl());
+        }
+        if (other.getRenderscriptNdkMode() != null) {
+            base.setRenderscriptNdkMode(other.getRenderscriptNdkMode());
+        }
+    }
+
+    /**
+     * Merge one NdkBuildType to another
+     * @param base NdkBuildType to merge to.  base is mutated to contain the merged result.
+     * @param other NdkBuildType to merge.  Options in other has priority over base if both are set.
+     */
+    public static void merge(NdkBuildType base, NdkBuildType other) {
+        merge(base, (NdkOptions) other);
+        if (other.getDebuggable() != null) {
+            base.setDebuggable(other.getDebuggable());
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/AndroidConfig.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/AndroidConfig.java
new file mode 100644
index 0000000..8ec1eca
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/AndroidConfig.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 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.managed;
+
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.build.gradle.model.AndroidComponentModelSourceSet;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.repository.FullRevision;
+
+import org.gradle.api.Action;
+import org.gradle.model.Managed;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Unmanaged;
+
+import java.util.Collection;
+import java.util.List;
+
+import groovy.lang.Closure;
+
+/**
+ * Component model for all Android plugin.
+ */
+@Managed
+public interface AndroidConfig {
+
+    /** Build tool version */
+    String getBuildToolsVersion();
+    void setBuildToolsVersion(String buildToolsVersion);
+
+    /** Compile SDK version */
+    String getCompileSdkVersion();
+    void setCompileSdkVersion(String compileSdkVersion);
+
+    /** Build tool revisions */
+    @Unmanaged
+    FullRevision getBuildToolsRevision();
+    void setBuildToolsRevision(FullRevision fullRevision);
+
+    /** Default config, shared by all flavors. */
+    ProductFlavor getDefaultConfig();
+
+    /** List of device providers */
+    @Unmanaged
+    List<DeviceProvider> getDeviceProviders();
+    void setDeviceProviders(List<DeviceProvider> providers);
+
+    /** List of remote CI servers */
+    @Unmanaged
+    List<TestServer> getTestServers();
+    void setTestServers(List<TestServer> providers);
+
+    /** Name of the variant to publish */
+    String getDefaultPublishConfig();
+    void setDefaultPublishConfig(String defaultPublishConfig);
+
+    /** Whether to also publish non-default variants */
+    Boolean getPublishNonDefault();
+    void setPublishNonDefault(Boolean publishNonDefault);
+
+    /** Filter to determine which variants to build */
+    @Unmanaged
+    Action<VariantFilter> getVariantFilter();
+    void setVariantFilter(Action<VariantFilter> filter);
+
+    /** A prefix to be used when creating new resources. Used by Studio */
+    String getResourcePrefix();
+    void setResourcePrefix(String resourcePrefix);
+
+    /** Whether to generate pure splits or multi apk */
+    Boolean getGeneratePureSplits();
+    void setGeneratePureSplits(Boolean generateSplits);
+
+    /** Whether to preprocess resources */
+    @Unmanaged
+    PreprocessingOptions getPreProcessingOptions();
+    void setPreProcessingOptions(PreprocessingOptions preprocessingOptions);
+
+    /** Build types used by this project. */
+    ModelMap<BuildType> getBuildTypes();
+
+    /** All product flavors used by this project. */
+    ModelMap<ProductFlavor> getProductFlavors();
+
+    /** Signing configs used by this project. */
+    ModelMap<SigningConfig> getSigningConfigs();
+
+    @Unmanaged
+    AndroidComponentModelSourceSet getSources();
+    void setSources(AndroidComponentModelSourceSet sources);
+
+    NdkConfig getNdk();
+
+    /** Adb options */
+    @Unmanaged
+    AdbOptions getAdbOptions();
+    void setAdbOptions(AdbOptions adbOptions);
+
+    /** Options for aapt, tool for packaging resources. */
+    @Unmanaged
+    AaptOptions getAaptOptions();
+    void setAaptOptions(AaptOptions aaptOptions);
+
+    /** Compile options */
+    @Unmanaged
+    CompileOptions getCompileOptions();
+    void setCompileOptions(CompileOptions compileOptions);
+
+    /** Dex options. */
+    @Unmanaged
+    DexOptions getDexOptions();
+    void setDexOptions(DexOptions dexOptions);
+
+    /** JaCoCo options. */
+    @Unmanaged
+    JacocoExtension getJacoco();
+    void setJacoco(JacocoExtension jacoco);
+
+    /** Lint options. */
+    @Unmanaged
+    LintOptions getLintOptions();
+    void setLintOptions(LintOptions lintOptions);
+
+    /** Packaging options. */
+    @Unmanaged
+    PackagingOptions getPackagingOptions();
+    void setPackagingOptions(PackagingOptions packagingOptions);
+
+    /** Options for running tests. */
+    @Unmanaged
+    TestOptions getTestOptions();
+    void setTestOptions(TestOptions testOptions);
+
+    /** APK splits */
+    @Unmanaged
+    Splits getSplits();
+    void setSplits(Splits splits);
+
+    @Unmanaged
+    Collection<LibraryRequest> getLibraryRequests();
+    void setLibraryRequests(Collection<LibraryRequest> libraryRequests);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ApiVersion.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ApiVersion.java
new file mode 100644
index 0000000..1a6ea0a
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ApiVersion.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import org.gradle.model.Managed;
+
+/**
+ * A Managed ApiVersion.
+ */
+@Managed
+public interface ApiVersion {
+
+    Integer getApiLevel();
+    void setApiLevel(Integer apiLevel);
+
+    String getCodename();
+    void setCodename(String codename);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/BuildType.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/BuildType.java
new file mode 100644
index 0000000..f1adf89
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/BuildType.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import com.android.builder.model.AndroidArtifact;
+
+import org.gradle.api.Named;
+import org.gradle.model.Managed;
+import org.gradle.model.ModelSet;
+import org.gradle.model.Unmanaged;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A Managed build type.
+ *
+ * TODO: Convert Unmanaged Collection to Managed type when Gradle provides ModelSet for basic class.
+ */
+@Managed
+public interface BuildType extends Named {
+
+    /**
+     * Map of Build Config Fields where the key is the field name.
+     *
+     * @return a non-null map of class fields (possibly empty).
+     */
+    ModelSet<ClassField> getBuildConfigFields();
+
+    /**
+     * Map of generated res values where the key is the res name.
+     *
+     * @return a non-null map of class fields (possibly empty).
+     */
+    ModelSet<ClassField> getResValues();
+
+    /**
+     * Returns the collection of proguard rule files.
+     *
+     * <p>These files are only applied to the production code.
+     *
+     * @return a non-null collection of files.
+     * @see #getTestProguardFiles()
+     */
+    @Unmanaged
+    Set<File> getProguardFiles();
+    void setProguardFiles(Set<File> files);
+
+    /**
+     * Returns the collection of proguard rule files for consumers of the library to use.
+     *
+     * @return a non-null collection of files.
+     */
+    @Unmanaged
+    Set<File> getConsumerProguardFiles();
+    void setConsumerProguardFiles(Set<File> files);
+
+    /**
+     * Returns the collection of proguard rule files to use for the test APK.
+     *
+     * @return a non-null collection of files.
+     */
+    @Unmanaged
+    Set<File> getTestProguardFiles();
+    void setTestProguardFiles(Set<File> files);
+
+    /**
+     * Returns the map of key value pairs for placeholder substitution in the android manifest file.
+     *
+     * This map will be used by the manifest merger.
+     * @return the map of key value pairs.
+     */
+    // TODO: Add the commented fields.
+    //Map<String, Object> getManifestPlaceholders();
+
+    /**
+     * Returns whether multi-dex is enabled.
+     *
+     * This can be null if the flag is not set, in which case the default value is used.
+     */
+    Boolean getMultiDexEnabled();
+    void setMultiDexEnabled(Boolean multiDexEnabled);
+
+    String getMultiDexKeepFile();
+    void setMultiDexKeepFile(String multiDexKeepFile);
+
+    String getMultiDexKeepProguard();
+    void setMultiDexKeepProguard(String multiDexKeepProguard);
+
+    /**
+     * Returns the optional jarjar rule files, or empty if jarjar should be skipped.
+     *
+     * <p>If more than one file is provided, the rule files will be merged in order with last one
+     * win in case of rule redefinition.
+     *
+     * <p>Can only be used with Jack toolchain.
+     *
+     * @return the optional jarjar rule file.
+     */
+    @Unmanaged
+    List<File> getJarJarRuleFiles();
+    void setJarJarRuleFiles(List<File> jarJarRuleFiles);
+
+    /**
+     * Returns whether the build type is configured to generate a debuggable apk.
+     *
+     * @return true if the apk is debuggable
+     */
+    Boolean getDebuggable();
+    void setDebuggable(Boolean isDebuggable);
+
+    /**
+     * Returns whether the build type is configured to be build with support for code coverage.
+     *
+     * @return true if code coverage is enabled.
+     */
+    Boolean getTestCoverageEnabled();
+    void setTestCoverageEnabled(Boolean isTestCoverageEnabled);
+
+    /**
+     * Returns whether the build type is configured to be build with support for pseudolocales.
+     *
+     * @return true if code coverage is enabled.
+     */
+    Boolean getPseudoLocalesEnabled();
+    void setPseudoLocalesEnabled(Boolean isPseudoLocalesEnabled);
+
+    /**
+     * Returns whether the build type is configured to generate an apk with debuggable
+     * renderscript code.
+     *
+     * @return true if the apk is debuggable
+     */
+    Boolean getRenderscriptDebuggable();
+    void setRenderscriptDebuggable(Boolean isRenderscriptDebuggable);
+
+    /**
+     * Returns the optimization level of the renderscript compilation.
+     *
+     * @return the optimization level.
+     */
+    Integer getRenderscriptOptimLevel();
+    void setRenderscriptOptimLevel(Integer renderscriptOptimLevel);
+
+    /**
+     * Returns the application id suffix applied to this build type.
+     * To get the final application id, use {@link AndroidArtifact#getApplicationId()}.
+     *
+     * @return the application id
+     */
+    String getApplicationIdSuffix();
+    void setApplicationIdSuffix(String applicationIdSuffix);
+
+    /**
+     * Returns the version name suffix.
+     *
+     * @return the version name suffix.
+     */
+    String getVersionNameSuffix();
+    void setVersionNameSuffix(String versionNameSuffix);
+
+    /**
+     * Returns whether minification is enabled for this build type.
+     *
+     * @return true if minification is enabled.
+     */
+    Boolean getMinifyEnabled();
+    void setMinifyEnabled(Boolean isMinifyEnabled);
+
+    /**
+     * Return whether zipalign is enabled for this build type.
+     *
+     * @return true if zipalign is enabled.
+     */
+    Boolean getZipAlignEnabled();
+    void setZipAlignEnabled(Boolean isZipAlignEnabled);
+
+    /**
+     * Returns whether the variant embeds the micro app.
+     */
+    Boolean getEmbedMicroApp();
+    void setEmbedMicroApp(Boolean isEmbedMicroApp);
+
+    /**
+     * Returns the associated signing config or null if none are set on the build type.
+     */
+    SigningConfig getSigningConfig();
+    void setSigningConfig(SigningConfig signingConfig);
+
+    Boolean getUseJack();
+    void setUseJack(Boolean useJack);
+
+    Boolean getShrinkResources();
+    void setShrinkResources(Boolean shrinkResources);
+
+    NdkBuildType getNdk();
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ClassField.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ClassField.java
new file mode 100644
index 0000000..f04492d
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ClassField.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+
+import java.util.Set;
+
+/**
+ * A Managed ClassField.
+ */
+@Managed
+public interface ClassField {
+
+    String getType();
+    void setType(String type);
+
+    String getName();
+    void setName(String name);
+
+    String getValue();
+    void setValue(String value);
+
+    String getDocumentation();
+    void setDocumentation(String documentation);
+
+    @Unmanaged
+    Set<String> getAnnotations();
+    void setAnnotations(Set<String> annotations);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/FilePattern.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/FilePattern.java
new file mode 100644
index 0000000..a710e27
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/FilePattern.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import org.gradle.model.Managed;
+import org.gradle.model.ModelSet;
+
+/**
+ * A Managed interface for FilterablePattern.
+ */
+@Managed
+public interface FilePattern {
+
+    ModelSet<ManagedString> getIncludes();
+
+    ModelSet<ManagedString> getExcludes();
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ManagedString.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ManagedString.java
new file mode 100644
index 0000000..0354dcd
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ManagedString.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import org.gradle.model.Managed;
+
+/**
+ * A Managed wrapper of String.
+ *
+ * This really should not be necessary, but at the moment, there is no way to create a ModelSet of
+ * String or equivalent.
+ */
+@Managed
+public interface ManagedString {
+    String getValue();
+    void setValue(String value);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkBuildType.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkBuildType.java
new file mode 100644
index 0000000..4da05e6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkBuildType.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import org.gradle.model.Managed;
+
+/**
+ * NdkOptions with additional options specific to build types.
+ */
+@Managed
+public interface NdkBuildType extends NdkOptions {
+    /**
+     * Returns whether the resulting shared object is debuggable.
+     */
+    Boolean getDebuggable();
+    void setDebuggable(Boolean isDebuggable);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkConfig.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkConfig.java
new file mode 100644
index 0000000..5240e3e
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkConfig.java
@@ -0,0 +1,30 @@
+package com.android.build.gradle.managed;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Root configuration model for android-ndk plugin.
+ */
+@Managed
+public interface NdkConfig extends NdkBuildType {
+
+    /**
+     * The toolchain version.
+     * Support "gcc" or "clang" (default: "gcc").
+     */
+    String getToolchain();
+    void setToolchain(@NonNull String toolchain);
+
+    /**
+     * The toolchain version.
+     * Set as empty to use the default version for the toolchain.
+     */
+    String getToolchainVersion();
+    void setToolchainVersion(@NonNull String toolchainVersion);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkOptions.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkOptions.java
new file mode 100644
index 0000000..344c3ca
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkOptions.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * DSL object for variant specific NDK-related settings.
+ */
+@Managed
+public interface NdkOptions {
+
+    /**
+     * The module name.
+     * The resulting shared object will be named "lib${getModuleName()}.so".
+     */
+    String getModuleName();
+    void setModuleName(@NonNull String moduleName);
+
+    /**
+     * The ABI Filters.  Leave empty to include all supported ABI.
+     */
+    @Unmanaged
+    Set<String> getAbiFilters();
+    void setAbiFilters(@NonNull Set<String> filters);
+
+    /**
+     * The C Flags
+     */
+    @Unmanaged
+    List<String> getCFlags();
+    void setCFlags(@NonNull List<String> cFlags);
+
+    /**
+     * The C++ Flags
+     */
+    @Unmanaged
+    List<String> getCppFlags();
+    void setCppFlags(@NonNull List<String> cppFlags);
+
+    /**
+     * The linker flags
+     */
+    @Unmanaged
+    List<String> getLdFlags();
+    void setLdFlags(@NonNull List<String> ldFlags);
+
+    /**
+     * The LD Libs
+     */
+    @Unmanaged
+    List<String> getLdLibs();
+    void setLdLibs(@NonNull List<String> ldLibs);
+
+    /**
+     * The STL.
+     *
+     * Supported values are:
+     *   - system (default)
+     *   - gabi++_static
+     *   - gabi++_shared
+     *   - stlport_static
+     *   - stlport_shared
+     *   - gnustl_static
+     *   - gnustl_shared
+     */
+    String getStl();
+    void setStl(@NonNull String stl);
+
+    Boolean getRenderscriptNdkMode();
+    void setRenderscriptNdkMode(Boolean renderscriptNdkMode);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ProductFlavor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ProductFlavor.java
new file mode 100644
index 0000000..b3d15ef
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ProductFlavor.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.DimensionAware;
+import com.android.builder.model.Variant;
+
+import org.gradle.api.Named;
+import org.gradle.model.Managed;
+import org.gradle.model.ModelSet;
+import org.gradle.model.Unmanaged;
+
+import java.util.List;
+import java.util.Set;
+
+import java.io.File;
+
+/**
+ * A Managed product flavor.
+ *
+ * TODO: Convert Unmanaged Collection to Managed type when Gradle provides ModelSet for basic class.
+ */
+@Managed
+public interface ProductFlavor extends Named, DimensionAware {
+
+    /**
+     * Map of Build Config Fields where the key is the field name.
+     *
+     * @return a non-null map of class fields (possibly empty).
+     */
+    @NonNull
+    ModelSet<ClassField> getBuildConfigFields();
+
+    /**
+     * Map of generated res values where the key is the res name.
+     *
+     * @return a non-null map of class fields (possibly empty).
+     */
+    @NonNull
+    ModelSet<ClassField> getResValues();
+
+    /**
+     * Returns the collection of proguard rule files.
+     *
+     * <p>These files are only applied to the production code.
+     *
+     * @return a non-null collection of files.
+     * @see #getTestProguardFiles()
+     */
+    @Unmanaged
+    Set<File> getProguardFiles();
+    void setProguardFiles(Set<File> files);
+
+    /**
+     * Returns the collection of proguard rule files for consumers of the library to use.
+     *
+     * @return a non-null collection of files.
+     */
+    @Unmanaged
+    Set<File> getConsumerProguardFiles();
+    void setConsumerProguardFiles(Set<File> files);
+
+    /**
+     * Returns the collection of proguard rule files to use for the test APK.
+     *
+     * @return a non-null collection of files.
+     */
+    @Unmanaged
+    Set<File> getTestProguardFiles();
+    void setTestProguardFiles(Set<File> files);
+
+    /**
+     * Returns the map of key value pairs for placeholder substitution in the android manifest file.
+     *
+     * This map will be used by the manifest merger.
+     * @return the map of key value pairs.
+     */
+    // TODO: Add the commented fields.
+    //Map<String, Object> getManifestPlaceholders();
+
+    /**
+     * Returns whether multi-dex is enabled.
+     *
+     * This can be null if the flag is not set, in which case the default value is used.
+     */
+    @Nullable
+    Boolean getMultiDexEnabled();
+    void setMultiDexEnabled(Boolean multiDexEnabled);
+
+    @Nullable
+    File getMultiDexKeepFile();
+    void setMultiDexKeepFile(File multiDexKeepFile);
+
+    @Nullable
+    File getMultiDexKeepProguard();
+    void setMultiDexKeepProguard(File multiDexKeepProguard);
+
+    /**
+     * Returns the optional jarjar rule files, or empty if jarjar should be skipped.
+     *
+     * <p>If more than one file is provided, the rule files will be merged in order with last one
+     * win in case of rule redefinition.
+     *
+     * <p>Can only be used with Jack toolchain.
+     *
+     * @return the optional jarjar rule file.
+     */
+    @Unmanaged
+    List<File> getJarJarRuleFiles();
+    void setJarJarRuleFiles(List<File> jarJarRuleFiles);
+
+    /**
+     * Returns the flavor dimension or null if not applicable.
+     */
+    @Override
+    @Nullable
+    String getDimension();
+    void setDimension(String dimension);
+
+    /**
+     * Returns the name of the product flavor. This is only the value set on this product flavor.
+     * To get the final application id name, use {@link AndroidArtifact#getApplicationId()}.
+     *
+     * @return the application id.
+     */
+    @Nullable
+    String getApplicationId();
+    void setApplicationId(String applicationId);
+
+    /**
+     * Returns the version code associated with this flavor or null if none have been set.
+     * This is only the value set on this product flavor, not necessarily the actual
+     * version code used.
+     *
+     * @return the version code, or null if not specified
+     */
+    @Nullable
+    Integer getVersionCode();
+    void setVersionCode(Integer versionCode);
+
+    /**
+     * Returns the version name. This is only the value set on this product flavor.
+     * To get the final value, use {@link Variant#getMergedFlavor()} as well as
+     * {@link BuildType#getVersionNameSuffix()}
+     *
+     * @return the version name.
+     */
+    @Nullable
+    String getVersionName();
+    void setVersionName(String versionName);
+
+    /**
+     * Returns the minSdkVersion. This is only the value set on this product flavor.
+     *
+     * @return the minSdkVersion, or null if not specified
+     */
+    @Nullable
+    ApiVersion getMinSdkVersion();
+
+    /**
+     * Returns the targetSdkVersion. This is only the value set on this product flavor.
+     *
+     * @return the targetSdkVersion, or null if not specified
+     */
+    @Nullable
+    ApiVersion getTargetSdkVersion();
+
+    /**
+     * Returns the maxSdkVersion. This is only the value set on this produce flavor.
+     *
+     * @return the maxSdkVersion, or null if not specified
+     */
+    @Nullable
+    Integer getMaxSdkVersion();
+    void setMaxSdkVersion(Integer maxSdkVersion);
+
+    /**
+     * Returns the renderscript target api. This is only the value set on this product flavor.
+     * TODO: make final renderscript target api available through the model
+     *
+     * @return the renderscript target api, or null if not specified
+     */
+    @Nullable
+    Integer getRenderscriptTargetApi();
+    void setRenderscriptTargetApi(Integer renderscriptTargetApi);
+
+    /**
+     * 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, false if not, and null if not specified.
+     */
+    @Nullable
+    Boolean getRenderscriptSupportModeEnabled();
+    void setRenderscriptSupportModeEnabled(Boolean renderscriptSupportModeEnabled);
+
+    /**
+     * Returns whether the renderscript code should be compiled to generate C/C++ bindings.
+     * @return true for C/C++ generation, false for Java, null if not specified.
+     */
+    @Nullable
+    Boolean getRenderscriptNdkModeEnabled();
+    void setRenderscriptNdkModeEnabled(Boolean renderscriptNdkModeEnabled);
+
+    /**
+     * Returns the test application id. This is only the value set on this product flavor.
+     * To get the final value, use {@link Variant#getExtraAndroidArtifacts()} with
+     * {@link AndroidProject#ARTIFACT_ANDROID_TEST} and then
+     * {@link AndroidArtifact#getApplicationId()}
+     *
+     * @return the test package name.
+     */
+    @Nullable
+    String getTestApplicationId();
+    void setTestApplicationId(String testApplicationId);
+
+    /**
+     * Returns the test instrumentation runner. This is only the value set on this product flavor.
+     * TODO: make test instrumentation runner available through the model.
+     *
+     * @return the test package name.
+     */
+    @Nullable
+    String getTestInstrumentationRunner();
+    void setTestInstrumentationRunner(String testInstrumentationRunner);
+
+    /**
+     * Returns the handlingProfile value. This is only the value set on this product flavor.
+     *
+     *  @return the handlingProfile value.
+     */
+    @Nullable
+    Boolean getTestHandleProfiling();
+    void setTestHandleProfiling(Boolean testHandleProfiling);
+
+    /**
+     * Returns the functionalTest value. This is only the value set on this product flavor.
+     *
+     * @return the functionalTest value.
+     */
+    @Nullable
+    Boolean getTestFunctionalTest();
+    void setTestFunctionalTest(Boolean testFunctionalTest);
+
+    /**
+     * Returns the resource configuration for this variant.
+     *
+     * This is the list of -c parameters for aapt.
+     *
+     * @return the resource configuration options.
+     */
+    @Unmanaged
+    @Nullable
+    Set<String> getResourceConfigurations();
+    void setResourceConfigurations(Set<String> resourceConfigurations);
+
+    /**
+     * Returns the associated signing config or null if none are set on the product flavor.
+     */
+    SigningConfig getSigningConfig();
+    void setSigningConfig(SigningConfig signingConfig);
+
+    Boolean getUseJack();
+    void setUseJack(Boolean useJack);
+
+    NdkOptions getNdk();
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/SigningConfig.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/SigningConfig.java
new file mode 100644
index 0000000..627f5b6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/SigningConfig.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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.managed;
+
+import org.gradle.api.Named;
+import org.gradle.model.Managed;
+
+/**
+ * A Managed SigningConfig.
+ */
+@Managed
+public interface SigningConfig extends Named {
+
+    String getStoreFile();
+    void setStoreFile(String storeFile);
+
+    String getStorePassword();
+    void setStorePassword(String storePassword);
+
+    String getKeyAlias();
+    void setKeyAlias(String keyAlias);
+
+    String getKeyPassword();
+    void setKeyPassword(String keyPassword);
+
+    String getStoreType();
+    void setStoreType(String storeType);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/AndroidConfigAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/AndroidConfigAdaptor.java
new file mode 100644
index 0000000..f2f118e
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/AndroidConfigAdaptor.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 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.managed.adaptor;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.api.AndroidSourceDirectorySet;
+import com.android.build.gradle.api.AndroidSourceFile;
+import com.android.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.BuildTypeData;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.ProductFlavorData;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.build.gradle.managed.SigningConfig;
+import com.android.build.gradle.model.AndroidComponentModelSourceSet;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.sdklib.repository.FullRevision;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import groovy.lang.Closure;
+
+/**
+ * An adaptor to convert a managed.AndroidConfig to an model.AndroidConfig.
+ */
+public class AndroidConfigAdaptor implements com.android.build.gradle.AndroidConfig {
+
+    private final AndroidConfig model;
+    private NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer;
+
+    public AndroidConfigAdaptor(
+            AndroidConfig model,
+            NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer) {
+        this.model = model;
+        this.sourceSetsContainer = sourceSetsContainer;
+        applyProjectSourceSet();
+    }
+
+    @Override
+    public String getBuildToolsVersion() {
+        return model.getBuildToolsVersion();
+    }
+
+    @Override
+    public String getCompileSdkVersion() {
+        return model.getCompileSdkVersion();
+    }
+
+    @Override
+    public FullRevision getBuildToolsRevision() {
+        return model.getBuildToolsRevision();
+    }
+
+    @Override
+    public boolean getEnforceUniquePackageName() {
+        return false;
+    }
+
+    @Override
+    public CoreProductFlavor getDefaultConfig() {
+        return new ProductFlavorAdaptor(model.getDefaultConfig());
+    }
+
+    @Override
+    @NonNull
+    public List<DeviceProvider> getDeviceProviders() {
+        return model.getDeviceProviders() == null ?
+                Lists.<DeviceProvider>newArrayList() :
+                model.getDeviceProviders();
+    }
+
+    @Override
+    @NonNull
+    public List<TestServer> getTestServers() {
+        return model.getTestServers();
+    }
+
+    @Override
+    public String getDefaultPublishConfig() {
+        return model.getDefaultPublishConfig();
+    }
+
+    @Override
+    public boolean getPublishNonDefault() {
+        return model.getPublishNonDefault();
+    }
+
+    @Override
+    public Action<VariantFilter> getVariantFilter() {
+        return model.getVariantFilter();
+    }
+
+    @Override
+    public String getResourcePrefix() {
+        return model.getResourcePrefix();
+    }
+
+    @Override
+    public List<String> getFlavorDimensionList() {
+        return null;
+    }
+
+    @Override
+    public boolean getGeneratePureSplits() {
+        return model.getGeneratePureSplits();
+    }
+
+    @Override
+    public PreprocessingOptions getPreprocessingOptions() {
+        return model.getPreProcessingOptions();
+    }
+
+    @Override
+    public Collection<CoreBuildType> getBuildTypes() {
+        return ImmutableList.copyOf(Iterables.transform(model.getBuildTypes().values(),
+                new Function<BuildType, CoreBuildType>() {
+                    @Override
+                    public CoreBuildType apply(BuildType buildType) {
+                        return new BuildTypeAdaptor(buildType);
+                    }
+                }));
+    }
+
+    @Override
+    public Collection<CoreProductFlavor> getProductFlavors() {
+        return ImmutableList.copyOf(Iterables.transform(model.getProductFlavors().values(),
+                new Function<ProductFlavor, CoreProductFlavor>() {
+                    @Override
+                    public CoreProductFlavor apply(ProductFlavor flavor) {
+                        return new ProductFlavorAdaptor(flavor);
+                    }
+                }));
+    }
+
+    @Override
+    public Collection<com.android.builder.model.SigningConfig> getSigningConfigs() {
+        return ImmutableList.copyOf(Iterables.transform(model.getSigningConfigs().values(),
+                new Function<SigningConfig, com.android.builder.model.SigningConfig>() {
+                    @Override
+                    public com.android.builder.model.SigningConfig apply(SigningConfig signingConfig) {
+                        return new SigningConfigAdaptor(signingConfig);
+                    }
+                }));
+    }
+
+    @Override
+    public NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
+        return sourceSetsContainer;
+    }
+
+    @Override
+    public Boolean getPackageBuildConfig() {
+        return true;
+    }
+
+    public AndroidComponentModelSourceSet getSources() {
+        return model.getSources();
+    }
+
+    public void setSources(AndroidComponentModelSourceSet sources) {
+        model.setSources(sources);
+    }
+
+    public CoreNdkOptions getNdk() {
+        return new NdkOptionsAdaptor(model.getNdk());
+    }
+
+    @Override
+    public AdbOptions getAdbOptions() {
+        return model.getAdbOptions();
+    }
+
+    @Override
+    public AaptOptions getAaptOptions() {
+        return model.getAaptOptions();
+    }
+
+    @Override
+    public CompileOptions getCompileOptions() {
+        return model.getCompileOptions();
+    }
+
+    @Override
+    public DexOptions getDexOptions() {
+        return model.getDexOptions();
+    }
+
+    @Override
+    public JacocoExtension getJacoco() {
+        return model.getJacoco();
+    }
+
+    @Override
+    public LintOptions getLintOptions() {
+        return model.getLintOptions();
+    }
+
+    @Override
+    public PackagingOptions getPackagingOptions() {
+        return model.getPackagingOptions();
+    }
+
+
+    @Override
+    public TestOptions getTestOptions() {
+        return model.getTestOptions();
+    }
+
+    @Override
+    public Splits getSplits() {
+        return model.getSplits();
+    }
+
+    @Override
+    public Collection<LibraryRequest> getLibraryRequests() {
+        return model.getLibraryRequests();
+    }
+
+    private void applyProjectSourceSet() {
+        for (FunctionalSourceSet source : getSources()) {
+            String name = source.getName();
+            AndroidSourceSet androidSource = name.equals(BuilderConstants.MAIN) ?
+                    sourceSetsContainer.maybeCreate(getDefaultConfig().getName()) :
+                    sourceSetsContainer.maybeCreate(name);
+
+            convertSourceFile(androidSource.getManifest(), source, "manifest");
+            convertSourceSet(androidSource.getResources(), source, "resource");
+            convertSourceSet(androidSource.getJava(), source, "java");
+            convertSourceSet(androidSource.getRes(), source, "res");
+            convertSourceSet(androidSource.getAssets(), source, "assets");
+            convertSourceSet(androidSource.getAidl(), source, "aidl");
+            convertSourceSet(androidSource.getRenderscript(), source, "renderscript");
+            convertSourceSet(androidSource.getJni(), source, "jni");
+            convertSourceSet(androidSource.getJniLibs(), source, "jniLibs");
+        }
+    }
+
+    @Nullable
+    private static AndroidSourceSet findAndroidSourceSet(
+            VariantManager variantManager,
+            String name) {
+        BuildTypeData buildTypeData = variantManager.getBuildTypes().get(name);
+        if (buildTypeData != null) {
+            return buildTypeData.getSourceSet();
+        }
+
+        boolean isTest = name.startsWith(ANDROID_TEST.getPrefix());
+        name = name.replaceFirst(ANDROID_TEST.getPrefix(), "");
+        ProductFlavorData productFlavorData = variantManager.getProductFlavors().get(name);
+        if (productFlavorData != null) {
+            return isTest ? productFlavorData.getTestSourceSet(ANDROID_TEST) : productFlavorData.getSourceSet();
+        }
+        return null;
+    }
+
+    /**
+     * Convert a FunctionalSourceSet to an AndroidSourceFile.
+     */
+    private static void convertSourceFile(
+            AndroidSourceFile androidFile,
+            FunctionalSourceSet source,
+            String sourceName) {
+        LanguageSourceSet languageSourceSet = source.findByName(sourceName);
+        if (languageSourceSet == null) {
+            return;
+        }
+        SourceDirectorySet dir = languageSourceSet.getSource();
+        if (dir == null) {
+            return;
+        }
+        // We use the first file in the file tree until Gradle has a way to specify one source file
+        // instead of an entire source set.
+        Set<File> files = dir.getAsFileTree().getFiles();
+        if (!files.isEmpty()) {
+            androidFile.srcFile(Iterables.getOnlyElement(files));
+        }
+    }
+
+    /**
+     * Convert a FunctionalSourceSet to an AndroidSourceDirectorySet.
+     */
+    private static void convertSourceSet(
+            AndroidSourceDirectorySet androidDir,
+            FunctionalSourceSet source,
+            String sourceName) {
+        LanguageSourceSet languageSourceSet = source.findByName(sourceName);
+        if (languageSourceSet == null) {
+            return;
+        }
+        SourceDirectorySet dir = languageSourceSet.getSource();
+        if (dir == null) {
+            return;
+        }
+        androidDir.setSrcDirs(dir.getSrcDirs());
+        androidDir.include(dir.getIncludes());
+        androidDir.exclude(dir.getExcludes());
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ApiVersionAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ApiVersionAdaptor.java
new file mode 100644
index 0000000..9a17558
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ApiVersionAdaptor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.managed.ApiVersion;
+
+/**
+ * An adaptor to convert a managed.ApiVersion to an model.ApiVersion.
+ */
+public class ApiVersionAdaptor implements com.android.builder.model.ApiVersion {
+
+    private final ApiVersion apiVersion;
+
+    public static boolean isEmpty(ApiVersion apiVersion) {
+        return apiVersion.getApiLevel() == null &&
+                apiVersion.getCodename() == null;
+    }
+
+    public ApiVersionAdaptor(ApiVersion apiVersion) {
+        this.apiVersion = apiVersion;
+    }
+
+    @Override
+    public int getApiLevel() {
+        return apiVersion.getApiLevel() == null ? 0 : apiVersion.getApiLevel();
+    }
+
+    @Nullable
+    @Override
+    public String getCodename() {
+        return apiVersion.getCodename();
+    }
+
+    @NonNull
+    @Override
+    public String getApiString() {
+        return getCodename() != null ? getCodename() : Integer.toString(getApiLevel());
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/BuildTypeAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/BuildTypeAdaptor.java
new file mode 100644
index 0000000..b6f364d
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/BuildTypeAdaptor.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2015 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.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.managed.BuildType;
+import com.android.builder.internal.ClassFieldImpl;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.SigningConfig;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An adaptor to convert a BuildType to a CoreBuildType.
+ */
+public class BuildTypeAdaptor implements CoreBuildType {
+    @NonNull
+    private final BuildType buildType;
+
+    public BuildTypeAdaptor(@NonNull BuildType buildType) {
+        this.buildType = buildType;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return buildType.getName();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, ClassField> getBuildConfigFields() {
+        ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+        for (com.android.build.gradle.managed.ClassField cf : buildType.getBuildConfigFields()) {
+            builder.put(
+                    cf.getName(),
+                    new ClassFieldImpl(
+                            cf.getType(),
+                            cf.getName(),
+                            cf.getValue(),
+                            Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+                            Objects.firstNonNull(cf.getDocumentation(), "")));
+        }
+        return builder.build();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, ClassField> getResValues() {
+        ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+        for (com.android.build.gradle.managed.ClassField cf : buildType.getResValues()) {
+            builder.put(
+                    cf.getName(),
+                    new ClassFieldImpl(
+                            cf.getType(),
+                            cf.getName(),
+                            cf.getValue(),
+                            Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+                            Objects.firstNonNull(cf.getDocumentation(), "")));
+        }
+        return builder.build();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getProguardFiles() {
+        return buildType.getProguardFiles();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getConsumerProguardFiles() {
+        return buildType.getConsumerProguardFiles();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getTestProguardFiles() {
+        return buildType.getTestProguardFiles();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, Object> getManifestPlaceholders() {
+        // TODO: To be implemented
+        return Maps.newHashMap();
+    }
+
+    @Nullable
+    @Override
+    public Boolean getMultiDexEnabled() {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public File getMultiDexKeepFile() {
+        return null;
+    }
+
+    @Nullable
+    @Override
+    public File getMultiDexKeepProguard() {
+        return null;
+    }
+
+    @Override
+    public boolean isDebuggable() {
+        return buildType.getDebuggable();
+    }
+
+    @Override
+    public boolean isTestCoverageEnabled() {
+        return buildType.getTestCoverageEnabled();
+    }
+
+    @Override
+    public boolean isJniDebuggable() {
+        return Objects.firstNonNull(buildType.getNdk().getDebuggable(), false);
+    }
+
+    @Override
+    public boolean isPseudoLocalesEnabled() {
+        return buildType.getPseudoLocalesEnabled();
+    }
+
+    @Override
+    public boolean isRenderscriptDebuggable() {
+        return buildType.getRenderscriptDebuggable();
+    }
+
+    @Override
+    public int getRenderscriptOptimLevel() {
+        return buildType.getRenderscriptOptimLevel();
+    }
+
+    @Nullable
+    @Override
+    public String getApplicationIdSuffix() {
+        return buildType.getApplicationIdSuffix();
+    }
+
+    @Nullable
+    @Override
+    public String getVersionNameSuffix() {
+        return buildType.getVersionNameSuffix();
+    }
+
+    @Override
+    public boolean isMinifyEnabled() {
+        return buildType.getMinifyEnabled();
+    }
+
+    @Override
+    public boolean isZipAlignEnabled() {
+        return buildType.getZipAlignEnabled();
+    }
+
+    @Override
+    public boolean isEmbedMicroApp() {
+        return buildType.getEmbedMicroApp();
+    }
+
+    @Nullable
+    @Override
+    public SigningConfig getSigningConfig() {
+        return buildType.getSigningConfig() == null ? null : new SigningConfigAdaptor(buildType.getSigningConfig());
+    }
+
+    @Override
+    public CoreNdkOptions getNdkConfig() {
+        return new NdkOptionsAdaptor(buildType.getNdk());
+    }
+
+    @Override
+    public Boolean getUseJack() {
+        return buildType.getUseJack();
+    }
+
+    @Override
+    public boolean isShrinkResources() {
+        return buildType.getShrinkResources();
+    }
+
+    @NonNull
+    @Override
+    public List<File> getJarJarRuleFiles() {
+        return buildType.getJarJarRuleFiles();
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/NdkOptionsAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/NdkOptionsAdaptor.java
new file mode 100644
index 0000000..4643423
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/NdkOptionsAdaptor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.managed.NdkOptions;
+import com.google.common.base.Joiner;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An adaptor to convert a NdkConfig to NdkConfig.
+ */
+public class NdkOptionsAdaptor implements CoreNdkOptions {
+
+    NdkOptions ndkOptions;
+
+    public NdkOptionsAdaptor(@NonNull NdkOptions ndkOptions) {
+        this.ndkOptions = ndkOptions;
+    }
+
+    @Nullable
+    @Override
+    public String getModuleName() {
+        return ndkOptions.getModuleName();
+    }
+
+    @Nullable
+    @Override
+    public String getcFlags() {
+        return Joiner.on(' ').join(ndkOptions.getCFlags());
+    }
+
+    @Nullable
+    @Override
+    public List<String> getLdLibs() {
+        return ndkOptions.getLdLibs();
+    }
+
+    @Nullable
+    @Override
+    public Set<String> getAbiFilters() {
+        return ndkOptions.getAbiFilters();
+    }
+
+    @Nullable
+    @Override
+    public String getStl() {
+        return ndkOptions.getStl();
+    }
+
+    @Nullable
+    @Override
+    public Integer getJobs() {
+        return null;
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ProductFlavorAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ProductFlavorAdaptor.java
new file mode 100644
index 0000000..68639f7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ProductFlavorAdaptor.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2015 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.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.internal.ClassFieldImpl;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.SigningConfig;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An adaptor to convert a ProductFlavor to CoreProductFlavor.
+ */
+public class ProductFlavorAdaptor implements CoreProductFlavor {
+
+    @NonNull
+    protected final ProductFlavor productFlavor;
+
+    public ProductFlavorAdaptor(@NonNull ProductFlavor productFlavor) {
+        this.productFlavor = productFlavor;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return productFlavor.getName().equals("defaultConfig") ? BuilderConstants.MAIN : productFlavor.getName();
+    }
+
+    @Nullable
+    @Override
+    public String getDimension() {
+        return productFlavor.getDimension();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, ClassField> getBuildConfigFields() {
+        ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+        for (com.android.build.gradle.managed.ClassField cf : productFlavor.getBuildConfigFields()) {
+            builder.put(
+                    cf.getName(),
+                    new ClassFieldImpl(
+                            cf.getType(),
+                            cf.getName(),
+                            cf.getValue(),
+                            Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+                            Objects.firstNonNull(cf.getDocumentation(), "")));
+        }
+        return builder.build();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, ClassField> getResValues() {
+        ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+        for (com.android.build.gradle.managed.ClassField cf : productFlavor.getResValues()) {
+            builder.put(
+                    cf.getName(),
+                    new ClassFieldImpl(
+                            cf.getType(),
+                            cf.getName(),
+                            cf.getValue(),
+                            Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+                            Objects.firstNonNull(cf.getDocumentation(), "")));
+        }
+        return builder.build();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getProguardFiles() {
+        return productFlavor.getProguardFiles();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getConsumerProguardFiles() {
+        return productFlavor.getConsumerProguardFiles();
+    }
+
+    @NonNull
+    @Override
+    public Collection<File> getTestProguardFiles() {
+        return productFlavor.getTestProguardFiles();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, Object> getManifestPlaceholders() {
+        // TODO: To be implemented
+        return Maps.newHashMap();
+    }
+
+    @Nullable
+    @Override
+    public Boolean getMultiDexEnabled() {
+        return productFlavor.getMultiDexEnabled();
+    }
+
+    @Nullable
+    @Override
+    public File getMultiDexKeepFile() {
+        return productFlavor.getMultiDexKeepFile();
+    }
+
+    @Nullable
+    @Override
+    public File getMultiDexKeepProguard() {
+        return productFlavor.getMultiDexKeepProguard();
+    }
+
+    @Nullable
+    @Override
+    public String getApplicationId() {
+        return productFlavor.getApplicationId();
+    }
+
+    @Nullable
+    @Override
+    public Integer getVersionCode() {
+        return productFlavor.getVersionCode();
+    }
+
+    @Nullable
+    @Override
+    public String getVersionName() {
+        return productFlavor.getVersionName();
+    }
+
+    @Nullable
+    @Override
+    public ApiVersion getMinSdkVersion() {
+        return ApiVersionAdaptor.isEmpty(productFlavor.getMinSdkVersion()) ?
+                null :
+                new ApiVersionAdaptor(productFlavor.getMinSdkVersion());
+    }
+
+    @Nullable
+    @Override
+    public ApiVersion getTargetSdkVersion() {
+        return ApiVersionAdaptor.isEmpty(productFlavor.getTargetSdkVersion()) ?
+                null :
+                new ApiVersionAdaptor(productFlavor.getTargetSdkVersion());
+    }
+
+    @Nullable
+    @Override
+    public Integer getMaxSdkVersion() {
+        return productFlavor.getMaxSdkVersion();
+    }
+
+    @Nullable
+    @Override
+    public Integer getRenderscriptTargetApi() {
+        return productFlavor.getRenderscriptTargetApi();
+    }
+
+    @Nullable
+    @Override
+    public Boolean getRenderscriptSupportModeEnabled() {
+        return productFlavor.getRenderscriptSupportModeEnabled();
+    }
+
+    @Nullable
+    @Override
+    public Boolean getRenderscriptNdkModeEnabled() {
+        return productFlavor.getRenderscriptNdkModeEnabled();
+    }
+
+    @Nullable
+    @Override
+    public String getTestApplicationId() {
+        return productFlavor.getTestApplicationId();
+    }
+
+    @Nullable
+    @Override
+    public String getTestInstrumentationRunner() {
+        return productFlavor.getTestInstrumentationRunner();
+    }
+
+    @NonNull
+    @Override
+    public Map<String, String> getTestInstrumentationRunnerArguments() {
+        // TODO: To be implemented.
+        return Maps.newHashMap();
+    }
+
+    @Nullable
+    @Override
+    public Boolean getTestHandleProfiling() {
+        return productFlavor.getTestHandleProfiling();
+    }
+
+    @Nullable
+    @Override
+    public Boolean getTestFunctionalTest() {
+        return productFlavor.getTestFunctionalTest();
+    }
+
+    @NonNull
+    @Override
+    public Collection<String> getResourceConfigurations() {
+        return productFlavor.getResourceConfigurations();
+    }
+
+    @Nullable
+    @Override
+    public SigningConfig getSigningConfig() {
+        return productFlavor.getSigningConfig() == null ?
+                null :
+                new SigningConfigAdaptor(productFlavor.getSigningConfig());
+    }
+
+    @Override
+    public CoreNdkOptions getNdkConfig() {
+        return new NdkOptionsAdaptor(productFlavor.getNdk());
+    }
+
+    @Override
+    public Boolean getUseJack() {
+        return productFlavor.getUseJack();
+    }
+
+    @NonNull
+    @Override
+    public List<File> getJarJarRuleFiles() {
+        return productFlavor.getJarJarRuleFiles();
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/SigningConfigAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/SigningConfigAdaptor.java
new file mode 100644
index 0000000..ddf199c
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/SigningConfigAdaptor.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreSigningConfig;
+import com.android.build.gradle.managed.SigningConfig;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+
+import java.io.File;
+
+/**
+ * An adaptor to convert a managed.SigningConfig to a model.SigningConfig.
+ */
+public class SigningConfigAdaptor implements CoreSigningConfig {
+
+    @NonNull
+    private final SigningConfig signingConfig;
+
+    public SigningConfigAdaptor(@NonNull SigningConfig signingConfig) {
+        this.signingConfig = signingConfig;
+    }
+
+    @NonNull
+    @Override
+    public String getName() {
+        return signingConfig.getName();
+    }
+
+    @Nullable
+    @Override
+    @InputFile @Optional
+    public File getStoreFile() {
+        return signingConfig.getStoreFile() == null ? null : new File(signingConfig.getStoreFile());
+    }
+
+    @Nullable
+    @Override
+    @Input
+    public String getStorePassword() {
+        return signingConfig.getStorePassword();
+    }
+
+    @Nullable
+    @Override
+    @Input
+    public String getKeyAlias() {
+        return signingConfig.getKeyAlias();
+    }
+
+    @Nullable
+    @Override
+    public String getKeyPassword() {
+        return signingConfig.getKeyPassword();
+    }
+
+    @Nullable
+    @Override
+    @Input
+    public String getStoreType() {
+        return signingConfig.getStoreType();
+    }
+
+    @Override
+    public boolean isSigningReady() {
+        return signingConfig.getStoreFile() != null &&
+                signingConfig.getStorePassword() != null &&
+                signingConfig.getKeyAlias() != null &&
+                signingConfig.getKeyPassword() != null;
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java
index 5218247..2fede34 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java
@@ -16,9 +16,8 @@
 
 package com.android.build.gradle.model;
 
-import com.android.build.gradle.api.GroupableProductFlavor;
-import com.android.builder.model.BuildType;
-import com.android.builder.model.ProductFlavor;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ProductFlavor;
 
 import org.gradle.platform.base.BinarySpec;
 
@@ -30,5 +29,5 @@
 public interface AndroidBinary extends BinarySpec {
     BuildType getBuildType();
 
-    List<? extends ProductFlavor> getProductFlavors();
+    List<ProductFlavor> getProductFlavors();
 }
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.groovy
deleted file mode 100644
index db13b2e..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.groovy
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2014 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.model
-
-import com.android.build.gradle.internal.ProductFlavorCombo
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.BuildTypeFactory
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory
-import com.android.builder.core.BuilderConstants
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.internal.registry.LanguageRegistry
-import org.gradle.language.base.plugins.ComponentModelBasePlugin
-import org.gradle.model.Defaults
-import org.gradle.model.Finalize
-import org.gradle.model.Model
-import org.gradle.model.Mutate
-import org.gradle.model.Path
-import org.gradle.model.RuleSource
-import org.gradle.model.collection.CollectionBuilder
-import org.gradle.model.internal.core.ModelCreators
-import org.gradle.model.internal.core.ModelPath
-import org.gradle.model.internal.core.ModelReference
-import org.gradle.model.internal.registry.ModelRegistry
-import org.gradle.platform.base.Binary
-import org.gradle.platform.base.BinaryContainer
-import org.gradle.platform.base.BinaryType
-import org.gradle.platform.base.BinaryTypeBuilder
-import org.gradle.platform.base.ComponentBinaries
-import org.gradle.platform.base.ComponentSpecContainer
-import org.gradle.platform.base.ComponentType
-import org.gradle.platform.base.ComponentTypeBuilder
-import org.gradle.platform.base.LanguageType
-import org.gradle.platform.base.LanguageTypeBuilder
-
-import javax.inject.Inject
-
-/**
- * Plugin to set up infrastructure for other android plugins.
- */
-@CompileStatic
-public class AndroidComponentModelPlugin implements Plugin<Project> {
-    /**
-     * The name of ComponentSpec created with android component model plugin.
-     */
-    public static final String COMPONENT_NAME = "android";
-
-    private final ModelRegistry modelRegistry
-
-    @Inject
-    public AndroidComponentModelPlugin(ModelRegistry modelRegistry) {
-        this.modelRegistry = modelRegistry
-    }
-
-    @Override
-    public void apply(Project project) {
-        project.apply plugin: ComponentModelBasePlugin
-
-        // Remove this when our models no longer depends on Project.
-        modelRegistry.create(
-                ModelCreators.bridgedInstance(
-                        ModelReference.of("projectModel", Project), project)
-                                .descriptor("Model of project.")
-                                .build())
-    }
-
-    static class Rules extends RuleSource {
-        @LanguageType
-        void registerLanguage(LanguageTypeBuilder<AndroidLanguageSourceSet> builder) {
-            builder.setLanguageName("android")
-            builder.defaultImplementation(AndroidLanguageSourceSet)
-        }
-
-        @Model("android")
-        void android(AndroidModel androidModel) {
-        }
-
-        // Initialize each component separately to ensure correct ordering.
-        @Defaults
-        void androidModelBuildTypes (
-                AndroidModel androidModel,
-                @Path("androidBuildTypes") NamedDomainObjectContainer<BuildType> buildTypes) {
-            androidModel.buildTypes = buildTypes
-        }
-
-        @Defaults
-        void androidModelProductFlavors (
-                AndroidModel androidModel,
-                @Path("androidProductFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavors) {
-            androidModel.productFlavors = productFlavors
-        }
-
-        @Defaults
-        void androidModelSources (
-                AndroidModel androidModel,
-                @Path("androidSources") AndroidComponentModelSourceSet sources) {
-            androidModel.sources = sources
-        }
-
-        @Model
-        NamedDomainObjectContainer<BuildType> androidBuildTypes(
-                ServiceRegistry serviceRegistry,
-                Project project) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class)
-            def buildTypeContainer = project.container(BuildType,
-                    new BuildTypeFactory(instantiator, project, project.getLogger()))
-
-            // create default Objects, signingConfig first as its used by the BuildTypes.
-            buildTypeContainer.create(BuilderConstants.DEBUG)
-            buildTypeContainer.create(BuilderConstants.RELEASE)
-
-            buildTypeContainer.whenObjectRemoved {
-                throw new UnsupportedOperationException("Removing build types is not supported.")
-            }
-            return buildTypeContainer
-        }
-
-        @Model
-        NamedDomainObjectContainer<GroupableProductFlavor> androidProductFlavors(
-                ServiceRegistry serviceRegistry,
-                Project project) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class)
-            def productFlavorContainer = project.container(GroupableProductFlavor,
-                    new GroupableProductFlavorFactory(instantiator, project, project.getLogger()))
-
-            productFlavorContainer.whenObjectRemoved {
-                throw new UnsupportedOperationException(
-                        "Removing product flavors is not supported.")
-            }
-
-            return productFlavorContainer
-        }
-
-        @Model
-        List<ProductFlavorCombo> createProductFlavorCombo (
-                @Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavors) {
-            // TODO: Create custom product flavor container to manually configure flavor dimensions.
-            List<String> flavorDimensionList = productFlavors*.dimension.unique().asList()
-            flavorDimensionList.removeAll([null])
-
-            return ProductFlavorCombo.createCombinations(flavorDimensionList, productFlavors)
-        }
-
-        @ComponentType
-        void defineComponentType(ComponentTypeBuilder<AndroidComponentSpec> builder) {
-            builder.defaultImplementation(DefaultAndroidComponentSpec)
-        }
-
-        @Mutate
-        void createAndroidComponents(CollectionBuilder<AndroidComponentSpec> androidComponents) {
-            androidComponents.create(COMPONENT_NAME)
-        }
-
-        @Model
-        AndroidComponentSpec androidComponentSpec(ComponentSpecContainer specs) {
-            return (AndroidComponentSpec) specs.getByName(COMPONENT_NAME)
-        }
-
-        @Model
-        AndroidComponentModelSourceSet androidSources (
-                ServiceRegistry serviceRegistry,
-                ProjectSourceSet projectSourceSet,
-                LanguageRegistry languageRegistry) {
-            def instantiator = serviceRegistry.get(Instantiator.class)
-            def sources = new AndroidComponentModelSourceSet(instantiator, projectSourceSet)
-
-            languageRegistry.each { languageRegistration ->
-                sources.registerLanguage(languageRegistration)
-            }
-
-            // Create main source set.
-            sources.create("main")
-
-            return sources
-        }
-
-        /**
-         * Create all source sets for each AndroidBinary.
-         *
-         * Need to ensure this is done before model mutation in build.gradle.
-         */
-        @Mutate
-        void createVariantSourceSet(
-                @Path("android.sources") AndroidComponentModelSourceSet sources,
-                @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypes,
-                @Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> flavors,
-                List<ProductFlavorCombo> flavorGroups) {
-            buildTypes.each { buildType ->
-                sources.maybeCreate(buildType.name)
-            }
-            flavorGroups.each { group ->
-                sources.maybeCreate(group.name)
-                if (!group.flavorList.isEmpty()) {
-                    buildTypes.each { buildType ->
-                        sources.maybeCreate(group.name + buildType.name.capitalize())
-                    }
-                }
-            }
-            if (flavorGroups.size() != flavors.size()) {
-                // If flavorGroups and flavors are the same size, there is at most 1 flavor
-                // dimension.  So we don't need to reconfigure the source sets for flavorGroups.
-                flavors.each { flavor ->
-                    sources.maybeCreate(flavor.name)
-                }
-            }
-        }
-
-        @Finalize
-        void setDefaultSrcDir(@Path("android.sources") AndroidComponentModelSourceSet sourceSet) {
-            sourceSet.setDefaultSrcDir()
-        }
-
-        @BinaryType
-        void defineBinaryType(BinaryTypeBuilder<AndroidBinary> builder) {
-            builder.defaultImplementation(DefaultAndroidBinary)
-        }
-
-        @ComponentBinaries
-        void createBinaries(
-                CollectionBuilder<AndroidBinary> binaries,
-                @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypes,
-                List<ProductFlavorCombo> flavorCombos,
-                AndroidComponentSpec spec) {
-            if (flavorCombos.isEmpty()) {
-                flavorCombos.add(new ProductFlavorCombo());
-            }
-
-            buildTypes.each { BuildType buildType ->
-                flavorCombos.each { ProductFlavorCombo flavorCombo ->
-                    binaries.create(getBinaryName(buildType, flavorCombo)) {
-                        def binary = it as DefaultAndroidBinary
-                        binary.buildType = buildType
-                        binary.productFlavors = flavorCombo.flavorList
-                    }
-                }
-            }
-        }
-
-        private static String getBinaryName(BuildType buildType, ProductFlavorCombo flavorCombo) {
-            if (flavorCombo.flavorList.isEmpty()) {
-                return  buildType.name
-            } else {
-                return  flavorCombo.name + buildType.name.capitalize()
-            }
-        }
-    }
-
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.java
new file mode 100644
index 0000000..e680dad
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+
+import com.android.build.gradle.internal.ProductFlavorCombo;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.builder.core.BuilderConstants;
+import com.android.sdklib.repository.FullRevision;
+import com.android.utils.StringHelper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
+
+import org.gradle.api.Action;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.registry.LanguageRegistration;
+import org.gradle.language.base.internal.registry.LanguageRegistry;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.model.Defaults;
+import org.gradle.model.Finalize;
+import org.gradle.model.Model;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinaryType;
+import org.gradle.platform.base.BinaryTypeBuilder;
+import org.gradle.platform.base.ComponentBinaries;
+import org.gradle.platform.base.ComponentType;
+import org.gradle.platform.base.ComponentTypeBuilder;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+import org.gradle.tooling.BuildException;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Plugin to set up infrastructure for other android plugins.
+ */
+public class AndroidComponentModelPlugin implements Plugin<Project> {
+
+    /**
+     * The name of ComponentSpec created with android component model plugin.
+     */
+    public static final String COMPONENT_NAME = "android";
+
+    //public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.5.*");
+    public static final String GRADLE_ACCEPTABLE_VERSION = "2.5";
+
+    private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
+            "com.android.build.gradle.overrideVersionCheck";
+
+    @Override
+    public void apply(Project project) {
+        checkGradleVersion(project);
+        project.getPlugins().apply(ComponentModelBasePlugin.class);
+    }
+
+    private static void checkGradleVersion(Project project) {
+        String gradleVersion = project.getGradle().getGradleVersion();
+        if (!gradleVersion.startsWith(GRADLE_ACCEPTABLE_VERSION)) {
+            boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+            File file = new File("gradle" + File.separator + "wrapper" + File.separator +
+                    "gradle-wrapper.properties");
+            String errorMessage = 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_ACCEPTABLE_VERSION, gradleVersion, file.getAbsolutePath(),
+                    GRADLE_ACCEPTABLE_VERSION);
+            if (allowNonMatching) {
+                project.getLogger().warn(errorMessage);
+                project.getLogger().warn("As %s is set, continuing anyways.",
+                        GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+            } else {
+                throw new BuildException(errorMessage, null);
+            }
+        }
+    }
+
+    @SuppressWarnings("MethodMayBeStatic")
+    public static class Rules extends RuleSource {
+
+        @LanguageType
+        public void registerLanguage(LanguageTypeBuilder<AndroidLanguageSourceSet> builder) {
+            builder.setLanguageName("android");
+            builder.defaultImplementation(AndroidLanguageSourceSet.class);
+        }
+
+        /**
+         * Create "android" model block.
+         */
+        @Model("android")
+        public void android(AndroidConfig androidModel) {
+        }
+
+        @Defaults
+        public void androidModelSources(AndroidConfig androidModel,
+                @Path("androidSources") AndroidComponentModelSourceSet sources) {
+            androidModel.setSources(sources);
+        }
+
+        @Finalize
+        public void finalizeAndroidModel(AndroidConfig androidModel) {
+            if (androidModel.getBuildToolsRevision() == null
+                    && androidModel.getBuildToolsVersion() != null) {
+                androidModel.setBuildToolsRevision(
+                        FullRevision.parseRevision(androidModel.getBuildToolsVersion()));
+            }
+
+            if (androidModel.getCompileSdkVersion() != null
+                    && !androidModel.getCompileSdkVersion().startsWith("android-")
+                    && Ints.tryParse(androidModel.getCompileSdkVersion()) != null) {
+                androidModel.setCompileSdkVersion("android-" + androidModel.getCompileSdkVersion());
+            }
+
+        }
+
+        @Defaults
+        public void createDefaultBuildTypes(
+                @Path("android.buildTypes") ModelMap<BuildType> buildTypes) {
+            buildTypes.create(BuilderConstants.DEBUG, new Action<BuildType>() {
+                @Override
+                public void execute(BuildType buildType) {
+                    buildType.setDebuggable(true);
+                    buildType.setEmbedMicroApp(false);
+                }
+            });
+            buildTypes.create(BuilderConstants.RELEASE);
+        }
+
+        @Model
+        public List<ProductFlavorCombo<ProductFlavor>> createProductFlavorCombo(
+                @Path("android.productFlavors") ModelMap<ProductFlavor> productFlavors) {
+            // TODO: Create custom product flavor container to manually configure flavor dimensions.
+            Set<String> flavorDimensionList = Sets.newHashSet();
+            for (ProductFlavor flavor : productFlavors.values()) {
+                if (flavor.getDimension() != null) {
+                    flavorDimensionList.add(flavor.getDimension());
+                }
+            }
+
+            return ProductFlavorCombo.createCombinations(
+                    Lists.newArrayList(flavorDimensionList),
+                    productFlavors.values());
+        }
+
+        @ComponentType
+        public void defineComponentType(ComponentTypeBuilder<AndroidComponentSpec> builder) {
+            builder.defaultImplementation(DefaultAndroidComponentSpec.class);
+        }
+
+        @Mutate
+        public void createAndroidComponents(ModelMap<AndroidComponentSpec> androidComponents) {
+            androidComponents.create(COMPONENT_NAME);
+        }
+
+        @Model
+        public AndroidComponentModelSourceSet androidSources(ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return new AndroidComponentModelSourceSet(instantiator);
+        }
+
+        /**
+         * Create all source sets for each AndroidBinary.
+         */
+        @Mutate
+        public void createVariantSourceSet(
+                @Path("android.sources") final AndroidComponentModelSourceSet sources,
+                @Path("android.buildTypes") final ModelMap<BuildType> buildTypes,
+                @Path("android.productFlavors") ModelMap<ProductFlavor> flavors,
+                List<ProductFlavorCombo<ProductFlavor>> flavorGroups, ProjectSourceSet projectSourceSet,
+                LanguageRegistry languageRegistry) {
+            sources.setProjectSourceSet(projectSourceSet);
+            for (LanguageRegistration languageRegistration : languageRegistry) {
+                sources.registerLanguage(languageRegistration);
+            }
+
+            // Create main source set.
+            sources.create("main");
+            sources.create(ANDROID_TEST.getPrefix());
+            sources.create(UNIT_TEST.getPrefix());
+
+            for (BuildType buildType : buildTypes.values()) {
+                sources.maybeCreate(buildType.getName());
+
+                for (ProductFlavorCombo group: flavorGroups) {
+                    sources.maybeCreate(group.getName());
+                    if (!group.getFlavorList().isEmpty()) {
+                        sources.maybeCreate(
+                                group.getName() + StringHelper.capitalize(buildType.getName()));
+                    }
+
+                }
+
+            }
+            if (flavorGroups.size() != flavors.size()) {
+                // If flavorGroups and flavors are the same size, there is at most 1 flavor
+                // dimension.  So we don't need to reconfigure the source sets for flavorGroups.
+                for (ProductFlavor flavor: flavors.values()) {
+                    sources.maybeCreate(flavor.getName());
+                }
+            }
+        }
+
+        @Finalize
+        public void setDefaultSrcDir(
+                @Path("android.sources") AndroidComponentModelSourceSet sourceSet) {
+            sourceSet.setDefaultSrcDir();
+        }
+
+        @BinaryType
+        public void defineBinaryType(BinaryTypeBuilder<AndroidBinary> builder) {
+            builder.defaultImplementation(DefaultAndroidBinary.class);
+        }
+
+        @ComponentBinaries
+        public void createBinaries(
+                final ModelMap<AndroidBinary> binaries,
+                @Path("android") final AndroidConfig androidConfig,
+                @Path("android.buildTypes") final ModelMap<BuildType> buildTypes,
+                final List<ProductFlavorCombo<ProductFlavor>> flavorCombos,
+                final AndroidComponentSpec spec) {
+            if (flavorCombos.isEmpty()) {
+                flavorCombos.add(new ProductFlavorCombo<ProductFlavor>());
+            }
+
+            for (final BuildType buildType : buildTypes.values()) {
+                for (final ProductFlavorCombo<ProductFlavor> flavorCombo : flavorCombos) {
+                    binaries.create(getBinaryName(buildType, flavorCombo),
+                            new Action<AndroidBinary>() {
+                                @Override
+                                public void execute(AndroidBinary androidBinary) {
+                                    DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+                                    binary.setBuildType(buildType);
+                                    binary.setProductFlavors(flavorCombo.getFlavorList());
+                                }
+                            });
+                }
+            }
+        }
+
+        private static String getBinaryName(BuildType buildType, ProductFlavorCombo flavorCombo) {
+            if (flavorCombo.getFlavorList().isEmpty()) {
+                return buildType.getName();
+            } else {
+                return flavorCombo.getName() + StringHelper.capitalize(buildType.getName());
+            }
+
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java
index af341ea..7e555d9 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java
@@ -39,11 +39,8 @@
         implements NamedDomainObjectContainer<FunctionalSourceSet> {
     ProjectSourceSet projectSourceSet;
 
-    public AndroidComponentModelSourceSet (
-            Instantiator instantiator,
-            ProjectSourceSet projectSourceSet) {
+    public AndroidComponentModelSourceSet (Instantiator instantiator) {
         super(FunctionalSourceSet.class, instantiator);
-        this.projectSourceSet = projectSourceSet;
     }
 
     public <T extends LanguageSourceSet> void registerLanguage(final LanguageRegistration<T> languageRegistration) {
@@ -56,7 +53,15 @@
                         languageRegistration.getSourceSetFactory(functionalSourceSet.getName()));
             }
         });
+    }
 
+    /**
+     * Setter for projectSourceSet.
+     * Having a setter avoid the need for ProjectSourceSet to be part of the constructor, which can cause 
+     * cyclic rule dependenecy issues.
+     */
+    public void setProjectSourceSet(ProjectSourceSet projectSourceSet) {
+        this.projectSourceSet = projectSourceSet;
     }
 
     @Override
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.groovy
deleted file mode 100644
index ae964e7..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2015 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.model
-
-import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantManager
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.TestVariantData
-import groovy.transform.CompileStatic
-import org.gradle.api.Task
-import org.gradle.model.Mutate
-import org.gradle.model.RuleSource
-import org.gradle.model.collection.CollectionBuilder
-import org.gradle.platform.base.BinaryContainer
-
-import static com.android.builder.core.VariantType.ANDROID_TEST
-
-/**
- * Plugin for creating test tasks for AndroidBinary.
- */
-@SuppressWarnings("GrMethodMayBeStatic")
-@CompileStatic
-class AndroidComponentModelTestPlugin extends RuleSource {
-
-    @Mutate
-    void createConnectedTestTasks(
-            CollectionBuilder<Task> tasks,
-            BinaryContainer binaries,
-            TaskManager taskManager,
-            AndroidComponentSpec spec) {
-        TestedExtension extension
-        if (spec.extension instanceof TestedExtension) {
-            extension = spec.extension as TestedExtension
-        } else {
-            return
-        }
-
-        VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-        binaries.withType(AndroidBinary) { androidBinary->
-            DefaultAndroidBinary binary = androidBinary as DefaultAndroidBinary
-
-            if (binary.buildType.name != extension.testBuildType) {
-                return
-            }
-
-            // Create test tasks.
-            BaseVariantData testedVariantData = binary.variantData
-
-            assert testedVariantData != null,
-                    "Internal error: tested variant must be created before test variant."
-
-            TestVariantData testVariantData =
-                    variantManager.createTestVariantData(testedVariantData, ANDROID_TEST)
-            variantManager.getVariantDataList().add(testVariantData);
-            variantManager.createTasksForVariantData(
-                    new TaskCollectionBuilderAdaptor(tasks),
-                    testVariantData)
-        }
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.java
new file mode 100644
index 0000000..187c0a0
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.builder.core.BuilderConstants;
+import com.google.common.base.Preconditions;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinaryContainer;
+
+/**
+ * Plugin for creating test tasks for AndroidBinary.
+ */
+@SuppressWarnings("MethodMayBeStatic")
+public class AndroidComponentModelTestPlugin extends RuleSource {
+
+    @Mutate
+    public void createConnectedTestTasks(
+            final ModelMap<Task> tasks,
+            BinaryContainer binaries,
+            TaskManager taskManager,
+            ModelMap<AndroidComponentSpec> specs) {
+        final VariantManager variantManager =
+                ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME)).getVariantManager();
+        binaries.withType(AndroidBinary.class, new Action<AndroidBinary>() {
+            @Override
+            public void execute(AndroidBinary androidBinary) {
+                DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+
+                // TODO: compare against testBuildType instead of BuilderConstants.DEBUG.
+                if (!binary.getBuildType().getName().equals(BuilderConstants.DEBUG)) {
+                    return;
+
+                }
+
+                // Create test tasks.
+                BaseVariantData testedVariantData = binary.getVariantData();
+
+                Preconditions.checkState(testedVariantData != null,
+                        "Internal error: tested variant must be created before test variant.");
+
+                TestVariantData testVariantData =
+                        variantManager.createTestVariantData(testedVariantData, ANDROID_TEST);
+                variantManager.getVariantDataList().add(testVariantData);
+                variantManager.createTasksForVariantData(
+                        new TaskModelMapAdaptor(tasks),
+                        testVariantData);
+            }
+        });
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java
index 83895c7..210b45a 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java
@@ -16,7 +16,7 @@
 
 package com.android.build.gradle.model;
 
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.managed.AndroidConfig;
 
 import org.gradle.platform.base.ComponentSpec;
 
@@ -24,5 +24,5 @@
  * Android ComponentSpec.
  */
 public interface AndroidComponentSpec extends ComponentSpec{
-    BaseExtension getExtension();
+    AndroidConfig getExtension();
 }
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidModel.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidModel.java
deleted file mode 100644
index d86d097..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidModel.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014 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.model;
-
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.SigningConfig;
-import com.android.build.gradle.ndk.NdkExtension;
-
-import org.gradle.api.NamedDomainObjectContainer;
-import org.gradle.model.Managed;
-import org.gradle.model.Unmanaged;
-
-/**
- * Component model for all Android plugin.
- */
-@Managed
-public interface AndroidModel {
-    @Unmanaged
-    NamedDomainObjectContainer<BuildType> getBuildTypes();
-
-    void setBuildTypes(NamedDomainObjectContainer<BuildType> buildTypes);
-
-    @Unmanaged
-    NamedDomainObjectContainer<GroupableProductFlavor> getProductFlavors();
-
-    void setProductFlavors(NamedDomainObjectContainer<GroupableProductFlavor> productFlavors);
-
-    @Unmanaged
-    NamedDomainObjectContainer<SigningConfig> getSigningConfigs();
-
-    void setSigningConfigs(NamedDomainObjectContainer<SigningConfig> signingConfigs);
-
-    @Unmanaged
-    AndroidComponentModelSourceSet getSources();
-
-    void setSources(AndroidComponentModelSourceSet sources);
-
-    @Unmanaged
-    NdkExtension getNdk();
-
-    void setNdk(NdkExtension ndk);
-
-    @Unmanaged
-    BaseExtension getConfig();
-
-    void setConfig(BaseExtension config);
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.groovy
deleted file mode 100644
index b4b8896..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 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.model
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.DependencyManager
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.variant.ApplicationVariantFactory
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.builder.core.AndroidBuilder
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.model.Model
-import org.gradle.model.RuleSource
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-/**
- * Gradle component model plugin class for 'application' projects.
- */
-public class AppComponentModelPlugin implements Plugin<Project> {
-
-    @Override
-    void apply(Project project) {
-        project.plugins.apply(BaseComponentModelPlugin)
-        project.plugins.apply(AndroidComponentModelTestPlugin)
-    }
-
-    static class Rules extends RuleSource {
-
-        @Model
-        Boolean isApplication() {
-            return true
-        }
-
-        @Model
-        TaskManager createTaskManager(
-                BaseExtension androidExtension,
-                Project project,
-                AndroidBuilder androidBuilder,
-                SdkHandler sdkHandler,
-                ExtraModelInfo extraModelInfo,
-                ToolingModelBuilderRegistry toolingRegistry) {
-            DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo)
-
-            return new ApplicationComponentTaskManager(
-                    project,
-                    androidBuilder,
-                    androidExtension,
-                    sdkHandler,
-                    dependencyManager,
-                    toolingRegistry);
-        }
-
-
-        @Model
-        VariantFactory createVariantFactory(
-                ServiceRegistry serviceRegistry,
-                AndroidBuilder androidBuilder,
-                BaseExtension extension) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
-            return new ApplicationVariantFactory(
-                    instantiator,
-                    androidBuilder,
-                    extension)
-        }
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.java
new file mode 100644
index 0000000..0fd6ac7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 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.model;
+
+import static com.android.build.gradle.model.ModelConstants.IS_APPLICATION;
+import static com.android.build.gradle.model.ModelConstants.TASK_MANAGER;
+
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.variant.ApplicationVariantFactory;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.RuleSource;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+/**
+ * Gradle component model plugin class for 'application' projects.
+ */
+public class AppComponentModelPlugin implements Plugin<Project> {
+
+    @Override
+    public void apply(Project project) {
+        project.getPluginManager().apply(BaseComponentModelPlugin.class);
+        project.getPluginManager().apply(AndroidComponentModelTestPlugin.class);
+    }
+
+    @SuppressWarnings("MethodMayBeStatic")
+    public static class Rules extends RuleSource {
+        @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+        @Model(IS_APPLICATION)
+        public Boolean isApplication() {
+            return true;
+        }
+
+        @Model(TASK_MANAGER)
+        public TaskManager createTaskManager(
+                AndroidConfig androidExtension,
+                Project project,
+                AndroidBuilder androidBuilder,
+                SdkHandler sdkHandler,
+                ExtraModelInfo extraModelInfo,
+                ToolingModelBuilderRegistry toolingRegistry) {
+            DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo);
+
+            return new ApplicationComponentTaskManager(
+                    project,
+                    androidBuilder,
+                    androidExtension,
+                    sdkHandler,
+                    dependencyManager,
+                    toolingRegistry);
+        }
+
+        @Model
+        public VariantFactory createVariantFactory(
+                ServiceRegistry serviceRegistry,
+                AndroidBuilder androidBuilder,
+                AndroidConfig extension) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return new ApplicationVariantFactory(instantiator, androidBuilder, extension);
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java
index c91ac2f..d191c7a 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java
@@ -16,19 +16,19 @@
 
 package com.android.build.gradle.model;
 
-import com.android.build.gradle.BaseExtension;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.internal.ApplicationTaskManager;
 import com.android.build.gradle.internal.DependencyManager;
 import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.scope.VariantScope;
 import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.builder.core.AndroidBuilder;
 import com.google.common.collect.ImmutableList;
 
 import org.gradle.api.Project;
-import org.gradle.api.tasks.TaskContainer;
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
 
-import java.io.File;
 import java.util.Collection;
 
 /**
@@ -39,7 +39,7 @@
     public ApplicationComponentTaskManager (
             Project project,
             AndroidBuilder androidBuilder,
-            BaseExtension extension,
+            AndroidConfig extension,
             SdkHandler sdkHandler,
             DependencyManager dependencyManager,
             ToolingModelBuilderRegistry toolingRegistry) {
@@ -54,9 +54,7 @@
     }
 
     @Override
-    protected Collection<File> getNdkOutputDirectories(BaseVariantData variantData) {
-        NdkComponentModelPlugin plugin = project.getPlugins().getPlugin(
-                NdkComponentModelPlugin.class);
-        return plugin.getOutputDirectories(variantData.getVariantConfiguration());
+    public void configureScopeForNdk(@NonNull VariantScope scope) {
+        NdkComponentModelPlugin.configureScopeForNdk(scope);
     }
 }
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.groovy
deleted file mode 100644
index ef9857b..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.groovy
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2014 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.model
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.AppExtension
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.api.AndroidSourceDirectorySet
-import com.android.build.gradle.api.AndroidSourceSet
-import com.android.build.gradle.internal.BuildTypeData
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LibraryCache
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.build.gradle.internal.ProductFlavorData
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantManager
-import com.android.build.gradle.internal.coverage.JacocoPlugin
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.dsl.SigningConfigFactory
-import com.android.build.gradle.internal.model.ModelBuilder
-import com.android.build.gradle.internal.process.GradleJavaProcessExecutor
-import com.android.build.gradle.internal.process.GradleProcessExecutor
-import com.android.build.gradle.internal.profile.RecordingBuildListener
-import com.android.build.gradle.internal.tasks.DependencyReportTask
-import com.android.build.gradle.internal.tasks.SigningReportTask
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.tasks.JillTask
-import com.android.build.gradle.tasks.PreDex
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.internal.compiler.JackConversionCache
-import com.android.builder.internal.compiler.PreDexCache
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.ProcessRecorderFactory
-import com.android.builder.profile.Recorder
-import com.android.builder.profile.ThreadRecorder
-import com.android.builder.sdk.TargetInfo
-import com.android.ide.common.internal.ExecutorSingleton
-import com.android.ide.common.process.LoggedProcessOutputHandler
-import com.android.utils.ILogger
-import groovy.transform.CompileStatic
-import org.gradle.api.DefaultTask
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.base.internal.SourceTransformTaskConfig
-import org.gradle.language.base.internal.registry.LanguageTransform
-import org.gradle.model.Model
-import org.gradle.model.Mutate
-import org.gradle.model.Path
-import org.gradle.model.RuleSource
-import org.gradle.model.collection.CollectionBuilder
-import org.gradle.model.internal.core.ModelCreators
-import org.gradle.model.internal.core.ModelReference
-import org.gradle.model.internal.registry.ModelRegistry
-import org.gradle.platform.base.BinaryContainer
-import org.gradle.platform.base.BinarySpec
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-import javax.inject.Inject
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@CompileStatic
-public class BaseComponentModelPlugin implements Plugin<Project> {
-    ToolingModelBuilderRegistry toolingRegistry
-    ModelRegistry modelRegistry
-
-    @Inject
-    protected BaseComponentModelPlugin(
-            ToolingModelBuilderRegistry toolingRegistry,
-            ModelRegistry modelRegistry) {
-        this.toolingRegistry = toolingRegistry
-        this.modelRegistry = modelRegistry
-    }
-
-    /**
-     * Replace BasePlugin's apply method for component model.
-     */
-    @Override
-    public void apply(Project project) {
-        ProcessRecorderFactory.initialize(new LoggerWrapper(project.logger), project.rootProject.
-                file("profiler" + System.currentTimeMillis() + ".json"))
-        project.gradle.addListener(new RecordingBuildListener(ThreadRecorder.get()));
-
-        project.apply plugin: AndroidComponentModelPlugin
-
-        project.apply plugin: JavaBasePlugin
-        project.apply plugin: JacocoPlugin
-
-        // TODO: Create configurations for build types and flavors, or migrate to new dependency
-        // management if it's ready.
-        ConfigurationContainer configurations = project.getConfigurations()
-        createConfiguration(
-                configurations,
-                "compile",
-                "Classpath for default sources.")
-
-        createConfiguration(
-                configurations,
-                "default-metadata",
-                "Metadata for published APKs")
-
-        createConfiguration(
-                configurations,
-                "default-mapping",
-                "Metadata for published APKs")
-
-        project.tasks.getByName("assemble").description =
-                "Assembles all variants of all applications and secondary packages."
-
-        project.apply plugin: NdkComponentModelPlugin
-
-        modelRegistry.create(
-                ModelCreators.bridgedInstance(
-                        ModelReference.of("toolingRegistry", ToolingModelBuilderRegistry), toolingRegistry)
-                        .descriptor("Tooling model builder model registry.")
-                        .build())
-    }
-
-    @SuppressWarnings("GrMethodMayBeStatic")
-    static class Rules extends RuleSource {
-
-        @Mutate
-        void configureAndroidModel(
-                AndroidModel androidModel,
-                @Path("androidConfig") BaseExtension config,
-                @Path("androidSigningConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigs) {
-            androidModel.config = config
-            androidModel.signingConfigs = signingConfigs
-        }
-
-        // TODO: Remove code duplicated from BasePlugin.
-        @Model
-        ExtraModelInfo createExtraModelInfo(Project project) {
-            return new ExtraModelInfo(project)
-        }
-
-        @Model
-        SdkHandler createSdkHandler(Project project) {
-            ILogger logger = new LoggerWrapper(project.logger)
-            SdkHandler sdkHandler = new SdkHandler(project, logger)
-
-            // call back on execution. This is called after the whole build is done (not
-            // after the current project is done).
-            // This is will be called for each (android) projects though, so this should support
-            // being called 2+ times.
-            project.gradle.buildFinished {
-                ExecutorSingleton.shutdown()
-                sdkHandler.unload()
-                PreDexCache.getCache().clear(
-                        project.rootProject.file(
-                                "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"),
-                        logger)
-                JackConversionCache.getCache().clear(
-                        project.rootProject.file(
-                                "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"),
-                        logger)
-                LibraryCache.getCache().unload()
-            }
-
-            project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
-                for (Task task : taskGraph.allTasks) {
-                    if (task instanceof PreDex) {
-                        PreDexCache.getCache().load(
-                                project.rootProject.file(
-                                        "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"))
-                        break;
-                    } else if (task instanceof JillTask) {
-                        JackConversionCache.getCache().load(
-                                project.rootProject.file(
-                                        "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"))
-                        break;
-                    }
-                }
-            }
-            return sdkHandler
-        }
-
-        @Model
-        AndroidBuilder createAndroidBuilder(Project project) {
-            String creator = "Android Gradle"
-            ILogger logger = new LoggerWrapper(project.logger)
-
-            return new AndroidBuilder(
-                    project == project.rootProject ? project.name : project.path,
-                    creator,
-                    new GradleProcessExecutor(project),
-                    new GradleJavaProcessExecutor(project),
-                    new LoggedProcessOutputHandler(logger),
-                    logger,
-                    project.logger.isEnabled(LogLevel.INFO))
-
-        }
-
-        @Model("androidConfig")
-        BaseExtension androidConfig(
-                ServiceRegistry serviceRegistry,
-                @Path("androidBuildTypes") NamedDomainObjectContainer<BuildType> buildTypeContainer,
-                @Path("androidProductFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavorContainer,
-                @Path("androidSigningConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
-                @Path("isApplication") Boolean isApplication,
-                AndroidBuilder androidBuilder,
-                SdkHandler sdkHandler,
-                ExtraModelInfo extraModelInfo,
-                Project project) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
-
-            Class extensionClass = isApplication ? AppExtension : LibraryExtension
-
-            BaseExtension extension = (BaseExtension) instantiator.newInstance(extensionClass,
-                    (ProjectInternal) project, instantiator, androidBuilder,
-                    sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer,
-                    extraModelInfo, !isApplication)
-
-            return extension
-        }
-
-        @Mutate
-        void addDefaultAndroidSourceSet(AndroidComponentModelSourceSet sources) {
-            sources.addDefaultSourceSet("resources", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("java", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("manifest", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("res", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("assets", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("aidl", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("renderscript", AndroidLanguageSourceSet.class);
-            sources.addDefaultSourceSet("jniLibs", AndroidLanguageSourceSet.class);
-        }
-
-        @Mutate
-        void forwardCompileSdkVersion(
-                @Path("android.ndk") NdkExtension ndkExtension,
-                @Path("android.config") BaseExtension baseExtension) {
-            if (ndkExtension.compileSdkVersion.isEmpty() && baseExtension.compileSdkVersion != null) {
-                ndkExtension.compileSdkVersion(baseExtension.compileSdkVersion);
-            }
-        }
-
-        @Model("androidSigningConfigs")
-        NamedDomainObjectContainer<SigningConfig> signingConfig(ServiceRegistry serviceRegistry,
-                Project project) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
-            def signingConfigContainer =
-                    project.container(SigningConfig, new SigningConfigFactory(instantiator))
-            signingConfigContainer.create(DEBUG)
-            signingConfigContainer.whenObjectRemoved {
-                throw new UnsupportedOperationException("Removing signingConfigs is not supported.")
-            }
-            return signingConfigContainer
-        }
-
-        @Mutate
-        void closeProjectSourceSet(AndroidComponentModelSourceSet sources) {
-        }
-
-        @Mutate
-        void createAndroidComponents(
-                AndroidComponentSpec androidSpec,
-                ServiceRegistry serviceRegistry,
-                @Path("android.config") BaseExtension androidExtension,
-                @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypeContainer,
-                @Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavorContainer,
-                @Path("android.signingConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
-                VariantFactory variantFactory,
-                TaskManager taskManager,
-                Project project,
-                AndroidBuilder androidBuilder,
-                SdkHandler sdkHandler,
-                ExtraModelInfo extraModelInfo,
-                ToolingModelBuilderRegistry toolingRegistry,
-                @Path("isApplication") Boolean isApplication) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
-
-            // check if the target has been set.
-            TargetInfo targetInfo = androidBuilder.getTargetInfo()
-            if (targetInfo == null) {
-                sdkHandler.initTarget(
-                        androidExtension.getCompileSdkVersion(),
-                        androidExtension.buildToolsRevision,
-                        androidBuilder)
-            }
-
-            VariantManager variantManager = new VariantManager(
-                    project,
-                    androidBuilder,
-                    androidExtension,
-                    variantFactory,
-                    taskManager,
-                    instantiator)
-
-            signingConfigContainer.all { SigningConfig signingConfig ->
-                variantManager.addSigningConfig(signingConfig)
-            }
-            buildTypeContainer.all { BuildType buildType ->
-                variantManager.addBuildType(buildType)
-            }
-            productFlavorContainer.all { GroupableProductFlavor productFlavor ->
-                variantManager.addProductFlavor(productFlavor)
-            }
-
-            ModelBuilder modelBuilder = new ModelBuilder(
-                    androidBuilder, variantManager, taskManager,
-                    androidExtension, extraModelInfo, !isApplication);
-            toolingRegistry.register(modelBuilder);
-
-
-            def spec = androidSpec as DefaultAndroidComponentSpec
-            spec.extension = androidExtension
-            spec.variantManager = variantManager
-        }
-
-        @Mutate
-        void createVariantData(
-                CollectionBuilder<AndroidBinary> binaries,
-                AndroidComponentSpec spec) {
-            VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-            binaries.all {
-                DefaultAndroidBinary binary = it as DefaultAndroidBinary
-                binary.variantData =
-                        variantManager.createVariantData(binary.buildType, binary.productFlavors)
-                variantManager.getVariantDataList().add(binary.variantData);
-            }
-        }
-
-        @Mutate
-        void createLifeCycleTasks(
-                CollectionBuilder<Task> tasks,
-                TaskManager taskManager) {
-            taskManager.createTasksBeforeEvaluate(new TaskCollectionBuilderAdaptor(tasks))
-        }
-
-        @Mutate
-        void createAndroidTasks(
-                CollectionBuilder<Task> tasks,
-                AndroidComponentSpec androidSpec,
-                TaskManager taskManager,
-                SdkHandler sdkHandler,
-                Project project,
-                AndroidComponentModelSourceSet androidSources) {
-            DefaultAndroidComponentSpec spec = (DefaultAndroidComponentSpec) androidSpec
-
-            applyProjectSourceSet(spec, androidSources, spec.extension)
-
-            // setup SDK repositories.
-            for (File file : sdkHandler.sdkLoader.repositories) {
-                project.repositories.maven { MavenArtifactRepository repo ->
-                    repo.url = file.toURI()
-                }
-            }
-
-            // TODO: determine how to provide functionalities of variant API objects.
-        }
-
-        // TODO: Use @BinaryTasks after figuring how to configure non-binary specific tasks.
-        @Mutate
-        void createBinaryTasks(
-                CollectionBuilder<Task> tasks,
-                BinaryContainer binaries,
-                AndroidComponentSpec spec,
-                TaskManager taskManager) {
-
-            VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-            binaries.withType(AndroidBinary) { androidBinary ->
-                ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
-                        new Recorder.Block<Void>() {
-                            @Override
-                            public Void call() throws Exception {
-                                    DefaultAndroidBinary binary = androidBinary as DefaultAndroidBinary
-                                    variantManager.createTasksForVariantData(
-                                            new TaskCollectionBuilderAdaptor(tasks),
-                                            binary.variantData)
-                                return null;
-                            }
-                        });
-            }
-        }
-
-        /**
-         * Create tasks that must be created after other tasks for variants are created.
-         */
-        @Mutate
-        void createRemainingTasks(
-                CollectionBuilder<Task> tasks,
-                TaskManager taskManager,
-                AndroidComponentSpec spec) {
-            VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-
-            // create the test tasks.
-            taskManager.createTopLevelTestTasks (
-                    new TaskCollectionBuilderAdaptor(tasks),
-                    !variantManager.productFlavors.isEmpty() /*hasFlavors*/);
-        }
-
-        @Mutate
-        void createReportTasks(CollectionBuilder<Task> tasks, AndroidComponentSpec spec) {
-            VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-
-            tasks.create("androidDependencies", DependencyReportTask) { DependencyReportTask dependencyReportTask ->
-                dependencyReportTask.setDescription("Displays the Android dependencies of the project")
-                dependencyReportTask.setVariants(variantManager.variantDataList)
-                dependencyReportTask.setGroup("Android")
-            }
-
-            tasks.create("signingReport", SigningReportTask) { SigningReportTask signingReportTask ->
-                signingReportTask.setDescription("Displays the signing info for each variant")
-                signingReportTask.setVariants(variantManager.variantDataList)
-                signingReportTask.setGroup("Android")
-            }
-        }
-
-        private static void applyProjectSourceSet(
-                AndroidComponentSpec androidSpec,
-                AndroidComponentModelSourceSet sources,
-                BaseExtension baseExtension) {
-            DefaultAndroidComponentSpec spec = (DefaultAndroidComponentSpec)androidSpec
-            VariantManager variantManager = spec.variantManager
-            for (FunctionalSourceSet source : sources) {
-                String name = source.getName();
-                AndroidSourceSet androidSource = (
-                        name.equals(BuilderConstants.MAIN)
-                                ? baseExtension.sourceSets.getByName(baseExtension.getDefaultConfig().getName())
-                                : (name.equals(ANDROID_TEST.prefix)
-                                        ? baseExtension.sourceSets.getByName(ANDROID_TEST.getPrefix())
-                                        : findAndroidSourceSet(variantManager, name)))
-
-                if (androidSource == null) {
-                    continue;
-                }
-
-                convertSourceSet(androidSource.getResources(), source.findByName("resource")?.getSource())
-                convertSourceSet(androidSource.getJava(), source.findByName("java")?.getSource())
-                convertSourceSet(androidSource.getRes(), source.findByName("res")?.getSource())
-                convertSourceSet(androidSource.getAssets(), source.findByName("assets")?.getSource())
-                convertSourceSet(androidSource.getAidl(), source.findByName("aidl")?.getSource())
-                convertSourceSet(androidSource.getRenderscript(), source.findByName("renderscript")?.getSource())
-                convertSourceSet(androidSource.getJni(), source.findByName("jni")?.getSource())
-                convertSourceSet(androidSource.getJniLibs(), source.findByName("jniLibs")?.getSource())
-            }
-        }
-
-        private static convertSourceSet(
-                AndroidSourceDirectorySet androidDir,
-                @Nullable SourceDirectorySet dir) {
-            if (dir == null) {
-                return
-            }
-            androidDir.setSrcDirs(dir.getSrcDirs())
-            androidDir.include(dir.getIncludes())
-            androidDir.exclude(dir.getExcludes())
-        }
-
-        @Nullable
-        private static AndroidSourceSet findAndroidSourceSet(
-                VariantManager variantManager,
-                String name) {
-            BuildTypeData buildTypeData = variantManager.getBuildTypes().get(name)
-            if (buildTypeData != null) {
-                return buildTypeData.getSourceSet();
-            }
-
-            boolean isTest = name.startsWith(ANDROID_TEST.prefix)
-            name = name.replaceFirst(ANDROID_TEST.prefix, "")
-            ProductFlavorData productFlavorData = variantManager.getProductFlavors().get(name)
-            if (productFlavorData != null) {
-                return isTest ? productFlavorData.getTestSourceSet(ANDROID_TEST) : productFlavorData.getSourceSet();
-            }
-            return null;
-        }
-    }
-
-    /**
-     * Default Android LanguageRegistration.
-     *
-     * Allows default LanguageSourceSet to be create until specialized LanguageRegistration is
-     * created.
-     */
-    private static class AndroidSource implements LanguageTransform<AndroidLanguageSourceSet, AndroidObject> {
-        public String getName() {
-            return "main";
-        }
-
-        public Class<AndroidLanguageSourceSet> getSourceSetType() {
-            return AndroidLanguageSourceSet.class;
-        }
-
-        public Class<? extends AndroidLanguageSourceSet> getSourceSetImplementation() {
-            return AndroidLanguageSourceSet.class;
-        }
-
-        public Map<String, Class<?>> getBinaryTools() {
-            return Collections.emptyMap();
-        }
-
-        public Class<AndroidObject> getOutputType() {
-            return null;
-        }
-
-        public SourceTransformTaskConfig getTransformTask() {
-            return new SourceTransformTaskConfig() {
-                public String getTaskPrefix() {
-                    return "process";
-                }
-
-                public Class<? extends DefaultTask> getTaskType() {
-                    return DefaultTask;
-                }
-
-                public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
-                }
-            };
-        }
-
-        public boolean applyToBinary(BinarySpec binary) {
-            return false
-        }
-    }
-
-    private void createConfiguration(
-            @NonNull ConfigurationContainer configurations,
-            @NonNull String configurationName,
-            @NonNull String configurationDescription) {
-        Configuration configuration = configurations.findByName(configurationName)
-        if (configuration == null) {
-            configuration = configurations.create(configurationName)
-        }
-        configuration.setVisible(false);
-        configuration.setDescription(configurationDescription)
-    }
-
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.java
new file mode 100644
index 0000000..2144cd7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
+import static com.android.build.gradle.model.ModelConstants.ANDROID_BUILDER;
+import static com.android.build.gradle.model.ModelConstants.ANDROID_CONFIG_ADAPTOR;
+import static com.android.build.gradle.model.ModelConstants.EXTRA_MODEL_INFO;
+import static com.android.builder.core.BuilderConstants.DEBUG;
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidGradleOptions;
+import com.android.build.gradle.internal.AndroidConfigHelper;
+import com.android.build.gradle.internal.ExecutionConfigurationUtil;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LibraryCache;
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.NdkOptionsHelper;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.coverage.JacocoPlugin;
+import com.android.build.gradle.internal.process.GradleJavaProcessExecutor;
+import com.android.build.gradle.internal.process.GradleProcessExecutor;
+import com.android.build.gradle.internal.profile.RecordingBuildListener;
+import com.android.build.gradle.internal.tasks.DependencyReportTask;
+import com.android.build.gradle.internal.tasks.SigningReportTask;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ClassField;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.managed.NdkOptions;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.build.gradle.managed.SigningConfig;
+import com.android.build.gradle.managed.adaptor.AndroidConfigAdaptor;
+import com.android.build.gradle.managed.adaptor.BuildTypeAdaptor;
+import com.android.build.gradle.managed.adaptor.ProductFlavorAdaptor;
+import com.android.build.gradle.tasks.JillTask;
+import com.android.build.gradle.tasks.PreDex;
+import com.android.builder.Version;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.internal.compiler.JackConversionCache;
+import com.android.builder.internal.compiler.PreDexCache;
+import com.android.builder.profile.ProcessRecorderFactory;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.signing.DefaultSigningConfig;
+import com.android.ide.common.internal.ExecutorSingleton;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.signing.KeystoreHelper;
+import com.android.prefs.AndroidLocation;
+import com.android.utils.ILogger;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.Action;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.model.Defaults;
+import org.gradle.model.Model;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.ModelCreators;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import groovy.lang.Closure;
+
+public class BaseComponentModelPlugin implements Plugin<Project> {
+
+    private ToolingModelBuilderRegistry toolingRegistry;
+
+    private ModelRegistry modelRegistry;
+
+    @Inject
+    protected BaseComponentModelPlugin(ToolingModelBuilderRegistry toolingRegistry,
+            ModelRegistry modelRegistry) {
+        this.toolingRegistry = toolingRegistry;
+        this.modelRegistry = modelRegistry;
+    }
+
+    /**
+     * Replace BasePlugin's apply method for component model.
+     */
+    @Override
+    public void apply(Project project) {
+        ExecutionConfigurationUtil.setThreadPoolSize(project);
+        try {
+            List<Recorder.Property> propertyList = Lists.newArrayList(
+                    new Recorder.Property("plugin_version", Version.ANDROID_GRADLE_PLUGIN_VERSION),
+                    new Recorder.Property("next_gen_plugin", "true"),
+                    new Recorder.Property("gradle_version", project.getGradle().getGradleVersion())
+            );
+            String benchmarkName = AndroidGradleOptions.getBenchmarkName(project);
+            if (benchmarkName != null) {
+                propertyList.add(new Recorder.Property("benchmark_name", benchmarkName));
+            }
+            String benchmarkMode = AndroidGradleOptions.getBenchmarkMode(project);
+            if (benchmarkMode != null) {
+                propertyList.add(new Recorder.Property("benchmark_mode", benchmarkMode));
+            }
+
+            ProcessRecorderFactory.initialize(
+                    new LoggerWrapper(project.getLogger()),
+                    project.getRootProject()
+                            .file("profiler" + System.currentTimeMillis() + ".json"),
+                    propertyList);
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to initialize ProcessRecorderFactory");
+        }
+        project.getGradle().addListener(new RecordingBuildListener(ThreadRecorder.get()));
+
+        project.getPlugins().apply(AndroidComponentModelPlugin.class);
+        project.getPlugins().apply(JavaBasePlugin.class);
+        project.getPlugins().apply(JacocoPlugin.class);
+
+        // TODO: Create configurations for build types and flavors, or migrate to new dependency
+        // management if it's ready.
+        ConfigurationContainer configurations = project.getConfigurations();
+        createConfiguration(configurations, "compile", "Classpath for default sources.");
+        createConfiguration(configurations, "default-metadata", "Metadata for published APKs");
+        createConfiguration(configurations, "default-mapping", "Metadata for published APKs");
+
+        project.getPlugins().apply(NdkComponentModelPlugin.class);
+
+        // Remove this when our models no longer depends on Project.
+        modelRegistry.create(ModelCreators
+                .bridgedInstance(ModelReference.of("projectModel", Project.class), project)
+                .descriptor("Model of project.").build());
+
+        toolingRegistry.register(new ComponentModelBuilder(modelRegistry));
+
+        // Inserting the ToolingModelBuilderRegistry into the model so that it can be use to create
+        // TaskManager in child classes.
+        modelRegistry.create(ModelCreators.bridgedInstance(
+                ModelReference.of("toolingRegistry", ToolingModelBuilderRegistry.class),
+                toolingRegistry).descriptor("Tooling model builder model registry.").build());
+    }
+
+    private static void createConfiguration(@NonNull ConfigurationContainer configurations,
+            @NonNull String configurationName, @NonNull String configurationDescription) {
+        Configuration configuration = configurations.findByName(configurationName);
+        if (configuration == null) {
+            configuration = configurations.create(configurationName);
+        }
+
+        configuration.setVisible(false);
+        configuration.setDescription(configurationDescription);
+    }
+
+    @SuppressWarnings("MethodMayBeStatic")
+    public static class Rules extends RuleSource {
+
+        @Defaults
+        public void configureAndroidModel(
+                AndroidConfig androidModel,
+                ServiceRegistry serviceRegistry) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            AndroidConfigHelper.configure(androidModel, instantiator);
+
+            androidModel.getSigningConfigs().create(DEBUG, new Action<SigningConfig>() {
+                @Override
+                public void execute(SigningConfig signingConfig) {
+                    try {
+                        signingConfig.setStoreFile(KeystoreHelper.defaultDebugKeystoreLocation());
+                        signingConfig.setStorePassword(DefaultSigningConfig.DEFAULT_PASSWORD);
+                        signingConfig.setKeyAlias(DefaultSigningConfig.DEFAULT_ALIAS);
+                        signingConfig.setKeyPassword(DefaultSigningConfig.DEFAULT_PASSWORD);
+                        signingConfig.setStoreType(KeyStore.getDefaultType());
+                    } catch (AndroidLocation.AndroidLocationException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+        }
+
+        // com.android.build.gradle.AndroidConfig do not contain an NdkConfig.  Copy it to the
+        // defaultConfig for now.
+        @Defaults
+        public void copyNdkConfig(
+                @Path("android.defaultConfig.ndk") NdkOptions defaultNdkConfig,
+                @Path("android.ndk") NdkConfig pluginNdkConfig) {
+            NdkOptionsHelper.init(defaultNdkConfig);
+            NdkOptionsHelper.merge(defaultNdkConfig, pluginNdkConfig);
+        }
+
+       // TODO: Remove code duplicated from BasePlugin.
+        @Model(EXTRA_MODEL_INFO)
+        public ExtraModelInfo createExtraModelInfo(
+                Project project,
+                @NonNull @Path("isApplication") Boolean isApplication) {
+            return new ExtraModelInfo(project, isApplication);
+        }
+
+        @Model
+        public SdkHandler createSdkHandler(final Project project) {
+            final ILogger logger = new LoggerWrapper(project.getLogger());
+            final SdkHandler sdkHandler = new SdkHandler(project, logger);
+
+            // call back on execution. This is called after the whole build is done (not
+            // after the current project is done).
+            // This is will be called for each (android) projects though, so this should support
+            // being called 2+ times.
+            project.getGradle().buildFinished(new Closure<Object>(this, this) {
+                public void doCall(Object it) {
+                    ExecutorSingleton.shutdown();
+                    sdkHandler.unload();
+                    try {
+                        PreDexCache.getCache().clear(project.getRootProject()
+                                .file(String.valueOf(project.getRootProject().getBuildDir()) + "/"
+                                        + FD_INTERMEDIATES + "/dex-cache/cache.xml"), logger);
+                        JackConversionCache.getCache().clear(project.getRootProject()
+                                .file(String.valueOf(project.getRootProject().getBuildDir()) + "/"
+                                        + FD_INTERMEDIATES + "/jack-cache/cache.xml"), logger);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                    LibraryCache.getCache().unload();
+                }
+
+                public void doCall() {
+                    doCall(null);
+                }
+            });
+
+            project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+                public void doCall(TaskExecutionGraph taskGraph) {
+                    for (Task task : taskGraph.getAllTasks()) {
+                        if (task instanceof PreDex) {
+                            PreDexCache.getCache().load(project.getRootProject()
+                                    .file(String.valueOf(project.getRootProject().getBuildDir())
+                                            + "/" + FD_INTERMEDIATES + "/dex-cache/cache.xml"));
+                            break;
+                        } else if (task instanceof JillTask) {
+                            JackConversionCache.getCache().load(project.getRootProject()
+                                    .file(String.valueOf(project.getRootProject().getBuildDir())
+                                            + "/" + FD_INTERMEDIATES + "/jack-cache/cache.xml"));
+                            break;
+                        }
+                    }
+                }
+            });
+
+            // setup SDK repositories.
+            for (final File file : sdkHandler.getSdkLoader().getRepositories()) {
+                project.getRepositories().maven(new Action<MavenArtifactRepository>() {
+                    @Override
+                    public void execute(MavenArtifactRepository repo) {
+                        repo.setUrl(file.toURI());
+
+                    }
+                });
+            }
+            return sdkHandler;
+        }
+
+        @Model(ANDROID_BUILDER)
+        public AndroidBuilder createAndroidBuilder(Project project, ExtraModelInfo extraModelInfo) {
+            String creator = "Android Gradle";
+            ILogger logger = new LoggerWrapper(project.getLogger());
+
+            return new AndroidBuilder(project.equals(project.getRootProject()) ? project.getName()
+                    : project.getPath(), creator, new GradleProcessExecutor(project),
+                    new GradleJavaProcessExecutor(project),
+                    extraModelInfo, logger, project.getLogger().isEnabled(LogLevel.INFO));
+
+        }
+
+        @Mutate
+        public void initDebugBuildTypes(
+                @Path("android.buildTypes") ModelMap<BuildType> buildTypes,
+                @Path("android.signingConfigs") final ModelMap<SigningConfig> signingConfigs) {
+            buildTypes.beforeEach(new Action<BuildType>() {
+                @Override
+                public void execute(BuildType buildType) {
+                    initBuildType(buildType);
+                }
+            });
+
+            buildTypes.named(DEBUG, new Action<BuildType>() {
+                @Override
+                public void execute(BuildType buildType) {
+                    buildType.setSigningConfig(signingConfigs.get(DEBUG));
+                }
+            });
+        }
+
+        private static void initBuildType(@NonNull BuildType buildType) {
+            buildType.setDebuggable(false);
+            buildType.setTestCoverageEnabled(false);
+            buildType.setPseudoLocalesEnabled(false);
+            buildType.setRenderscriptDebuggable(false);
+            buildType.setRenderscriptOptimLevel(3);
+            buildType.setMinifyEnabled(false);
+            buildType.setZipAlignEnabled(true);
+            buildType.setEmbedMicroApp(true);
+            buildType.setUseJack(false);
+            buildType.setShrinkResources(false);
+            buildType.setProguardFiles(Sets.<File>newHashSet());
+            buildType.setConsumerProguardFiles(Sets.<File>newHashSet());
+            buildType.setTestProguardFiles(Sets.<File>newHashSet());
+        }
+
+        @Mutate
+        public void initDefaultConfig(@Path("android.defaultConfig") ProductFlavor defaultConfig) {
+            initProductFlavor(defaultConfig);
+        }
+
+        @Mutate
+        public void initProductFlavors(
+                @Path("android.productFlavors") final ModelMap<ProductFlavor> productFlavors) {
+            productFlavors.beforeEach(new Action<ProductFlavor>() {
+                @Override
+                public void execute(ProductFlavor productFlavor) {
+                    initProductFlavor(productFlavor);
+                }
+            });
+        }
+
+        private void initProductFlavor(ProductFlavor productFlavor) {
+            productFlavor.setProguardFiles(Sets.<File>newHashSet());
+            productFlavor.setConsumerProguardFiles(Sets.<File>newHashSet());
+            productFlavor.setTestProguardFiles(Sets.<File>newHashSet());
+            productFlavor.setResourceConfigurations(Sets.<String>newHashSet());
+            productFlavor.setJarJarRuleFiles(Lists.<File>newArrayList());
+            productFlavor.getBuildConfigFields().beforeEach(new Action<ClassField>() {
+                @Override
+                public void execute(ClassField classField) {
+                    classField.setAnnotations(Sets.<String>newHashSet());
+                }
+            });
+            productFlavor.getResValues().beforeEach(new Action<ClassField>() {
+                @Override
+                public void execute(ClassField classField) {
+                    classField.setAnnotations(Sets.<String>newHashSet());
+                }
+            });
+        }
+
+        @Mutate
+        public void addDefaultAndroidSourceSet(
+                @Path("android.sources") AndroidComponentModelSourceSet sources) {
+            sources.addDefaultSourceSet("resources", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("java", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("manifest", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("res", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("assets", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("aidl", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("renderscript", AndroidLanguageSourceSet.class);
+            sources.addDefaultSourceSet("jniLibs", AndroidLanguageSourceSet.class);
+
+            sources.all(new Action<FunctionalSourceSet>() {
+                @Override
+                public void execute(FunctionalSourceSet functionalSourceSet) {
+                    LanguageSourceSet manifest = functionalSourceSet.getByName("manifest");
+                    manifest.getSource().setIncludes(ImmutableList.of("AndroidManifest.xml"));
+                }
+            });
+        }
+
+        @Model(ANDROID_CONFIG_ADAPTOR)
+        public com.android.build.gradle.AndroidConfig createModelAdaptor(
+                ServiceRegistry serviceRegistry,
+                AndroidConfig androidExtension,
+                Project project,
+                @Path("isApplication") Boolean isApplication) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return new AndroidConfigAdaptor(androidExtension, AndroidConfigHelper
+                    .createSourceSetsContainer(project, instantiator, !isApplication));
+        }
+
+        @Mutate
+        public void createAndroidComponents(
+                ComponentSpecContainer androidSpecs,
+                ServiceRegistry serviceRegistry, AndroidConfig androidExtension,
+                com.android.build.gradle.AndroidConfig adaptedModel,
+                @Path("android.buildTypes") ModelMap<BuildType> buildTypes,
+                @Path("android.productFlavors") ModelMap<ProductFlavor> productFlavors,
+                @Path("android.signingConfigs") ModelMap<SigningConfig> signingConfigs,
+                VariantFactory variantFactory,
+                TaskManager taskManager,
+                Project project,
+                AndroidBuilder androidBuilder,
+                SdkHandler sdkHandler,
+                ExtraModelInfo extraModelInfo,
+                @Path("isApplication") Boolean isApplication) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+
+            // check if the target has been set.
+            TargetInfo targetInfo = androidBuilder.getTargetInfo();
+            if (targetInfo == null) {
+                sdkHandler.initTarget(androidExtension.getCompileSdkVersion(),
+                        androidExtension.getBuildToolsRevision(),
+                        androidExtension.getLibraryRequests(), androidBuilder);
+            }
+
+            VariantManager variantManager = new VariantManager(project, androidBuilder,
+                    adaptedModel, variantFactory, taskManager, instantiator);
+
+            for (BuildType buildType : buildTypes.values()) {
+                variantManager.addBuildType(new BuildTypeAdaptor(buildType));
+            }
+
+            for (ProductFlavor productFlavor : productFlavors.values()) {
+                variantManager.addProductFlavor(new ProductFlavorAdaptor(productFlavor));
+            }
+
+            DefaultAndroidComponentSpec spec =
+                    (DefaultAndroidComponentSpec) androidSpecs.get(COMPONENT_NAME);
+            spec.setExtension(androidExtension);
+            spec.setVariantManager(variantManager);
+        }
+
+        @Mutate
+        public void createVariantData(
+                ModelMap<AndroidBinary> binaries,
+                ModelMap<AndroidComponentSpec> specs,
+                TaskManager taskManager) {
+            final VariantManager variantManager =
+                    ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME)).getVariantManager();
+            binaries.afterEach(new Action<AndroidBinary>() {
+                @Override
+                public void execute(AndroidBinary androidBinary) {
+                    DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+                    List<ProductFlavorAdaptor> adaptedFlavors = Lists.newArrayList();
+                    for (ProductFlavor flavor : binary.getProductFlavors()) {
+                        adaptedFlavors.add(new ProductFlavorAdaptor(flavor));
+                    }
+                    binary.setVariantData(
+                            variantManager.createVariantData(
+                                    new BuildTypeAdaptor(binary.getBuildType()),
+                                    adaptedFlavors));
+                    variantManager.getVariantDataList().add(binary.getVariantData());
+                }
+            });
+        }
+
+        @Mutate
+        public void createLifeCycleTasks(ModelMap<Task> tasks, TaskManager taskManager) {
+            taskManager.createTasksBeforeEvaluate(new TaskModelMapAdaptor(tasks));
+        }
+
+        @Mutate
+        public void createAndroidTasks(
+                ModelMap<Task> tasks,
+                ModelMap<AndroidComponentSpec> androidSpecs,
+                TaskManager taskManager,
+                SdkHandler sdkHandler,
+                Project project, AndroidComponentModelSourceSet androidSources) {
+            // setup SDK repositories.
+            for (final File file : sdkHandler.getSdkLoader().getRepositories()) {
+                project.getRepositories().maven(new Action<MavenArtifactRepository>() {
+                    @Override
+                    public void execute(MavenArtifactRepository repo) {
+                        repo.setUrl(file.toURI());
+                    }
+                });
+            }
+            // TODO: determine how to provide functionalities of variant API objects.
+        }
+
+        // TODO: Use @BinaryTasks after figuring how to configure non-binary specific tasks.
+        @Mutate
+        public void createBinaryTasks(
+                final ModelMap<Task> tasks,
+                BinaryContainer binaries,
+                ModelMap<AndroidComponentSpec> specs,
+                TaskManager taskManager) {
+            final VariantManager variantManager =
+                    ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME)).getVariantManager();
+            binaries.withType(AndroidBinary.class, new Action<AndroidBinary>() {
+                @Override
+                public void execute(AndroidBinary androidBinary) {
+                    DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+                    variantManager.createTasksForVariantData(
+                            new TaskModelMapAdaptor(tasks),
+                            binary.getVariantData());
+                }
+            });
+        }
+
+        /**
+         * Create tasks that must be created after other tasks for variants are created.
+         */
+        @Mutate
+        public void createRemainingTasks(
+                ModelMap<Task> tasks,
+                TaskManager taskManager,
+                ModelMap<AndroidComponentSpec> spec) {
+            VariantManager variantManager =
+                    ((DefaultAndroidComponentSpec)spec.get(COMPONENT_NAME)).getVariantManager();
+
+            // create the test tasks.
+            taskManager.createTopLevelTestTasks(new TaskModelMapAdaptor(tasks),
+                    !variantManager.getProductFlavors().isEmpty());
+        }
+
+        @Mutate
+        public void createReportTasks(
+                ModelMap<Task> tasks,
+                ModelMap<AndroidComponentSpec> specs) {
+            final VariantManager variantManager =
+                    ((DefaultAndroidComponentSpec)specs.get(COMPONENT_NAME)).getVariantManager();
+
+            tasks.create("androidDependencies", DependencyReportTask.class,
+                    new Action<DependencyReportTask>() {
+                        @Override
+                        public void execute(DependencyReportTask dependencyReportTask) {
+                            dependencyReportTask.setDescription(
+                                    "Displays the Android dependencies of the project");
+                            dependencyReportTask.setVariants(variantManager.getVariantDataList());
+                            dependencyReportTask.setGroup("Android");
+                        }
+                    });
+
+            tasks.create("signingReport", SigningReportTask.class,
+                    new Action<SigningReportTask>() {
+                        @Override
+                        public void execute(SigningReportTask signingReportTask) {
+                            signingReportTask
+                                    .setDescription("Displays the signing info for each variant");
+                            signingReportTask.setVariants(variantManager.getVariantDataList());
+                            signingReportTask.setGroup("Android");
+
+                        }
+                    });
+        }
+
+        @Mutate
+        public void modifyAssembleTaskDescription(@Path("tasks.assemble") Task assembleTask) {
+            assembleTask.setDescription(
+                    "Assembles all variants of all applications and secondary packages.");
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentModelBuilder.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentModelBuilder.java
new file mode 100644
index 0000000..81256fd
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentModelBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import static com.android.build.gradle.model.ModelConstants.ANDROID_BUILDER;
+import static com.android.build.gradle.model.ModelConstants.ANDROID_CONFIG_ADAPTOR;
+import static com.android.build.gradle.model.ModelConstants.BINARIES;
+import static com.android.build.gradle.model.ModelConstants.COMPONENTS;
+import static com.android.build.gradle.model.ModelConstants.EXTRA_MODEL_INFO;
+import static com.android.build.gradle.model.ModelConstants.IS_APPLICATION;
+import static com.android.build.gradle.model.ModelConstants.NDK_HANDLER;
+import static com.android.build.gradle.model.ModelConstants.TASK_MANAGER;
+
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.model.ModelBuilder;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.model.AndroidProject;
+
+import org.gradle.api.Project;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.tooling.provider.model.ToolingModelBuilder;
+
+/**
+ * A ToolingModelBuilder for creating AndroidProject in the component model plugin.
+ *
+ * It retrieves models from ModelRegistry and uses ModelBuilder to create AndroidProject.
+ *
+ * This model builder uses Gradle's internal API as a public API for create tooling model from
+ * component model is not yet available.
+ */
+public class ComponentModelBuilder implements ToolingModelBuilder {
+
+    ModelBuilder modelBuilder;
+    ModelRegistry registry;
+
+
+    public ComponentModelBuilder(ModelRegistry registry) {
+        this.registry = registry;
+    }
+
+    @Override
+    public boolean canBuild(String modelName) {
+        return modelName.equals(AndroidProject.class.getName());
+    }
+
+    @Override
+    public Object buildAll(String modelName, Project project) {
+        if (modelBuilder == null) {
+            modelBuilder = createModelBuilder();
+        }
+        return modelBuilder.buildAll(modelName, project);
+    }
+
+    private ModelBuilder createModelBuilder() {
+        AndroidBuilder androidBuilder = registry.realize(
+                new ModelPath(ANDROID_BUILDER),
+                ModelType.of(AndroidBuilder.class));
+        DefaultAndroidComponentSpec componentSpec = (DefaultAndroidComponentSpec) registry.realize(
+                new ModelPath(COMPONENTS),
+                ModelType.of(ComponentSpecContainer.class))
+                        .get(AndroidComponentModelPlugin.COMPONENT_NAME);
+        VariantManager variantManager = componentSpec.getVariantManager();
+        TaskManager taskManager = registry.realize(
+                new ModelPath(TASK_MANAGER),
+                ModelType.of(TaskManager.class));
+        AndroidConfig extension = registry.realize(
+                new ModelPath(ANDROID_CONFIG_ADAPTOR),
+                ModelType.of(AndroidConfig.class));
+        ExtraModelInfo extraModelInfo = registry.realize(
+                new ModelPath(EXTRA_MODEL_INFO),
+                ModelType.of(ExtraModelInfo.class));
+        Boolean isApplication = registry.realize(
+                new ModelPath(IS_APPLICATION),
+                ModelType.of(Boolean.class));
+        NdkHandler ndkHandler = registry.realize(
+                new ModelPath(NDK_HANDLER),
+                ModelType.of(NdkHandler.class));
+        BinaryContainer binaries = registry.realize(
+                new ModelPath(BINARIES),
+                ModelType.of(BinaryContainer.class));
+
+        return new ModelBuilder(
+                androidBuilder, variantManager, taskManager,
+                extension, extraModelInfo, ndkHandler,
+                new ComponentNativeLibraryFactory(binaries, ndkHandler),
+                !isApplication);
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentNativeLibraryFactory.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentNativeLibraryFactory.java
new file mode 100644
index 0000000..5497b62
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentNativeLibraryFactory.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.model.NativeLibraryFactory;
+import com.android.build.gradle.internal.model.NativeLibraryImpl;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.ndk.internal.BinaryToolHelper;
+import com.android.builder.model.NativeLibrary;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import org.gradle.nativeplatform.NativeLibraryBinarySpec;
+import org.gradle.platform.base.BinaryContainer;
+
+import java.io.File;
+import java.util.Collections;
+
+/**
+ * Implementation of NativeLibraryFactory from in the component model plugin.
+ *
+ * The library extract information directly from the binaries.
+ */
+public class ComponentNativeLibraryFactory implements NativeLibraryFactory {
+
+    BinaryContainer binaries;
+
+    NdkHandler ndkHandler;
+
+    public ComponentNativeLibraryFactory(BinaryContainer binaries,
+            NdkHandler ndkHandler) {
+        this.binaries = binaries;
+        this.ndkHandler = ndkHandler;
+    }
+
+    @NonNull
+    @Override
+    public Optional<NativeLibrary> create(
+            @NonNull VariantScope scope,
+            @NonNull String toolchainName,
+            @NonNull final Abi abi) {
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+        DefaultAndroidBinary androidBinary =
+                (DefaultAndroidBinary) binaries.findByName(variantData.getName());
+
+        if (androidBinary == null) {
+            // Binaries are not created for test variants.
+            return Optional.absent();
+        }
+
+        @SuppressWarnings("ConstantConditions")
+        Optional<NativeLibraryBinarySpec> nativeBinary =
+                Iterables.tryFind(androidBinary.getNativeBinaries(),
+                        new Predicate<NativeLibraryBinarySpec>() {
+                            @Override
+                            public boolean apply(NativeLibraryBinarySpec binary) {
+                                return binary.getTargetPlatform().getName().equals(abi.getName());
+                            }
+                        });
+
+        if (!nativeBinary.isPresent()) {
+            // We don't have native binaries.
+            return Optional.absent();
+        }
+
+        CoreNdkOptions ndkConfig = variantData.getVariantConfiguration().getNdkConfig();
+        // The DSL currently do not support all options available in the model such as the
+        // include dirs and the defines.  Therefore, just pass an empty collection for now.
+        return Optional.<NativeLibrary>of(new NativeLibraryImpl(
+                ndkConfig.getModuleName(),
+                toolchainName,
+                abi.getName(),
+                Collections.<File>emptyList(),  /*cIncludeDirs*/
+                Collections.<File>emptyList(),  /*cppIncludeDirs*/
+                Collections.<File>emptyList(),  /*cSystemIncludeDirs*/
+                ndkHandler.getStlIncludes(ndkConfig.getStl(), abi),
+                Collections.<String>emptyList(),  /*cDefines*/
+                Collections.<String>emptyList(),  /*cppDefines*/
+                BinaryToolHelper.getCCompiler(nativeBinary.get()).getArgs(),
+                BinaryToolHelper.getCppCompiler(nativeBinary.get()).getArgs(),
+                ImmutableList.of(variantData.getScope().getNdkDebuggableLibraryFolders(abi))));
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java
index ce7531f..5f61693 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java
@@ -16,9 +16,12 @@
 
 package com.android.build.gradle.model;
 
-import com.android.build.gradle.api.GroupableProductFlavor;
+import com.android.build.gradle.internal.NdkOptionsHelper;
 import com.android.build.gradle.internal.variant.BaseVariantData;
-import com.android.builder.model.BuildType;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.managed.NdkOptions;
+import com.android.build.gradle.managed.ProductFlavor;
 import com.google.common.collect.Lists;
 
 import org.gradle.nativeplatform.NativeLibraryBinarySpec;
@@ -33,7 +36,9 @@
 
     private BuildType buildType;
 
-    private List<? extends GroupableProductFlavor> productFlavors;
+    private List<ProductFlavor> productFlavors;
+
+    private NdkConfig mergedNdkConfig = new NdkConfigImpl();
 
     private BaseVariantData variantData;
 
@@ -51,14 +56,18 @@
     }
 
     @Override
-    public List<? extends GroupableProductFlavor> getProductFlavors() {
+    public List<ProductFlavor> getProductFlavors() {
         return productFlavors;
     }
 
-    public void setProductFlavors(List<? extends GroupableProductFlavor> productFlavors) {
+    public void setProductFlavors(List<ProductFlavor> productFlavors) {
         this.productFlavors = productFlavors;
     }
 
+    public NdkConfig getMergedNdkConfig() {
+        return mergedNdkConfig;
+    }
+
     public BaseVariantData getVariantData() {
         return variantData;
     }
@@ -67,11 +76,33 @@
         this.variantData = variantData;
     }
 
-    public List<? extends NativeLibraryBinarySpec> getNativeBinaries() {
+    public List<NativeLibraryBinarySpec> getNativeBinaries() {
         return nativeBinaries;
     }
 
     public List<String> getTargetAbi() {
         return targetAbi;
     }
+
+    public void computeMergedNdk(
+            NdkConfig ndkConfig,
+            List<com.android.build.gradle.managed.ProductFlavor> flavors,
+            com.android.build.gradle.managed.BuildType buildType) {
+
+
+        if (ndkConfig != null) {
+            NdkOptionsHelper.merge(mergedNdkConfig, ndkConfig);
+        }
+
+        for (int i = flavors.size() - 1 ; i >= 0 ; i--) {
+            NdkOptions ndkOptions = flavors.get(i).getNdk();
+            if (ndkOptions != null) {
+                NdkOptionsHelper.merge(mergedNdkConfig, ndkOptions);
+            }
+        }
+
+        if (buildType.getNdk() != null) {
+            NdkOptionsHelper.merge(mergedNdkConfig, buildType.getNdk());
+        }
+    }
 }
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java
index d67a7e2..a9c198f 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java
@@ -16,11 +16,10 @@
 
 package com.android.build.gradle.model;
 
-import com.android.build.gradle.BaseExtension;
 import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.managed.AndroidConfig;
 import com.android.builder.model.SigningConfig;
 
-import org.gradle.nativeplatform.NativeLibrary;
 import org.gradle.nativeplatform.NativeLibrarySpec;
 import org.gradle.platform.base.component.BaseComponentSpec;
 
@@ -28,7 +27,7 @@
  * Implementation for Android component spec.
  */
 public class DefaultAndroidComponentSpec extends BaseComponentSpec implements AndroidComponentSpec{
-    BaseExtension extension;
+    AndroidConfig extension;
 
     VariantManager variantManager;
 
@@ -37,11 +36,11 @@
     NativeLibrarySpec nativeLibrary;
 
     @Override
-    public BaseExtension getExtension() {
+    public AndroidConfig getExtension() {
         return extension;
     }
 
-    public void setExtension(BaseExtension extension) {
+    public void setExtension(AndroidConfig extension) {
         this.extension = extension;
     }
 
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.groovy
deleted file mode 100644
index b24a025..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 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.model
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.internal.DependencyManager
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.variant.LibraryVariantFactory
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.builder.core.AndroidBuilder
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.model.Model
-import org.gradle.model.RuleSource
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-/**
- * Gradle component model plugin class for 'application' projects.
- */
-public class LibraryComponentModelPlugin implements Plugin<Project> {
-    @Override
-    void apply(Project project) {
-        project.plugins.apply(BaseComponentModelPlugin)
-        project.tasks.create("assembleDefault")
-        project.plugins.apply(AndroidComponentModelTestPlugin)
-    }
-
-    static class Rules extends RuleSource{
-
-        @Model
-        Boolean isApplication() {
-            return false
-        }
-
-        @Model
-        TaskManager createTaskManager(
-                BaseExtension androidExtension,
-                Project project,
-                AndroidBuilder androidBuilder,
-                SdkHandler sdkHandler,
-                ExtraModelInfo extraModelInfo,
-                ToolingModelBuilderRegistry toolingRegistry) {
-            DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo)
-
-            return new LibraryComponentTaskManager(
-                    project,
-                    androidBuilder,
-                    androidExtension,
-                    sdkHandler,
-                    dependencyManager,
-                    toolingRegistry)
-        }
-
-        @Model
-        VariantFactory createVariantFactory(
-                ServiceRegistry serviceRegistry,
-                AndroidBuilder androidBuilder,
-                BaseExtension extension) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class)
-            return new LibraryVariantFactory(
-                    instantiator,
-                    androidBuilder,
-                    (LibraryExtension) extension)
-        }
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.java
new file mode 100644
index 0000000..af05716
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 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.model;
+
+import static com.android.build.gradle.model.ModelConstants.IS_APPLICATION;
+import static com.android.build.gradle.model.ModelConstants.TASK_MANAGER;
+
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.variant.LibraryVariantFactory;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.RuleSource;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+/**
+ * Gradle component model plugin class for 'application' projects.
+ */
+public class LibraryComponentModelPlugin implements Plugin<Project> {
+    @Override
+    public void apply(Project project) {
+        project.getPluginManager().apply(BaseComponentModelPlugin.class);
+        project.getTasks().create("assembleDefault");
+        project.getPluginManager().apply(AndroidComponentModelTestPlugin.class);
+    }
+
+    @SuppressWarnings("MethodMayBeStatic")
+    public static class Rules extends RuleSource {
+
+        @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+        @Model(IS_APPLICATION)
+        public Boolean isApplication() {
+            return false;
+        }
+
+        @Model(TASK_MANAGER)
+        public TaskManager createTaskManager(
+                AndroidConfig androidExtension,
+                Project project,
+                AndroidBuilder androidBuilder,
+                SdkHandler sdkHandler,
+                ExtraModelInfo extraModelInfo,
+                ToolingModelBuilderRegistry toolingRegistry) {
+            DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo);
+
+            return new LibraryComponentTaskManager(
+                    project,
+                    androidBuilder,
+                    androidExtension,
+                    sdkHandler,
+                    dependencyManager,
+                    toolingRegistry);
+        }
+
+        @Model
+        public VariantFactory createVariantFactory(
+                ServiceRegistry serviceRegistry,
+                AndroidBuilder androidBuilder,
+                AndroidConfig extension) {
+            Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+            return new LibraryVariantFactory(instantiator, androidBuilder, extension);
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java
index 8a7f3e9..f1b79ec 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java
@@ -16,19 +16,19 @@
 
 package com.android.build.gradle.model;
 
-import com.android.build.gradle.BaseExtension;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
 import com.android.build.gradle.internal.DependencyManager;
 import com.android.build.gradle.internal.LibraryTaskManager;
 import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.scope.VariantScope;
 import com.android.build.gradle.internal.variant.BaseVariantData;
 import com.android.builder.core.AndroidBuilder;
 import com.google.common.collect.ImmutableList;
 
 import org.gradle.api.Project;
-import org.gradle.api.tasks.TaskContainer;
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
 
-import java.io.File;
 import java.util.Collection;
 
 /**
@@ -39,7 +39,7 @@
     public LibraryComponentTaskManager(
             Project project,
             AndroidBuilder androidBuilder,
-            BaseExtension extension,
+            AndroidConfig extension,
             SdkHandler sdkHandler,
             DependencyManager dependencyManager,
             ToolingModelBuilderRegistry toolingRegistry) {
@@ -54,9 +54,7 @@
     }
 
     @Override
-    protected Collection<File> getNdkOutputDirectories(BaseVariantData variantData) {
-        NdkComponentModelPlugin plugin = project.getPlugins().getPlugin(
-                NdkComponentModelPlugin.class);
-        return plugin.getOutputDirectories(variantData.getVariantConfiguration());
+    public void configureScopeForNdk(@NonNull VariantScope scope) {
+        NdkComponentModelPlugin.configureScopeForNdk(scope);
     }
 }
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ModelConstants.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ModelConstants.java
new file mode 100644
index 0000000..039c3c6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ModelConstants.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+/**
+ * Component model path names.
+ */
+public class ModelConstants {
+
+    public static final String ANDROID_BUILDER = "androidBuilder";
+
+    public static final String ANDROID_CONFIG_ADAPTOR = "androidConfigAdaptor";
+
+    public static final String BINARIES = "binaries";
+
+    public static final String COMPONENTS = "components";
+
+    public static final String EXTRA_MODEL_INFO = "extraModelInfo";
+
+    public static final String IS_APPLICATION = "isApplication";
+
+    public static final String NDK_HANDLER = "ndkHandler";
+
+    public static final String TASK_MANAGER = "taskManager";
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.groovy
deleted file mode 100644
index 409b8b1..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.groovy
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2014 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.model
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.ProductFlavorCombo
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.ndk.internal.NdkConfiguration
-import com.android.build.gradle.ndk.internal.NdkExtensionConvention
-import com.android.build.gradle.ndk.internal.NdkHandler
-import com.android.build.gradle.ndk.internal.ToolchainConfiguration
-import com.android.builder.core.VariantConfiguration
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.internal.project.ProjectIdentifier
-import org.gradle.api.tasks.TaskContainer
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.model.Finalize
-import org.gradle.model.Model
-import org.gradle.model.Mutate
-import org.gradle.model.Path
-import org.gradle.model.RuleSource
-import org.gradle.nativeplatform.BuildTypeContainer
-import org.gradle.nativeplatform.FlavorContainer
-import org.gradle.nativeplatform.NativeLibraryBinarySpec
-import org.gradle.nativeplatform.NativeLibrarySpec
-import org.gradle.nativeplatform.SharedLibraryBinarySpec
-import org.gradle.nativeplatform.StaticLibraryBinary
-import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
-import org.gradle.platform.base.BinaryContainer
-import org.gradle.platform.base.BinarySpec
-import org.gradle.platform.base.ComponentSpecContainer
-import org.gradle.platform.base.PlatformContainer
-
-/**
- * Plugin for Android NDK applications.
- */
-class NdkComponentModelPlugin implements Plugin<Project> {
-    private Project project
-
-    void apply(Project project) {
-        this.project = project
-
-        project.apply plugin: AndroidComponentModelPlugin
-
-        project.apply plugin: 'c'
-        project.apply plugin: 'cpp'
-
-        // Remove static library tasks from assemble
-        project.binaries.withType(StaticLibraryBinary) {
-            it.buildable = false
-        }
-    }
-
-    static class Rules extends RuleSource {
-        @Mutate
-        void configureAndroidModel(
-                AndroidModel androidModel,
-                @Path("androidNdk") NdkExtension ndk) {
-            androidModel.ndk = ndk
-        }
-
-        @Model("androidNdk")
-        NdkExtension createAndroidNdk(ServiceRegistry serviceRegistry) {
-            Instantiator instantiator = serviceRegistry.get(Instantiator.class)
-            return instantiator.newInstance(NdkExtension)
-        }
-
-        @Mutate
-        void addDefaultNativeSourceSet(AndroidComponentModelSourceSet sources) {
-            sources.addDefaultSourceSet("c", CSourceSet.class);
-            sources.addDefaultSourceSet("cpp", CppSourceSet.class);
-        }
-
-        @Model
-        NdkHandler ndkHandler(ProjectIdentifier projectId, NdkExtension extension) {
-            while (projectId.parentIdentifier != null) {
-                projectId = projectId.parentIdentifier
-            }
-            return new NdkHandler(projectId.projectDir, extension)
-        }
-
-        @Finalize
-        void setDefaultNdkExtensionValue(NdkExtension extension) {
-            NdkExtensionConvention.setExtensionDefault(extension)
-        }
-
-        @Mutate
-        void createAndroidPlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
-            // Create android platforms.
-            ToolchainConfiguration.configurePlatforms(platforms, ndkHandler)
-        }
-
-        @Mutate
-        void createToolchains(
-                NativeToolChainRegistry toolchains,
-                NdkExtension ndkExtension,
-                NdkHandler ndkHandler) {
-            // Create toolchain for each ABI.
-            ToolchainConfiguration.configureToolchain(
-                    toolchains,
-                    ndkExtension.getToolchain(),
-                    ndkExtension.getToolchainVersion(),
-                    ndkHandler)
-        }
-
-        @Mutate
-        void createNativeBuildTypes(
-                BuildTypeContainer nativeBuildTypes,
-                @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> androidBuildTypes) {
-            for (def buildType : androidBuildTypes) {
-                nativeBuildTypes.maybeCreate(buildType.name)
-            }
-        }
-
-        @Mutate
-        void createNativeFlavors(
-                FlavorContainer nativeFlavors,
-                List<ProductFlavorCombo> androidFlavorGroups) {
-            for (def group : androidFlavorGroups) {
-                nativeFlavors.maybeCreate(group.name)
-            }
-        }
-
-        @Mutate
-        void createNativeLibrary(
-                ComponentSpecContainer specs,
-                @Path("android.ndk") NdkExtension extension,
-                NdkHandler ndkHandler,
-                @Path("android.sources") AndroidComponentModelSourceSet sources,
-                @Path("buildDir") File buildDir) {
-            if (!extension.moduleName.isEmpty()) {
-                NativeLibrarySpec library = specs.create(extension.moduleName, NativeLibrarySpec)
-                specs.withType(DefaultAndroidComponentSpec) { androidSpec ->
-                    androidSpec.nativeLibrary = library
-                    NdkConfiguration.configureProperties(
-                            library, sources, buildDir, extension, ndkHandler)
-                }
-            }
-        }
-        @Mutate
-        void createAdditionalTasksForNatives(
-                TaskContainer tasks,
-                ComponentSpecContainer specs,
-                NdkExtension extension,
-                NdkHandler ndkHandler,
-                @Path("buildDir") File buildDir) {
-            specs.withType(DefaultAndroidComponentSpec) { androidSpec ->
-                if (androidSpec.nativeLibrary != null) {
-                    androidSpec.nativeLibrary.binaries.withType(SharedLibraryBinarySpec) { binary ->
-                        NdkConfiguration.createTasks(
-                                tasks, binary, buildDir, extension, ndkHandler)
-                    }
-                }
-            }
-        }
-
-        @Mutate
-        void attachNativeBinaryToAndroid(
-                BinaryContainer binaries,
-                ComponentSpecContainer specs,
-                AndroidComponentSpec androidSpec,
-                @Path("android.ndk") NdkExtension extension) {
-            if (!extension.moduleName.isEmpty()) {
-                NativeLibrarySpec library =
-                        specs.withType(NativeLibrarySpec).getByName(extension.moduleName);
-                binaries.withType(DefaultAndroidBinary).each { binary ->
-                    def nativeBinaries = getNativeBinaries(library, binary.buildType, binary.productFlavors)
-                    binary.getNativeBinaries().addAll(nativeBinaries)
-                }
-            }
-        }
-
-        @Finalize
-        void attachNativeTasksToAssembleTasks(BinaryContainer binaries) {
-            binaries.withType(DefaultAndroidBinary) { binary ->
-                if (binary.targetAbi.isEmpty())  {
-                    binary.builtBy(binary.nativeBinaries)
-                } else {
-                    for (NativeLibraryBinarySpec nativeBinary : binary.nativeBinaries) {
-                        if (binary.targetAbi.contains(nativeBinary.targetPlatform.name)) {
-                            binary.builtBy(nativeBinary)
-                        }
-                    }
-                }
-            }
-        }
-
-        /**
-         * Remove unintended tasks created by Gradle native plugin from task list.
-         *
-         * Gradle native plugins creates static library tasks automatically.  This method removes them
-         * to avoid cluttering the task list.
-         */
-        @Mutate
-        void hideNativeTasks(TaskContainer tasks, BinaryContainer binaries) {
-            // Gradle do not support a way to remove created tasks.  The best workaround is to clear the
-            // group of the task and have another task depends on it.  Therefore, we have to create
-            // a dummy task to depend on all the tasks that we do not want to show up on the task
-            // list. The dummy task dependsOn itself, effectively making it non-executable and
-            // invisible unless the --all option is use.
-            Task nonExecutableTask = tasks.create("nonExecutableTask")
-            nonExecutableTask.dependsOn nonExecutableTask
-            nonExecutableTask.description =
-                    "Dummy task to hide other unwanted tasks in the task list."
-
-            binaries.withType(NativeLibraryBinarySpec) { binary ->
-                Task buildTask = binary.getBuildTask()
-                nonExecutableTask.dependsOn buildTask
-                buildTask.group = null
-            }
-        }
-    }
-
-    private static Collection<SharedLibraryBinarySpec> getNativeBinaries(
-            NativeLibrarySpec library,
-            com.android.builder.model.BuildType buildType,
-            List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavors) {
-        ProductFlavorCombo flavorGroup = new ProductFlavorCombo(productFlavors);
-        library.binaries.withType(SharedLibraryBinarySpec).matching { binary ->
-            (binary.buildType.name.equals(buildType.name)
-                    && ((productFlavors.isEmpty() && binary.flavor.name.equals("default"))
-                        || binary.flavor.name.equals(flavorGroup.name)))
-        }
-    }
-
-    /**
-     * Return library binaries for a VariantConfiguration.
-     */
-    public Collection<BinarySpec> getBinaries(VariantConfiguration variantConfig) {
-        if (variantConfig.getType().isForTesting()) {
-            // Do not return binaries for test variants as test source set is not supported at the
-            // moment.
-            return []
-        }
-
-        project.binaries.withType(SharedLibraryBinarySpec).matching { binary ->
-            (binary.buildType.name.equals(variantConfig.getBuildType().getName())
-                    && (binary.flavor.name.equals(variantConfig.getFlavorName())
-                            || (binary.flavor.name.equals("default")
-                                    && variantConfig.getFlavorName().isEmpty()))
-                    && (variantConfig.getNdkConfig().getAbiFilters() == null
-                            || variantConfig.getNdkConfig().getAbiFilters().contains(
-                                    binary.targetPlatform.name)))
-        }
-    }
-
-    /**
-     * Return the output directory of the native binary tasks for a VariantConfiguration.
-     */
-    public Collection<File> getOutputDirectories(VariantConfiguration variantConfig) {
-        // Return the parent's parent directory of the binaries' output.
-        // A binary's output file is set to something in the form of
-        // "/path/to/lib/platformName/libmodulename.so".  We want to return "/path/to/lib".
-        (getBinaries(variantConfig)
-                *.getPrimaryOutput()
-                *.getParentFile()
-                *.getParentFile()
-                .unique())
-    }
-}
-
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.java
new file mode 100644
index 0000000..a727529
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2014 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.model;
+
+import static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.NdkOptionsHelper;
+import com.android.build.gradle.internal.ProductFlavorCombo;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.build.gradle.ndk.internal.NdkConfiguration;
+import com.android.build.gradle.ndk.internal.NdkExtensionConvention;
+import com.android.build.gradle.ndk.internal.NdkNamingScheme;
+import com.android.build.gradle.ndk.internal.ToolchainConfiguration;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.VariantConfiguration;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+import org.gradle.api.Action;
+import org.gradle.api.BuildableModelElement;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.language.c.plugins.CPlugin;
+import org.gradle.language.cpp.plugins.CppPlugin;
+import org.gradle.model.Defaults;
+import org.gradle.model.Finalize;
+import org.gradle.model.Model;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.Validate;
+import org.gradle.nativeplatform.BuildTypeContainer;
+import org.gradle.nativeplatform.FlavorContainer;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeLibraryBinarySpec;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.platform.base.PlatformContainer;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Plugin for Android NDK applications.
+ */
+public class NdkComponentModelPlugin implements Plugin<Project> {
+    private Project project;
+
+    @Override
+    public void apply(Project project) {
+        this.project = project;
+
+        project.getPluginManager().apply(AndroidComponentModelPlugin.class);
+        project.getPluginManager().apply(CPlugin.class);
+        project.getPluginManager().apply(CppPlugin.class);
+    }
+
+    @SuppressWarnings({"MethodMayBeStatic", "unused"})
+    public static class Rules extends RuleSource {
+
+        @Mutate
+        public void initializeNdkConfig(@Path("android.ndk") NdkConfig ndk) {
+            NdkOptionsHelper.init(ndk);
+            ndk.setModuleName("");
+            ndk.setToolchain("");
+            ndk.setToolchainVersion("");
+            ndk.setStl("");
+            ndk.setRenderscriptNdkMode(false);
+        }
+
+        @Finalize
+        public void setDefaultNdkExtensionValue(@Path("android.ndk") NdkConfig ndkConfig) {
+            NdkExtensionConvention.setExtensionDefault(ndkConfig);
+        }
+
+        @Validate
+        public void checkNdkDir(NdkHandler ndkHandler, @Path("android.ndk") NdkConfig ndkConfig) {
+            if (!ndkConfig.getModuleName().isEmpty() && !ndkHandler.isNdkDirConfigured()) {
+                throw new InvalidUserDataException(
+                        "NDK location not found. Define location with ndk.dir in the "
+                                + "local.properties file or with an ANDROID_NDK_HOME environment "
+                                + "variable.");
+            }
+            if (ndkHandler.isNdkDirConfigured()) {
+                if (!ndkHandler.getNdkDirectory().exists()) {
+                    throw new InvalidUserDataException(
+                            "Specified NDK location does not exists.  Please ensure ndk.dir in "
+                                    + "local.properties file or ANDROID_NDK_HOME is configured "
+                                    + "correctly.");
+
+                }
+            }
+        }
+
+        @Mutate
+        public void addDefaultNativeSourceSet(
+                @Path("android.sources") AndroidComponentModelSourceSet sources) {
+            sources.addDefaultSourceSet("jni", AndroidLanguageSourceSet.class);
+        }
+
+        @Model(ModelConstants.NDK_HANDLER)
+        public NdkHandler ndkHandler(
+                ProjectIdentifier projectId,
+                @Path("android.compileSdkVersion") String compileSdkVersion,
+                @Path("android.ndk") NdkConfig ndkConfig) {
+            while (projectId.getParentIdentifier() != null) {
+                projectId = projectId.getParentIdentifier();
+            }
+
+            return new NdkHandler(projectId.getProjectDir(), compileSdkVersion,
+                    ndkConfig.getToolchain(), ndkConfig.getToolchainVersion());
+        }
+
+        @Defaults
+        public void initBuildTypeNdk(@Path("android.buildTypes") ModelMap<BuildType> buildTypes) {
+            buildTypes.beforeEach(new Action<BuildType>() {
+                @Override
+                public void execute(BuildType buildType) {
+                    NdkOptionsHelper.init(buildType.getNdk());
+                }
+            });
+
+            buildTypes.named(
+                    BuilderConstants.DEBUG,
+                    new Action<BuildType>() {
+                        @Override
+                        public void execute(BuildType buildType) {
+                            if (buildType.getNdk().getDebuggable() == null) {
+                                buildType.getNdk().setDebuggable(true);
+                            }
+                        }
+                    });
+        }
+
+        @Defaults
+        public void initProductFlavorNdk(
+                @Path("android.productFlavors") ModelMap<ProductFlavor> productFlavors) {
+            productFlavors.beforeEach(new Action<ProductFlavor>() {
+                @Override
+                public void execute(ProductFlavor productFlavor) {
+                    NdkOptionsHelper.init(productFlavor.getNdk());
+                }
+            });
+        }
+
+        @Mutate
+        public void createAndroidPlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
+            if (!ndkHandler.isNdkDirConfigured()) {
+                return;
+            }
+            // Create android platforms.
+            ToolchainConfiguration.configurePlatforms(platforms, ndkHandler);
+        }
+
+        @Mutate
+        public void createToolchains(
+                NativeToolChainRegistry toolchainRegistry,
+                @Path("android.ndk") NdkConfig ndkConfig,
+                NdkHandler ndkHandler) {
+            if (!ndkHandler.isNdkDirConfigured()) {
+                return;
+            }
+            // Create toolchain for each ABI.
+            ToolchainConfiguration.configureToolchain(
+                    toolchainRegistry,
+                    ndkConfig.getToolchain(),
+                    ndkHandler);
+        }
+
+        @Mutate
+        public void createNativeBuildTypes(BuildTypeContainer nativeBuildTypes,
+                @Path("android.buildTypes") ModelMap<BuildType> androidBuildTypes) {
+            for (BuildType buildType : androidBuildTypes.values()) {
+                nativeBuildTypes.maybeCreate(buildType.getName());
+            }
+        }
+
+        @Mutate
+        public void createNativeFlavors(FlavorContainer nativeFlavors,
+                List<ProductFlavorCombo<ProductFlavor>> androidFlavorGroups) {
+            if (androidFlavorGroups.isEmpty()) {
+                // Create empty native flavor to override Gradle's default name.
+                nativeFlavors.maybeCreate("");
+            } else {
+                for (ProductFlavorCombo group : androidFlavorGroups) {
+                    nativeFlavors.maybeCreate(group.getName());
+                }
+            }
+        }
+
+        @Mutate
+        public void createNativeLibrary(
+                final ComponentSpecContainer specs,
+                @Path("android.ndk") final NdkConfig ndkConfig,
+                final NdkHandler ndkHandler,
+                @Path("android.sources") final AndroidComponentModelSourceSet sources,
+                @Path("buildDir") final File buildDir) {
+            if (!ndkHandler.isNdkDirConfigured()) {
+                return;
+            }
+            if (!ndkConfig.getModuleName().isEmpty()) {
+                specs.create(
+                        ndkConfig.getModuleName(),
+                        NativeLibrarySpec.class,
+                        new Action<NativeLibrarySpec>() {
+                            @Override
+                            public void execute(final NativeLibrarySpec nativeLib) {
+                                ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME))
+                                        .setNativeLibrary(nativeLib);
+                                NdkConfiguration.configureProperties(
+                                        nativeLib,
+                                        sources,
+                                        buildDir,
+                                        ndkHandler);
+                            }
+                        });
+                DefaultAndroidComponentSpec androidSpecs =
+                        (DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME);
+                androidSpecs.setNativeLibrary(
+                        (NativeLibrarySpec) specs.get(ndkConfig.getModuleName()));
+            }
+        }
+
+        @Mutate
+        public void createAdditionalTasksForNatives(
+                final ModelMap<Task> tasks,
+                ModelMap<AndroidComponentSpec> specs,
+                @Path("android.ndk") final NdkConfig ndkConfig,
+                final NdkHandler ndkHandler,
+                BinaryContainer binaries,
+                @Path("buildDir") final File buildDir) {
+            if (!ndkHandler.isNdkDirConfigured()) {
+                return;
+            }
+            final DefaultAndroidComponentSpec androidSpec =
+                    (DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME);
+            if (androidSpec.getNativeLibrary() != null) {
+                binaries.withType(DefaultAndroidBinary.class, new Action<DefaultAndroidBinary>() {
+                    @Override
+                    public void execute(DefaultAndroidBinary binary) {
+                        for (NativeBinarySpec nativeBinary : binary.getNativeBinaries()) {
+                            NdkConfiguration.createTasks(
+                                    tasks,
+                                    (SharedLibraryBinarySpec) nativeBinary,
+                                    buildDir,
+                                    binary.getMergedNdkConfig(),
+                                    ndkHandler);
+                        }
+                    }
+                });
+            }
+        }
+
+        @Mutate
+        public void configureNativeBinary(
+                BinaryContainer binaries,
+                ComponentSpecContainer specs,
+                @Path("android.ndk") final NdkConfig ndkConfig,
+                @Path("buildDir") final File buildDir,
+                final NdkHandler ndkHandler) {
+            if (!ndkConfig.getModuleName().isEmpty()) {
+                final NativeLibrarySpec library = specs.withType(NativeLibrarySpec.class)
+                        .get(ndkConfig.getModuleName());
+                binaries.withType(
+                        DefaultAndroidBinary.class,
+                        new Action<DefaultAndroidBinary>() {
+                            @Override
+                            public void execute(DefaultAndroidBinary binary) {
+                                binary.computeMergedNdk(
+                                        ndkConfig,
+                                        binary.getProductFlavors(),
+                                        binary.getBuildType());
+
+                                Collection<SharedLibraryBinarySpec> nativeBinaries =
+                                        getNativeBinaries(
+                                                library,
+                                                binary.getBuildType(),
+                                                binary.getProductFlavors());
+                                for (SharedLibraryBinarySpec nativeBin : nativeBinaries) {
+                                    if (binary.getMergedNdkConfig().getAbiFilters().isEmpty() ||
+                                            binary.getMergedNdkConfig().getAbiFilters().contains(
+                                                    nativeBin.getTargetPlatform().getName())) {
+                                        NdkConfiguration.configureBinary(
+                                                nativeBin,
+                                                buildDir,
+                                                binary.getMergedNdkConfig(),
+                                                ndkHandler);
+                                        binary.getNativeBinaries().add(nativeBin);
+                                    }
+                                }
+                            }
+                        });
+            }
+        }
+
+        @Finalize
+        public void attachNativeTasksToAndroidBinary(ModelMap<AndroidBinary> binaries) {
+            binaries.afterEach(new Action<AndroidBinary>() {
+                @Override
+                public void execute(AndroidBinary androidBinary) {
+                    DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+                    for (NativeLibraryBinarySpec nativeBinary : binary.getNativeBinaries()) {
+                        if (binary.getTargetAbi().isEmpty() || binary.getTargetAbi().contains(
+                                nativeBinary.getTargetPlatform().getName())) {
+                            binary.getBuildTask().dependsOn(NdkNamingScheme.getNdkBuildTaskName(nativeBinary));
+                        }
+                    }
+                }
+            });
+        }
+
+        @Mutate
+        public void removeNativeBinaryFromAssembleTask(ModelMap<AndroidComponentSpec> components) {
+            // Setting each native binary to not buildable to prevent the native tasks to be
+            // automatically added to the "assemble" task.
+            components.afterEach(new Action<AndroidComponentSpec>() {
+                @Override
+                public void execute(AndroidComponentSpec spec) {
+                    NativeLibrarySpec nativeLibrary =
+                            ((DefaultAndroidComponentSpec)spec).getNativeLibrary();
+                    if (nativeLibrary != null) {
+                        nativeLibrary.getBinaries().afterEach(
+                                new Action<BinarySpec>() {
+                                    @Override
+                                    public void execute(BinarySpec binary) {
+                                        ((BaseBinarySpec) binary).setBuildable(false);
+                                    }
+                                });
+                    }
+                }
+            });
+        }
+
+        /**
+         * Remove unintended tasks created by Gradle native plugin from task list.
+         *
+         * Gradle native plugins creates static library tasks automatically.  This method removes
+         * them to avoid cluttering the task list.
+         */
+        @Mutate
+        public void hideNativeTasks(TaskContainer tasks, BinaryContainer binaries) {
+            // Gradle do not support a way to remove created tasks.  The best workaround is to clear the
+            // group of the task and have another task depends on it.  Therefore, we have to create
+            // a dummy task to depend on all the tasks that we do not want to show up on the task
+            // list. The dummy task dependsOn itself, effectively making it non-executable and
+            // invisible unless the --all option is use.
+            final Task nonExecutableTask = tasks.create("nonExecutableTask");
+            nonExecutableTask.dependsOn(nonExecutableTask);
+            nonExecutableTask
+                    .setDescription("Dummy task to hide other unwanted tasks in the task list.");
+
+            binaries.withType(NativeLibraryBinarySpec.class, new Action<NativeLibraryBinarySpec>() {
+                @Override
+                public void execute(NativeLibraryBinarySpec binary) {
+                    Task buildTask = binary.getBuildTask();
+                    nonExecutableTask.dependsOn(buildTask);
+                    buildTask.setGroup(null);
+                }
+            });
+        }
+    }
+
+
+    public static void configureScopeForNdk(VariantScope scope) {
+        VariantConfiguration config = scope.getVariantConfiguration();
+        ImmutableSet.Builder<File> builder = ImmutableSet.builder();
+        for (Abi abi : NdkHandler.getAbiList()) {
+            scope.addNdkDebuggableLibraryFolders(
+                    abi,
+                    new File(
+                            scope.getGlobalScope().getBuildDir(),
+                            NdkNamingScheme.getDebugLibraryDirectoryName(
+                                    config.getBuildType().getName(),
+                                    config.getFlavorName(),
+                                    abi.getName())));
+
+            // Return the parent directory of the binaries' output.
+            // If output directory is "/path/to/lib/platformName".  We want to return
+            // "/path/to/lib".
+            builder.add(new File(
+                    scope.getGlobalScope().getBuildDir(),
+                    NdkNamingScheme.getOutputDirectoryName(
+                            config.getBuildType().getName(),
+                            config.getFlavorName(),
+                            abi.getName())).getParentFile());
+        }
+        scope.setNdkSoFolder(builder.build());
+    }
+
+
+    private static Collection<SharedLibraryBinarySpec> getNativeBinaries(
+            NativeLibrarySpec library,
+            final BuildType buildType,
+            final List<ProductFlavor> productFlavors) {
+        final ProductFlavorCombo<ProductFlavor> flavorGroup =
+                new ProductFlavorCombo<ProductFlavor>(productFlavors);
+        return ImmutableList.copyOf(Iterables.filter(
+                library.getBinaries().withType(SharedLibraryBinarySpec.class).values(),
+                new Predicate<SharedLibraryBinarySpec>() {
+                    @Override
+                    public boolean apply(SharedLibraryBinarySpec binary) {
+                        return binary.getBuildType().getName().equals(buildType.getName())
+                                && (binary.getFlavor().getName().equals(flavorGroup.getName())
+                                || (productFlavors.isEmpty()
+                                && binary.getFlavor().getName().equals("default")));
+                    }
+                }));
+    }
+
+    /**
+     * Return library binaries for a VariantConfiguration.
+     */
+    public Collection<? extends BuildableModelElement> getBinaries(final VariantConfiguration variantConfig) {
+        if (variantConfig.getType().isForTesting()) {
+            // Do not return binaries for test variants as test source set is not supported at the
+            // moment.
+            return Collections.emptyList();
+        }
+        BinaryContainer binaries = (BinaryContainer) project.getExtensions().getByName("binaries");
+        return binaries.withType(AndroidBinary.class).matching(
+                new Spec<AndroidBinary>() {
+                    @Override
+                    public boolean isSatisfiedBy(AndroidBinary binary) {
+                        return (binary.getName().equals(variantConfig.getFullName()));
+                    }
+                }
+        );
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkConfigImpl.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkConfigImpl.java
new file mode 100644
index 0000000..1d37a60
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkConfigImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.managed.NdkConfig;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Implementation of NdkConfig.
+ * Used in AndroidBinary, which is currently not a Managed type.
+ */
+public class NdkConfigImpl implements NdkConfig {
+
+    String moduleName;
+
+    String toolchain;
+
+    String toolchainVersion;
+
+    Set<String> abiFilters = Sets.newHashSet();
+
+    List<String> cFlags = Lists.newArrayList();
+
+    List<String> cppFlags = Lists.newArrayList();
+
+    List<String> ldFlags = Lists.newArrayList();
+
+    List<String> ldLibs = Lists.newArrayList();
+
+    String stl;
+
+    Boolean isDebuggable;
+
+    Boolean renderscriptNdkMode;
+
+    @Override
+    public String getModuleName() {
+        return moduleName;
+    }
+
+    @Override
+    public void setModuleName(@NonNull String moduleName) {
+        this.moduleName = moduleName;
+    }
+
+    @Override
+    public String getToolchain() {
+        return toolchain;
+    }
+
+    @Override
+    public void setToolchain(@NonNull String toolchain) {
+        this.toolchain = toolchain;
+    }
+
+    @Override
+    public String getToolchainVersion() {
+        return toolchainVersion;
+    }
+
+    @Override
+    public void setToolchainVersion(@NonNull String toolchainVersion) {
+        this.toolchainVersion = toolchainVersion;
+    }
+
+    @Override
+    public Set<String> getAbiFilters() {
+        return abiFilters;
+    }
+
+    @Override
+    public void setAbiFilters(@NonNull Set<String> abiFilters) {
+        throw new UnsupportedOperationException("Field should not be set.");
+    }
+
+    @Override
+    public List<String> getCFlags() {
+        return cFlags;
+    }
+
+    @Override
+    public void setCFlags(@NonNull List<String> cFlags) {
+        throw new UnsupportedOperationException("Field should not be set.");
+    }
+
+    @Override
+    public List<String> getCppFlags() {
+        return cppFlags;
+    }
+
+    @Override
+    public void setCppFlags(@NonNull List<String> cppFlags) {
+        throw new UnsupportedOperationException("Field should not be set.");
+    }
+
+    @Override
+    public List<String> getLdFlags() {
+        return ldFlags;
+    }
+
+    @Override
+    public void setLdFlags(@NonNull List<String> ldFlags) {
+        throw new UnsupportedOperationException("Field should not be set.");
+    }
+
+    @Override
+    public List<String> getLdLibs() {
+        return ldLibs;
+    }
+
+    @Override
+    public void setLdLibs(@NonNull List<String> ldLibs) {
+        throw new UnsupportedOperationException("Field should not be set.");
+    }
+
+    @Override
+    public String getStl() {
+        return stl;
+    }
+
+    @Override
+    public void setStl(@NonNull String stl) {
+        this.stl = stl;
+    }
+
+    @Override
+    public Boolean getDebuggable() {
+        return isDebuggable;
+    }
+
+    @Override
+    public void setDebuggable(Boolean isDebuggable) {
+        this.isDebuggable = isDebuggable;
+    }
+
+    @Override
+    public Boolean getRenderscriptNdkMode() {
+        return renderscriptNdkMode;
+    }
+
+    @Override
+    public void setRenderscriptNdkMode(Boolean renderscriptNdkMode) {
+        this.renderscriptNdkMode = renderscriptNdkMode;
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskCollectionBuilderAdaptor.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskCollectionBuilderAdaptor.groovy
deleted file mode 100644
index 7650947..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskCollectionBuilderAdaptor.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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.model
-
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.TaskFactory
-import org.gradle.api.Action
-import org.gradle.api.Task
-import org.gradle.model.collection.CollectionBuilder
-
-/**
- * Adaptor to transform CollectionBuilder<Task> into TaskFactory.
- */
-class TaskCollectionBuilderAdaptor implements TaskFactory {
-
-    private final CollectionBuilder<Task> tasks
-
-    TaskCollectionBuilderAdaptor(CollectionBuilder<Task> tasks) {
-        this.tasks = tasks
-    }
-
-    @Override
-    boolean containsKey(String name) {
-        return tasks.containsKey(name)
-    }
-
-    @Override
-    void create(String name) {
-        tasks.create(name)
-    }
-
-    @Override
-    void create(String name, Action<? super Task> configAction) {
-        tasks.create(name, configAction)
-    }
-
-    @Override
-    def <S extends Task> void create(String name, Class<S> type) {
-        tasks.create(name, type)
-    }
-
-    @Override
-    def <S extends Task> void create(String name, Class<S> type, Action<? super S> configAction) {
-        tasks.create(name, type, configAction)
-    }
-
-    @Override
-    void named(String name, Action<? super Task> configAction) {
-        tasks.named(name, configAction)
-    }
-
-    @Nullable
-    @Override
-    Task named(String name) {
-        return tasks.get(name);
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskModelMapAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskModelMapAdaptor.java
new file mode 100644
index 0000000..3a62c05
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskModelMapAdaptor.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 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.model;
+
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.TaskFactory;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.model.ModelMap;
+
+/**
+ * Adaptor to transform ModelMap<Task> into TaskFactory.
+ */
+public class TaskModelMapAdaptor implements TaskFactory {
+
+    private final ModelMap<Task> tasks;
+
+    public TaskModelMapAdaptor(ModelMap<Task> tasks) {
+        this.tasks = tasks;
+    }
+
+    @Override
+    public boolean containsKey(String name) {
+        return tasks.containsKey(name);
+    }
+
+    @Override
+    public void create(String name) {
+        tasks.create(name);
+    }
+
+    @Override
+    public void create(String name, Action<? super Task> configAction) {
+        tasks.create(name, configAction);
+    }
+
+    @Override
+    public <S extends Task> void create(String name, Class<S> type) {
+        tasks.create(name, type);
+    }
+
+    @Override
+    public <S extends Task> void create(String name, Class<S> type,
+            Action<? super S> configAction) {
+        tasks.create(name, type, configAction);
+    }
+
+    @Override
+    public void named(String name, Action<? super Task> configAction) {
+        tasks.named(name, configAction);
+    }
+
+    @Nullable
+    @Override
+    public Task named(String name) {
+        return tasks.get(name);
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/NdkExtension.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/NdkExtension.java
deleted file mode 100644
index dc7a5c2..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/NdkExtension.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package com.android.build.gradle.ndk;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.google.common.collect.Sets;
-
-import org.gradle.api.Action;
-import org.gradle.api.tasks.util.PatternFilterable;
-import org.gradle.api.tasks.util.PatternSet;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Extension for android-ndk plugin.
- */
-public class NdkExtension {
-
-    @NonNull
-    private String moduleName = "";
-
-    @NonNull
-    private String target = "";
-
-    @NonNull
-    private String cFlags = "";
-
-    @NonNull
-    private String cppFlags = "";
-
-    @NonNull
-    private Set<String> ldLibs;
-
-    @NonNull
-    private String toolchain = "";
-
-    @NonNull
-    private String toolchainVersion = "";
-
-    @NonNull
-    private String stl = "";
-
-    private boolean renderscriptNdkMode = false;
-
-    @NonNull
-    private PatternSet cFilePattern;
-
-    @NonNull
-    private PatternSet cppFilePattern;
-
-    public NdkExtension() {
-        cFilePattern = new PatternSet();
-        cppFilePattern = new PatternSet();
-        ldLibs = Sets.newHashSet();
-    }
-
-    @NonNull
-    public String getModuleName() {
-        return moduleName;
-    }
-
-    public void setModuleName(@NonNull String moduleName) {
-        this.moduleName = moduleName;
-    }
-
-    @NonNull
-    public String getCompileSdkVersion() {
-        return target;
-    }
-
-    public void compileSdkVersion(@NonNull String target) {
-        this.target = target;
-    }
-
-    public void compileSdkVersion(int apiLevel) {
-        compileSdkVersion("android-" + apiLevel);
-    }
-
-    public void setCompileSdkVersion(int apiLevel) {
-        compileSdkVersion(apiLevel);
-    }
-
-    public void setCompileSdkVersion(@NonNull String target) {
-        compileSdkVersion(target);
-    }
-
-    @NonNull
-    public String getToolchain() {
-        return toolchain;
-    }
-
-    public void setToolchain(@NonNull String toolchain) {
-        this.toolchain = toolchain;
-    }
-
-    /**
-     * The toolchain version.
-     */
-    @NonNull
-    public String getToolchainVersion() {
-        return toolchainVersion;
-    }
-
-    public void setToolchainVersion(@NonNull String toolchainVersion) {
-        this.toolchainVersion = toolchainVersion;
-    }
-
-    @NonNull
-    public String getcFlags() {
-        return cFlags;
-    }
-
-    public void setcFlags(@NonNull String cFlags) {
-        this.cFlags = cFlags;
-    }
-
-    @NonNull
-    public String getCppFlags() {
-        return cppFlags;
-    }
-
-    public void setCppFlags(@NonNull String cppFlags) {
-        this.cppFlags = cppFlags;
-    }
-
-    @NonNull
-    public Set<String> getLdLibs() {
-        return ldLibs;
-    }
-
-    @NonNull
-    public NdkExtension ldLibs(String lib) {
-        ldLibs.add(lib);
-        return this;
-    }
-
-    @NonNull
-    public NdkExtension ldLibs(String... libs) {
-        Collections.addAll(ldLibs, libs);
-        return this;
-    }
-
-    @NonNull
-    public NdkExtension setLdLibs(@NonNull Collection<String> libs) {
-        ldLibs.clear();
-        ldLibs.addAll(libs);
-        return this;
-    }
-
-    @NonNull
-    public String getStl() {
-        return stl;
-    }
-
-    public void setStl(@NonNull String stl) {
-        this.stl = stl;
-    }
-
-    public boolean getRenderscriptNdkMode() {
-        return renderscriptNdkMode;
-    }
-
-    public void setRenderscriptNdkMode(boolean renderscriptNdkMode) {
-        this.renderscriptNdkMode = renderscriptNdkMode;
-    }
-
-    public void cFilePattern(Action<PatternFilterable> action) {
-        action.execute(cFilePattern);
-    }
-
-    @NonNull
-    public PatternFilterable getCFilePattern() {
-        return cFilePattern;
-    }
-
-    public void setCFilePattern(@NonNull PatternFilterable pattern) {
-        cFilePattern.copyFrom(pattern);
-    }
-
-    public void cppFilePattern(Action<PatternFilterable> action) {
-        action.execute(cppFilePattern);
-    }
-
-    @NonNull
-    public PatternFilterable getCppFilePattern() {
-        return cppFilePattern;
-    }
-
-    public void setCppFilePattern(@NonNull PatternFilterable pattern) {
-        cppFilePattern.copyFrom(pattern);
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.groovy
deleted file mode 100644
index 64ca127..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.groovy
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal;
-
-import org.gradle.nativeplatform.NativeBinarySpec;
-
-/**
- * An abstract class for NativeToolSpecification.
- */
-public abstract class AbstractNativeToolSpecification implements NativeToolSpecification {
-    /**
-     * Configure a native binary with this specification.
-     *
-     * @param binary The binary to be configured.  It is assumed the 'c' and 'cpp' plugin is applied
-     * such that the binary contains the cCompiler and cppCompiler extensions.
-     */
-    void apply(NativeBinarySpec binary) {
-        for (String arg : getCFlags()) {
-            binary.cCompiler.args(arg);
-        }
-        for (String arg : getCppFlags()) {
-            binary.cppCompiler.args(arg);
-        }
-        for (String arg : getLdFlags()) {
-            binary.linker.args(arg);
-        }
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.java
new file mode 100644
index 0000000..47a037b
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCCompiler;
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCppCompiler;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+/**
+ * An abstract class for NativeToolSpecification.
+ */
+public abstract class AbstractNativeToolSpecification implements NativeToolSpecification {
+    /**
+     * Configure a native binary with this specification.
+     *
+     * @param binary The binary to be configured.  It is assumed the 'c' and 'cpp' plugin is applied
+     *               such that the binary contains the cCompiler and cppCompiler extensions.
+     */
+    @Override
+    public void apply(NativeBinarySpec binary) {
+        for (String arg : getCFlags()) {
+            getCCompiler(binary).args(arg);
+        }
+
+        for (String arg : getCppFlags()) {
+            getCppCompiler(binary).args(arg);
+        }
+
+        for (String arg : getLdFlags()) {
+            binary.getLinker().args(arg);
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/BinaryToolHelper.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/BinaryToolHelper.java
new file mode 100644
index 0000000..82aa470
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/BinaryToolHelper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.ndk.internal;
+
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.platform.base.BinarySpec;
+
+/**
+ * Gradle's LanguageRegistration dynamically add the cCompiler and cppCompiler tool to the library.
+ *
+ * In Java, we can't call those functions dynamically, but can cast the binaries to ExternsionAware
+ * and access those tools through the extension container.
+ *
+ * This helper class hide the details for accessing the tools.
+ */
+public class BinaryToolHelper {
+    public static DefaultPreprocessingTool getCCompiler(BinarySpec binary) {
+        return (DefaultPreprocessingTool) ((ExtensionAware) binary).getExtensions().getByName("cCompiler");
+    }
+
+    public static DefaultPreprocessingTool getCppCompiler(BinarySpec binary) {
+        return (DefaultPreprocessingTool) ((ExtensionAware) binary).getExtensions().getByName("cppCompiler");
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.groovy
deleted file mode 100644
index fd12d54..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.groovy
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import com.android.SdkConstants
-import com.android.builder.core.BuilderConstants
-import org.gradle.nativeplatform.BuildType
-import org.gradle.nativeplatform.platform.NativePlatform
-
-/**
- * Flag configuration for Clang toolchain.
- */
-class ClangNativeToolSpecification extends AbstractNativeToolSpecification {
-
-    private NdkHandler ndkHandler
-
-    private NativePlatform platform
-
-    private boolean isDebugBuild
-
-    private static final def TARGET_TRIPLE = [
-            (SdkConstants.ABI_INTEL_ATOM) : "i686-none-linux-android",
-            (SdkConstants.ABI_INTEL_ATOM64) : "x86_64-none-linux-android",
-            (SdkConstants.ABI_ARMEABI) : "armv5-none-linux-android",
-            (SdkConstants.ABI_ARMEABI_V7A) : "armv7-none-linux-android",
-            (SdkConstants.ABI_ARM64_V8A) : "aarch64-none-linux-android",
-            (SdkConstants.ABI_MIPS) : "mipsel-none-linux-android",
-            (SdkConstants.ABI_MIPS64) : "mips64el-none-linux-android",
-    ]
-
-    private static final def RELEASE_CFLAGS = [
-            (SdkConstants.ABI_ARMEABI) : [
-                    "-fpic",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-no-canonical-prefixes",
-                    "-march=armv5te",
-                    "-mtune=xscale",
-                    "-msoft-float",
-                    "-mthumb",
-                    "-Os",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-            ],
-            (SdkConstants.ABI_ARMEABI_V7A) : [
-                    "-fpic",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-no-canonical-prefixes",
-                    "-march=armv7-a",
-                    "-mfloat-abi=softfp",
-                    "-mfpu=vfpv3-d16",
-                    "-mthumb",
-                    "-Os",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-            ],
-            (SdkConstants.ABI_ARM64_V8A) : [
-                    "-fpic",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM) : [
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-fPIC",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM64) : [
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-fPIC",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-            ],
-            (SdkConstants.ABI_MIPS) : [
-                    "-fpic",
-                    "-fno-strict-aliasing",
-                    "-finline-functions",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fmessage-length=0",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-            ],
-            (SdkConstants.ABI_MIPS64) : [
-                    "-fpic",
-                    "-fno-strict-aliasing",
-                    "-finline-functions",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fmessage-length=0",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-            ]
-    ]
-
-    private static final def DEBUG_CFLAGS = [
-            (SdkConstants.ABI_ARMEABI) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-marm",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_ARMEABI_V7A) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-marm",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_ARM64_V8A) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM64) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_MIPS) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-            ],
-            (SdkConstants.ABI_MIPS64) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-            ]
-    ]
-
-    public ClangNativeToolSpecification(
-            NdkHandler ndkHandler,
-            BuildType buildType,
-            NativePlatform platform) {
-        this.ndkHandler = ndkHandler
-        this.isDebugBuild = (buildType.name.equals(BuilderConstants.DEBUG))
-        this.platform = platform
-    }
-
-    @Override
-    public Iterable<String> getCFlags() {
-        getTargetFlags() + RELEASE_CFLAGS[platform.name] + DEBUG_CFLAGS[platform.name]
-    }
-
-    @Override
-    public Iterable<String> getCppFlags() {
-        getCFlags()
-    }
-
-    @Override
-    public Iterable<String> getLdFlags() {
-        getTargetFlags() +
-                (platform.name.equals(SdkConstants.ABI_ARMEABI_V7A) ? ["-Wl,--fix-cortex-a8"] : [])
-    }
-
-    private Iterable<String> getTargetFlags() {
-        [
-                "-gcc-toolchain",
-                ndkHandler.getToolchainPath(
-                        "gcc",
-                        ndkHandler.getGccToolchainVersion(platform.name),
-                        platform.name),
-                "-target",
-                TARGET_TRIPLE[platform.name]
-        ]
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.java
new file mode 100644
index 0000000..6b7b1e5
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import com.android.SdkConstants;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.builder.core.BuilderConstants;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.util.Map;
+
+/**
+ * Flag configuration for Clang toolchain.
+ */
+public class ClangNativeToolSpecification extends AbstractNativeToolSpecification {
+
+    private NdkHandler ndkHandler;
+
+    private NativePlatform platform;
+
+    private boolean isDebugBuild;
+
+    private static final Map<String, String> TARGET_TRIPLE = ImmutableMap.<String, String>builder()
+                .put(SdkConstants.ABI_INTEL_ATOM, "i686-none-linux-android")
+                .put(SdkConstants.ABI_INTEL_ATOM64, "x86_64-none-linux-android")
+                .put(SdkConstants.ABI_ARMEABI, "armv5-none-linux-android")
+                .put(SdkConstants.ABI_ARMEABI_V7A, "armv7-none-linux-android")
+                .put(SdkConstants.ABI_ARM64_V8A, "aarch64-none-linux-android")
+                .put(SdkConstants.ABI_MIPS, "mipsel-none-linux-android")
+                .put(SdkConstants.ABI_MIPS64, "mips64el-none-linux-android")
+                .build();
+
+
+    private static final ListMultimap<String, String> RELEASE_CFLAGS =
+            ImmutableListMultimap.<String, String>builder()
+                    .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+                            "-fpic",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-no-canonical-prefixes",
+                            "-march=armv5te",
+                            "-mtune=xscale",
+                            "-msoft-float",
+                            "-mthumb",
+                            "-Os",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing"))
+                    .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+                            "-fpic",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-no-canonical-prefixes",
+                            "-march=armv7-a",
+                            "-mfloat-abi=softfp",
+                            "-mfpu=vfpv3-d16",
+                            "-mthumb",
+                            "-Os",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing"))
+                    .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+                            "-fpic",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing"))
+                    .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-fPIC",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing"))
+                    .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-fPIC",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing"))
+                    .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+                            "-fpic",
+                            "-fno-strict-aliasing",
+                            "-finline-functions",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fmessage-length=0",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer"))
+                    .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+                            "-fpic",
+                            "-fno-strict-aliasing",
+                            "-finline-functions",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fmessage-length=0",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer"))
+                    .build();
+
+    private static final ListMultimap<String, String> DEBUG_CFLAGS =
+            ImmutableListMultimap.<String, String>builder()
+                    .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-marm",
+                            "-fno-strict-aliasing",
+                            "-fno-limit-debug-info"))
+                    .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-marm",
+                            "-fno-strict-aliasing",
+                            "-fno-limit-debug-info"))
+                    .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-fno-omit-frame-pointer",
+                            "-fno-strict-aliasing",
+                            "-fno-limit-debug-info"))
+                    .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-fno-omit-frame-pointer",
+                            "-fno-strict-aliasing",
+                            "-fno-limit-debug-info"))
+                    .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-fno-omit-frame-pointer",
+                            "-fno-strict-aliasing",
+                            "-fno-limit-debug-info"))
+                    .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-fno-omit-frame-pointer",
+                            "-fno-limit-debug-info"))
+                    .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+                            "-O0",
+                            "-UNDEBUG",
+                            "-fno-omit-frame-pointer",
+                            "-fno-limit-debug-info"))
+                    .build();
+
+    public ClangNativeToolSpecification(
+            NdkHandler ndkHandler,
+            NativePlatform platform,
+            boolean isDebugBuild) {
+        this.ndkHandler = ndkHandler;
+        this.isDebugBuild = isDebugBuild;
+        this.platform = platform;
+    }
+
+    @Override
+    public Iterable<String> getCFlags() {
+        return Iterables.concat(
+                getTargetFlags(),
+                RELEASE_CFLAGS.get(platform.getName()),
+                isDebugBuild ? DEBUG_CFLAGS.get(platform.getName()) : ImmutableList.<String>of());
+    }
+
+    @Override
+    public Iterable<String> getCppFlags() {
+        return getCFlags();
+    }
+
+    @Override
+    public Iterable<String> getLdFlags() {
+        return Iterables.concat(
+                getTargetFlags(),
+                platform.getName().equals(SdkConstants.ABI_ARMEABI_V7A)
+                        ? ImmutableList.of("-Wl,--fix-cortex-a8")
+                        : ImmutableList.<String>of());
+    }
+
+    private Iterable<String> getTargetFlags() {
+        return ImmutableList.of(
+                "-gcc-toolchain",
+                ndkHandler.getDefaultGccToolchainPath(Abi.getByName(platform.getName())).toString(),
+                "-target",
+                TARGET_TRIPLE.get(platform.getName()));
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/DefaultNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/DefaultNativeToolSpecification.java
new file mode 100644
index 0000000..14e64e7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/DefaultNativeToolSpecification.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.ndk.internal;
+
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.util.List;
+
+/**
+ * Default flags that applies to all toolchains.
+ */
+public class DefaultNativeToolSpecification extends AbstractNativeToolSpecification
+        implements NativeToolSpecification {
+
+    private static final List<String> CPP_FLAGS = ImmutableList.of("-fno-rtti", "-fno-exceptions");
+
+    private static final List<String> LD_FLAGS = ImmutableList.of(
+            "-Wl,--no-undefined",
+            "-Wl,-z,noexecstack",
+            "-Wl,-z,relro",
+            "-Wl,-z,now");
+
+    @Override
+    public Iterable<String> getCFlags() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Iterable<String> getCppFlags() {
+        return CPP_FLAGS;
+    }
+
+    @Override
+    public Iterable<String> getLdFlags() {
+        return LD_FLAGS;
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.groovy
deleted file mode 100644
index 281f67e..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.groovy
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import com.android.SdkConstants
-import com.android.builder.core.BuilderConstants
-import org.gradle.nativeplatform.BuildType
-import org.gradle.nativeplatform.platform.NativePlatform
-
-/**
- * Flag configuration for GCC toolchain.
- */
-
-class GccNativeToolSpecification extends AbstractNativeToolSpecification {
-
-    private static final def RELEASE_CFLAGS = [
-            (SdkConstants.ABI_ARMEABI) : [
-                    "-fpic",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-no-canonical-prefixes",
-                    "-march=armv5te",
-                    "-mtune=xscale",
-                    "-msoft-float",
-                    "-mthumb",
-                    "-Os",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fno-strict-aliasing",
-                    "-finline-limit=64",
-            ],
-            (SdkConstants.ABI_ARMEABI_V7A) : [
-                    "-fpic",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-no-canonical-prefixes",
-                    "-march=armv7-a",
-                    "-mfpu=vfpv3-d16",
-                    "-mfloat-abi=softfp",
-                    "-mthumb",
-                    "-Os",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fno-strict-aliasing",
-                    "-finline-limit=64",
-            ],
-            (SdkConstants.ABI_ARM64_V8A) : [
-                    "-fpic",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fstack-protector",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-                    "-funswitch-loops",
-                    "-finline-limit=300",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM) : [
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-no-canonical-prefixes",
-                    "-fstack-protector",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-                    "-funswitch-loops",
-                    "-finline-limit=300",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM64) : [
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-no-canonical-prefixes",
-                    "-fstack-protector",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-fstrict-aliasing",
-                    "-funswitch-loops",
-                    "-finline-limit=300",
-            ],
-            (SdkConstants.ABI_MIPS)       : [
-                    "-fpic",
-                    "-fno-strict-aliasing",
-                    "-finline-functions",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fmessage-length=0",
-                    "-fno-inline-functions-called-once",
-                    "-fgcse-after-reload",
-                    "-frerun-cse-after-loop",
-                    "-frename-registers",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-funswitch-loops",
-                    "-finline-limit=300",
-            ],
-            (SdkConstants.ABI_MIPS64) : [
-                    "-fpic",
-                    "-fno-strict-aliasing",
-                    "-finline-functions",
-                    "-ffunction-sections",
-                    "-funwind-tables",
-                    "-fmessage-length=0",
-                    "-fno-inline-functions-called-once",
-                    "-fgcse-after-reload",
-                    "-frerun-cse-after-loop",
-                    "-frename-registers",
-                    "-no-canonical-prefixes",
-                    "-O2",
-                    "-g",
-                    "-DNDEBUG",
-                    "-fomit-frame-pointer",
-                    "-funswitch-loops",
-                    "-finline-limit=300",
-            ],
-    ]
-
-    private static final def DEBUG_CFLAGS = [
-            (SdkConstants.ABI_ARMEABI) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_ARMEABI_V7A) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_ARM64_V8A) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_INTEL_ATOM64) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-strict-aliasing",
-            ],
-            (SdkConstants.ABI_MIPS) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-                    "-fno-unswitch-loops",
-            ],
-            (SdkConstants.ABI_MIPS64) : [
-                    "-O0",
-                    "-UNDEBUG",
-                    "-fno-omit-frame-pointer",
-            ]
-    ]
-
-    private static final Iterable<String> LDFLAGS = [
-            "-no-canonical-prefixes",
-    ]
-
-    private NativePlatform platform
-
-    private boolean isDebugBuild
-
-    GccNativeToolSpecification(BuildType buildType, NativePlatform platform) {
-        this.isDebugBuild = (buildType.name.equals(BuilderConstants.DEBUG))
-        this.platform = platform
-    }
-
-    @Override
-    public Iterable<String> getCFlags() {
-        RELEASE_CFLAGS[platform.name] + (isDebugBuild ? DEBUG_CFLAGS[platform.name] : [])
-    }
-
-    @Override
-    public Iterable<String> getCppFlags() {
-        getCFlags()
-    }
-
-    @Override
-    public Iterable<String> getLdFlags() {
-        LDFLAGS
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.java
new file mode 100644
index 0000000..d1b7bf6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import com.android.SdkConstants;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+/**
+ * Flag configuration for GCC toolchain.
+ */
+public class GccNativeToolSpecification extends AbstractNativeToolSpecification {
+
+    private static final ListMultimap<String, String> RELEASE_CFLAGS =
+            ImmutableListMultimap.<String, String>builder()
+                    .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+                            "-fpic",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-no-canonical-prefixes",
+                            "-march=armv5te",
+                            "-mtune=xscale",
+                            "-msoft-float",
+                            "-mthumb",
+                            "-Os",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fno-strict-aliasing",
+                            "-finline-limit=64"))
+                    .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+                            "-fpic",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-no-canonical-prefixes",
+                            "-march=armv7-a",
+                            "-mfpu=vfpv3-d16",
+                            "-mfloat-abi=softfp",
+                            "-mthumb",
+                            "-Os",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fno-strict-aliasing",
+                            "-finline-limit=64"))
+                    .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+                            "-fpic",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fstack-protector",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing",
+                            "-funswitch-loops",
+                            "-finline-limit=300"))
+                    .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-no-canonical-prefixes",
+                            "-fstack-protector",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing",
+                            "-funswitch-loops",
+                            "-finline-limit=300"))
+                    .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-no-canonical-prefixes",
+                            "-fstack-protector",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-fstrict-aliasing",
+                            "-funswitch-loops",
+                            "-finline-limit=300"))
+                    .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+                            "-fpic",
+                            "-fno-strict-aliasing",
+                            "-finline-functions",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fmessage-length=0",
+                            "-fno-inline-functions-called-once",
+                            "-fgcse-after-reload",
+                            "-frerun-cse-after-loop",
+                            "-frename-registers",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-funswitch-loops",
+                            "-finline-limit=300"))
+                    .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+                            "-fpic",
+                            "-fno-strict-aliasing",
+                            "-finline-functions",
+                            "-ffunction-sections",
+                            "-funwind-tables",
+                            "-fmessage-length=0",
+                            "-fno-inline-functions-called-once",
+                            "-fgcse-after-reload",
+                            "-frerun-cse-after-loop",
+                            "-frename-registers",
+                            "-no-canonical-prefixes",
+                            "-O2",
+                            "-g",
+                            "-DNDEBUG",
+                            "-fomit-frame-pointer",
+                            "-funswitch-loops",
+                            "-finline-limit=300"))
+                    .build();
+
+        private static final ListMultimap<String, String> DEBUG_CFLAGS =
+                ImmutableListMultimap.<String, String>builder()
+                        .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer",
+                                "-fno-strict-aliasing"))
+                        .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer",
+                                "-fno-strict-aliasing"))
+                        .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer",
+                                "-fno-strict-aliasing"))
+                        .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer",
+                                "-fno-strict-aliasing"))
+                        .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer",
+                                "-fno-strict-aliasing"))
+                        .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer",
+                                "-fno-unswitch-loops"))
+                        .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+                                "-O0",
+                                "-UNDEBUG",
+                                "-fno-omit-frame-pointer"))
+                        .build();
+
+    private static final Iterable<String> LDFLAGS = ImmutableList.of("-no-canonical-prefixes");
+
+    private NativePlatform platform;
+
+    private boolean isDebugBuild;
+
+
+    public GccNativeToolSpecification(NativePlatform platform, boolean isDebugBuild) {
+        this.isDebugBuild = isDebugBuild;
+        this.platform = platform;
+    }
+
+    @Override
+    public Iterable<String> getCFlags() {
+        return Iterables.concat(
+                RELEASE_CFLAGS.get(platform.getName()),
+                (isDebugBuild ? DEBUG_CFLAGS.get(platform.getName()) : ImmutableList.<String>of()));
+    }
+
+    @Override
+    public Iterable<String> getCppFlags() {
+        return getCFlags();
+    }
+
+    @Override
+    public Iterable<String> getLdFlags() {
+        return LDFLAGS;
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.groovy
deleted file mode 100644
index 073a83d..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.groovy
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import org.gradle.nativeplatform.NativeBinarySpec
-
-/**
- * Interface for native binaries flag configurations.
- */
-interface NativeToolSpecification {
-
-    public Iterable<String> getCFlags()
-
-    public Iterable<String> getCppFlags()
-
-    public Iterable<String> getLdFlags()
-
-    public void apply(NativeBinarySpec binary);
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.java
new file mode 100644
index 0000000..cb0555b
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+/**
+ * Interface for native binaries flag configurations.
+ */
+public interface NativeToolSpecification {
+
+    Iterable<String> getCFlags();
+
+    Iterable<String> getCppFlags();
+
+    Iterable<String> getLdFlags();
+
+    void apply(NativeBinarySpec binary);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.groovy
deleted file mode 100644
index a9e3a47..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.groovy
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import org.gradle.nativeplatform.BuildType
-import org.gradle.nativeplatform.platform.NativePlatform
-
-/**
- * Factory to create a NativeToolSpecification.
- */
-class NativeToolSpecificationFactory {
-    /**
-     * Returns a NativeToolSpecification.
-     *
-     * @param buildType Build type of the native binary.
-     * @param platform Target platform of the native binary.
-     * @return A NativeToolSpecification for the targeted native binary.
-     */
-    public static NativeToolSpecification create(
-            NdkHandler ndkHandler,
-            BuildType buildType,
-            NativePlatform platform) {
-        String toolchain = ndkHandler.getNdkExtension().getToolchain()
-        return (toolchain == null || toolchain.equals("gcc")
-                ? new GccNativeToolSpecification(buildType, platform)
-                : new ClangNativeToolSpecification(ndkHandler, buildType, platform))
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.java
new file mode 100644
index 0000000..cb91e06
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Toolchain;
+
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+/**
+ * Factory to create a NativeToolSpecification.
+ */
+public class NativeToolSpecificationFactory {
+
+    /**
+     * Returns a NativeToolSpecification.
+     *
+     * @param platform  Target platform of the native binary.
+     * @param isDebugBuild Is the build debuggable.
+     * @return A NativeToolSpecification for the targeted native binary.
+     */
+    public static NativeToolSpecification create(
+            NdkHandler ndkHandler,
+            NativePlatform platform,
+            boolean isDebugBuild) {
+        return (ndkHandler.getToolchain().equals(Toolchain.GCC)
+                ? new GccNativeToolSpecification(platform, isDebugBuild)
+                : new ClangNativeToolSpecification(ndkHandler, platform, isDebugBuild));
+    }
+
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.groovy
deleted file mode 100644
index b78036c..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.groovy
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import com.android.build.gradle.model.AndroidComponentModelSourceSet
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.tasks.GdbSetupTask
-import com.android.builder.core.BuilderConstants
-import org.gradle.api.tasks.TaskContainer
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.api.Task
-import org.gradle.api.tasks.Copy
-import org.gradle.nativeplatform.NativeBinarySpec
-import org.gradle.nativeplatform.NativeLibrarySpec
-import org.gradle.nativeplatform.SharedLibraryBinarySpec
-import org.gradle.language.c.tasks.CCompile
-import org.gradle.language.cpp.tasks.CppCompile
-import org.gradle.platform.base.BinarySpec
-
-/**
- * Configure settings used by the native binaries.
- */
-class NdkConfiguration {
-    public static void configureProperties(
-            NativeLibrarySpec library,
-            AndroidComponentModelSourceSet sources,
-            File buildDir,
-            NdkExtension ndkExtension,
-            NdkHandler ndkHandler) {
-        Collection<String> abiList = ndkHandler.getSupportedAbis()
-        abiList.each {
-            library.targetPlatform(it)
-        }
-
-        library.binaries.withType(SharedLibraryBinarySpec) { binary ->
-            sourceIfExist(binary, sources, "main")
-            sourceIfExist(binary, sources, binary.flavor.name)
-            sourceIfExist(binary, sources, binary.buildType.name)
-            sourceIfExist(binary, sources, binary.flavor.name + binary.buildType.name.capitalize())
-
-            cCompiler.define "ANDROID"
-            cppCompiler.define "ANDROID"
-            cCompiler.define "ANDROID_NDK"
-            cppCompiler.define "ANDROID_NDK"
-
-            // Set output library filename.
-            binary.sharedLibraryFile =
-                    new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary) + "/" +
-                            NdkNamingScheme.getSharedLibraryFileName(ndkExtension.getModuleName()))
-
-            // Replace output directory of compile tasks.
-            binary.tasks.withType(CCompile) { task ->
-                String sourceSetName = task.objectFileDir.name
-                task.objectFileDir = NdkNamingScheme.getObjectFilesOutputDirectory(
-                        binary, buildDir, sourceSetName)
-            }
-            binary.tasks.withType(CppCompile) { task ->
-                String sourceSetName = task.objectFileDir.name
-                task.objectFileDir = NdkNamingScheme.getObjectFilesOutputDirectory(
-                                binary, buildDir, sourceSetName)
-            }
-
-            String sysroot = ndkHandler.getSysroot(binary.targetPlatform)
-            cCompiler.args  "--sysroot=$sysroot"
-            cppCompiler.args  "--sysroot=$sysroot"
-            linker.args "--sysroot=$sysroot"
-
-            if (ndkExtension.getRenderscriptNdkMode()) {
-                cCompiler.args "-I$sysroot/usr/include/rs"
-                cCompiler.args "-I$sysroot/usr/include/rs/cpp"
-                cppCompiler.args "-I$sysroot/usr/include/rs"
-                cppCompiler.args "-I$sysroot/usr/include/rs/cpp"
-                linker.args "-L$sysroot/usr/lib/rs"
-            }
-
-            NativeToolSpecificationFactory.create(ndkHandler, binary.buildType, binary.targetPlatform).apply(binary)
-
-            // Add flags defined in NdkExtension
-            if (ndkExtension.getcFlags() != null) {
-                cCompiler.args ndkExtension.getcFlags()
-            }
-            if (ndkExtension.getCppFlags() != null) {
-                cppCompiler.args ndkExtension.getCppFlags()
-            }
-            for (String ldLibs : ndkExtension.getLdLibs()) {
-                linker.args "-l$ldLibs"
-            }
-        }
-    }
-
-    public static void createTasks(
-            TaskContainer tasks,
-            SharedLibraryBinarySpec binary,
-            File buildDir,
-            NdkExtension ndkExtension,
-            NdkHandler ndkHandler) {
-        StlConfiguration.apply(ndkHandler, ndkExtension.getStl(), tasks, buildDir, binary)
-
-        if (binary.buildType.name.equals(BuilderConstants.DEBUG)) {
-            setupNdkGdbDebug(tasks, binary, buildDir, ndkExtension, ndkHandler)
-        }
-    }
-
-    /**
-     * Add the sourceSet with the specified name to the binary if such sourceSet is defined.
-     */
-    private static void sourceIfExist(
-            BinarySpec binary,
-            AndroidComponentModelSourceSet projectSourceSet,
-            String sourceSetName) {
-        FunctionalSourceSet sourceSet = projectSourceSet.findByName(sourceSetName)
-        if (sourceSet != null) {
-            binary.source(sourceSet)
-        }
-    }
-
-    /**
-     * Setup tasks to create gdb.setup and copy gdbserver for NDK debugging.
-     */
-    private static void setupNdkGdbDebug(
-            TaskContainer tasks,
-            NativeBinarySpec binary,
-            File buildDir,
-            NdkExtension ndkExtension,
-            NdkHandler handler) {
-        Task copyGdbServerTask = tasks.create(
-                name: NdkNamingScheme.getTaskName(binary, "copy", "GdbServer"),
-                type: Copy) {
-            from(new File(
-                    handler.getPrebuiltDirectory(binary.targetPlatform),
-                    "gdbserver/gdbserver"))
-            into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)))
-        }
-        binary.builtBy copyGdbServerTask
-
-        Task createGdbSetupTask = tasks.create(
-                name: NdkNamingScheme.getTaskName(binary, "create", "Gdbsetup"),
-                type: GdbSetupTask) { def task ->
-            task.ndkHandler = handler
-            task.extension = ndkExtension
-            task.binary = binary
-            task.outputDir = new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary))
-        }
-        binary.builtBy createGdbSetupTask
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.java
new file mode 100644
index 0000000..13d907c
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCCompiler;
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCppCompiler;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.model.AndroidComponentModelSourceSet;
+import com.android.build.gradle.tasks.GdbSetupTask;
+import com.android.build.gradle.tasks.StripDebugSymbolTask;
+import com.android.utils.StringHelper;
+import com.google.common.base.Objects;
+
+import org.gradle.api.Action;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.Copy;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.c.tasks.CCompile;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.language.cpp.tasks.CppCompile;
+import org.gradle.model.ModelMap;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.platform.base.BinarySpec;
+
+import java.io.File;
+
+/**
+ * Configure settings used by the native binaries.
+ */
+public class NdkConfiguration {
+
+    public static void configureProperties(
+            NativeLibrarySpec library,
+            final AndroidComponentModelSourceSet sources,
+            final File buildDir,
+            final NdkHandler ndkHandler) {
+        for (Abi abi : ndkHandler.getSupportedAbis()) {
+            library.targetPlatform(abi.getName());
+        }
+
+        library.getBinaries()
+                .withType(SharedLibraryBinarySpec.class, new Action<SharedLibraryBinarySpec>() {
+                    @Override
+                    public void execute(final SharedLibraryBinarySpec binary) {
+                        sourceIfExist(binary, sources, "main");
+                        sourceIfExist(binary, sources, binary.getFlavor().getName());
+                        sourceIfExist(binary, sources, binary.getBuildType().getName());
+                        sourceIfExist(binary, sources,
+                                binary.getFlavor().getName()
+                                        + StringHelper.capitalize(binary.getBuildType().getName()));
+
+                        getCCompiler(binary).define("ANDROID");
+                        getCppCompiler(binary).define("ANDROID");
+                        getCCompiler(binary).define("ANDROID_NDK");
+                        getCppCompiler(binary).define("ANDROID_NDK");
+
+                        // Replace output directory of compile tasks.
+                        binary.getTasks().withType(CCompile.class, new Action<CCompile>() {
+                            @Override
+                            public void execute(CCompile task) {
+                                String sourceSetName = task.getObjectFileDir().getName();
+                                task.setObjectFileDir(
+                                        NdkNamingScheme.getObjectFilesOutputDirectory(
+                                                binary,
+                                                buildDir,
+                                                sourceSetName));
+                            }
+                        });
+                        binary.getTasks().withType(CppCompile.class, new Action<CppCompile>() {
+                            @Override
+                            public void execute(CppCompile task) {
+                                String sourceSetName = task.getObjectFileDir().getName();
+                                task.setObjectFileDir(
+                                        NdkNamingScheme.getObjectFilesOutputDirectory(
+                                                binary,
+                                                buildDir,
+                                                sourceSetName));
+                            }
+                        });
+
+                        new DefaultNativeToolSpecification().apply(binary);
+
+                        String sysroot = ndkHandler.getSysroot(
+                                Abi.getByName(binary.getTargetPlatform().getName()));
+
+                        getCCompiler(binary).args("--sysroot=" + sysroot);
+                        getCppCompiler(binary).args("--sysroot=" + sysroot);
+                        binary.getLinker().args("--sysroot=" + sysroot);
+                        binary.getLinker().args("-Wl,--build-id");
+
+                    }
+
+                });
+    }
+
+    /**
+     * Configure native binary with variant specific options.
+     */
+    public static void configureBinary(
+            SharedLibraryBinarySpec binary,
+            final File buildDir,
+            final NdkConfig ndkConfig,
+            final NdkHandler ndkHandler) {
+        // Set output library filename.
+        binary.setSharedLibraryFile(
+                new File(
+                        buildDir,
+                        NdkNamingScheme.getDebugLibraryDirectoryName(binary)
+                                + "/"
+                                + NdkNamingScheme.getSharedLibraryFileName(
+                                ndkConfig.getModuleName())));
+
+        String sysroot = ndkHandler.getSysroot(
+                Abi.getByName(binary.getTargetPlatform().getName()));
+
+        if (ndkConfig.getRenderscriptNdkMode()) {
+            getCCompiler(binary).args("-I" + sysroot + "/usr/include/rs");
+            getCCompiler(binary).args("-I" + sysroot + "/usr/include/rs/cpp");
+            getCppCompiler(binary).args("-I" + sysroot + "/usr/include/rs");
+            getCppCompiler(binary).args("-I" + sysroot + "/usr/include/rs/cpp");
+            binary.getLinker().args("-L" + sysroot + "/usr/lib/rs");
+        }
+
+        // STL flags must be applied before user defined flags to resolve possible undefined symbols
+        // in the STL library.
+        StlNativeToolSpecification stlConfig = new StlNativeToolSpecification(
+                ndkHandler,
+                ndkConfig.getStl(),
+                binary.getTargetPlatform());
+        stlConfig.apply(binary);
+
+        NativeToolSpecificationFactory.create(
+                ndkHandler,
+                binary.getTargetPlatform(),
+                Objects.firstNonNull(ndkConfig.getDebuggable(), false)).apply(
+                binary);
+
+        // Add flags defined in NdkConfig
+        for (String flag : ndkConfig.getCFlags()) {
+            getCCompiler(binary).args(flag.trim());
+        }
+
+        for (String flag : ndkConfig.getCppFlags()) {
+            getCppCompiler(binary).args(flag.trim());
+        }
+
+        for (String flag : ndkConfig.getLdFlags()) {
+            binary.getLinker().args(flag.trim());
+        }
+
+        for (String ldLib : ndkConfig.getLdLibs()) {
+            binary.getLinker().args("-l" + ldLib.trim());
+        }
+    }
+
+    public static void createTasks(
+            @NonNull ModelMap<Task> tasks,
+            @NonNull SharedLibraryBinarySpec binary,
+            @NonNull File buildDir,
+            @NonNull NdkConfig ndkConfig,
+            @NonNull NdkHandler ndkHandler) {
+        String compileNdkTaskName = NdkNamingScheme.getNdkBuildTaskName(binary);
+        tasks.create(compileNdkTaskName);
+
+        StlConfiguration.createStlCopyTask(tasks, binary, buildDir, ndkHandler,
+                ndkConfig.getStl(), compileNdkTaskName);
+
+        if (Boolean.TRUE.equals(ndkConfig.getDebuggable())) {
+            // TODO: Use AndroidTaskRegistry and scopes to create tasks in experimental plugin.
+            setupNdkGdbDebug(tasks, binary, buildDir, ndkConfig, ndkHandler, compileNdkTaskName);
+        }
+        createStripDebugTask(tasks, binary, buildDir, ndkHandler, compileNdkTaskName);
+    }
+
+    /**
+     * Add the sourceSet with the specified name to the binary if such sourceSet is defined.
+     */
+    private static void sourceIfExist(
+            BinarySpec binary,
+            AndroidComponentModelSourceSet projectSourceSet,
+            final String sourceSetName) {
+        FunctionalSourceSet sourceSet = projectSourceSet.findByName(sourceSetName);
+        if (sourceSet != null) {
+            final LanguageSourceSet jni = sourceSet.getByName("jni");
+            binary.sources(new Action<PolymorphicDomainObjectContainer<LanguageSourceSet>>() {
+                @Override
+                public void execute(
+                        PolymorphicDomainObjectContainer<LanguageSourceSet> languageSourceSets) {
+                    // Hardcode the acceptable extension until we find a suitable DSL for user to
+                    // modify.
+                    languageSourceSets.create(
+                            sourceSetName + "C",
+                            CSourceSet.class,
+                            new Action<LanguageSourceSet>() {
+                                @Override
+                                public void execute(LanguageSourceSet source) {
+                                    source.getSource().setSrcDirs(jni.getSource().getSrcDirs());
+                                    source.getSource().include("**/*.c");
+                                    source.getSource().exclude(jni.getSource().getExcludes());
+                                }
+                            });
+                    languageSourceSets.create(
+                            sourceSetName + "Cpp",
+                            CppSourceSet.class,
+                            new Action<LanguageSourceSet>() {
+                                @Override
+                                public void execute(LanguageSourceSet source) {
+                                    source.getSource().setSrcDirs(jni.getSource().getSrcDirs());
+                                    source.getSource().include("**/*.C");
+                                    source.getSource().include("**/*.CPP");
+                                    source.getSource().include("**/*.c++");
+                                    source.getSource().include("**/*.cc");
+                                    source.getSource().include("**/*.cp");
+                                    source.getSource().include("**/*.cpp");
+                                    source.getSource().include("**/*.cxx");
+                                    source.getSource().exclude(jni.getSource().getExcludes());
+                                }
+                            });
+                }
+            });
+        }
+    }
+
+    /**
+     * Setup tasks to create gdb.setup and copy gdbserver for NDK debugging.
+     */
+    private static void setupNdkGdbDebug(
+            @NonNull ModelMap<Task> tasks,
+            @NonNull final NativeBinarySpec binary,
+            @NonNull final File buildDir,
+            @NonNull final NdkConfig ndkConfig,
+            @NonNull final NdkHandler handler,
+            @NonNull String buildTaskName) {
+        final String copyGdbServerTaskName = NdkNamingScheme.getTaskName(binary, "copy", "GdbServer");
+        tasks.create(copyGdbServerTaskName, Copy.class, new Action<Copy>() {
+            @Override
+            public void execute(Copy task) {
+                task.from(new File(handler.getPrebuiltDirectory(
+                        Abi.getByName(binary.getTargetPlatform().getName())),
+                        "gdbserver/gdbserver"));
+                task.into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)));
+            }
+        });
+
+        final String createGdbSetupTaskName = NdkNamingScheme.getTaskName(binary, "create", "Gdbsetup");
+        tasks.create(createGdbSetupTaskName, GdbSetupTask.class, new Action<GdbSetupTask>() {
+            @Override
+            public void execute(GdbSetupTask task) {
+                task.setNdkHandler(handler);
+                task.setExtension(ndkConfig);
+                task.setBinary(binary);
+                task.setOutputDir(
+                        new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)));
+            }
+        });
+
+        tasks.named(buildTaskName, new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                task.dependsOn(copyGdbServerTaskName);
+                task.dependsOn(createGdbSetupTaskName);
+            }
+        });
+    }
+
+    private static void createStripDebugTask(
+            ModelMap<Task> tasks,
+            final SharedLibraryBinarySpec binary,
+            final File buildDir,
+            final NdkHandler handler,
+            String buildTaskName) {
+
+        final String taskName = NdkNamingScheme.getTaskName(binary, "stripSymbols");
+        tasks.create(
+                taskName,
+                StripDebugSymbolTask.class,
+                new StripDebugSymbolTask.ConfigAction(binary, buildDir, handler));
+        tasks.named(buildTaskName, new Action<Task>() {
+            @Override
+            public void execute(Task task) {
+                task.dependsOn(taskName);
+            }
+        });
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java
index 4621bdd..90b691f 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java
@@ -1,7 +1,7 @@
 package com.android.build.gradle.ndk.internal;
 
-
-import com.android.build.gradle.ndk.NdkExtension;
+import com.android.build.gradle.internal.core.Toolchain;
+import com.android.build.gradle.managed.NdkConfig;
 
 import org.gradle.api.InvalidUserDataException;
 
@@ -10,46 +10,27 @@
  */
 public class NdkExtensionConvention {
 
-    public static final String DEFAULT_TOOLCHAIN = "gcc";
-
-    // Default toolchain version depends on the target ABI.  Setting it to "default" to allow
-    // the version to be determined later.
-    public static final String DEFAULT_TOOLCHAIN_VERSION = "default";
-
     public static final String DEFAULT_STL = "system";
 
     /**
      * Validate the NdkExtension and provide default values.
      */
-    public static void setExtensionDefault(NdkExtension extension) {
-        if (extension.getToolchain().isEmpty()) {
-            extension.setToolchain(DEFAULT_TOOLCHAIN);
+    public static void setExtensionDefault(NdkConfig ndkConfig) {
+        if (ndkConfig.getToolchain().isEmpty()) {
+            ndkConfig.setToolchain(Toolchain.getDefault().getName());
         } else {
-            if (!extension.getToolchain().equals("gcc") &&
-                    !extension.getToolchain().equals("clang")) {
+            if (!ndkConfig.getToolchain().equals("gcc") &&
+                    !ndkConfig.getToolchain().equals("clang")) {
                 throw new InvalidUserDataException(String.format(
                         "Invalid toolchain '%s'.  Supported toolchains are 'gcc' and 'clang'.",
-                        extension.getToolchain()));
+                        ndkConfig.getToolchain()));
             }
         }
 
-        if (extension.getToolchainVersion().isEmpty()) {
-            extension.setToolchainVersion(DEFAULT_TOOLCHAIN_VERSION);
-        }
-
-        if (extension.getCFilePattern().getIncludes().isEmpty()) {
-            extension.getCFilePattern().include("**/*.c");
-        }
-
-        if (extension.getCppFilePattern().getIncludes().isEmpty()) {
-            extension.getCppFilePattern().include("**/*.cpp");
-            extension.getCppFilePattern().include("**/*.cc");
-        }
-
-        if (extension.getStl().isEmpty()) {
-            extension.setStl(DEFAULT_STL);
+        if (ndkConfig.getStl().isEmpty()) {
+            ndkConfig.setStl(DEFAULT_STL);
         } else {
-            StlConfiguration.checkStl(extension.getStl());
+            StlConfiguration.checkStl(ndkConfig.getStl());
         }
     }
 }
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkHandler.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkHandler.java
deleted file mode 100644
index 4723d00..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkHandler.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal;
-
-import static com.android.SdkConstants.FN_LOCAL_PROPERTIES;
-
-import com.android.SdkConstants;
-import com.android.annotations.Nullable;
-import com.android.build.gradle.ndk.NdkExtension;
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Closeables;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Project;
-import org.gradle.nativeplatform.platform.NativePlatform;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Handles NDK related information.
- */
-public class NdkHandler {
-
-    // Map of ABI to toolchain platform string.
-    private static final Map<String, String> PLATFORM_STRING;
-
-    // Map of ABI to target architecture
-    private static final Map<String, String> ARCHITECTURE_STRING;
-
-    // Map of toolchain names to the subdirectory name containing the toolchain.
-    private static final Map<String, String> TOOLCHAIN_STRING;
-
-    private static final List<String> ABI32 = ImmutableList.of(
-            SdkConstants.ABI_INTEL_ATOM,
-            SdkConstants.ABI_ARMEABI_V7A,
-            SdkConstants.ABI_ARMEABI,
-            SdkConstants.ABI_MIPS);
-
-    private static final List<String> ALL_ABI = ImmutableList.of(
-            SdkConstants.ABI_INTEL_ATOM,
-            SdkConstants.ABI_INTEL_ATOM64,
-            SdkConstants.ABI_ARMEABI_V7A,
-            SdkConstants.ABI_ARMEABI,
-            SdkConstants.ABI_ARM64_V8A,
-            SdkConstants.ABI_MIPS,
-            SdkConstants.ABI_MIPS64);
-
-    private static final String DEFAULT_LLVM_GCC32_VERSION="4.8";
-    private static final String DEFAULT_LLVM_GCC64_VERSION="4.9";
-
-    private NdkExtension ndkExtension;
-
-    private File ndkDirectory;
-
-    static {
-        // Initialize static maps.
-        PLATFORM_STRING = ImmutableMap.<String, String>builder()
-                .put(SdkConstants.ABI_INTEL_ATOM, "x86")
-                .put(SdkConstants.ABI_INTEL_ATOM64, "x86_64")
-                .put(SdkConstants.ABI_ARMEABI_V7A, "arm-linux-androideabi")
-                .put(SdkConstants.ABI_ARMEABI, "arm-linux-androideabi")
-                .put(SdkConstants.ABI_ARM64_V8A, "aarch64-linux-android")
-                .put(SdkConstants.ABI_MIPS, "mipsel-linux-android")
-                .put(SdkConstants.ABI_MIPS64, "mips64el-linux-android")
-                .build();
-
-        ARCHITECTURE_STRING = ImmutableMap.<String, String>builder()
-                .put(SdkConstants.ABI_INTEL_ATOM, SdkConstants.CPU_ARCH_INTEL_ATOM)
-                .put(SdkConstants.ABI_INTEL_ATOM64, SdkConstants.CPU_ARCH_INTEL_ATOM64)
-                .put(SdkConstants.ABI_ARMEABI_V7A, SdkConstants.CPU_ARCH_ARM)
-                .put(SdkConstants.ABI_ARMEABI, SdkConstants.CPU_ARCH_ARM)
-                .put(SdkConstants.ABI_ARM64_V8A, SdkConstants.CPU_ARCH_ARM64)
-                .put(SdkConstants.ABI_MIPS, SdkConstants.CPU_ARCH_MIPS)
-                .put(SdkConstants.ABI_MIPS64, SdkConstants.CPU_ARCH_MIPS64)
-                .build();
-
-        TOOLCHAIN_STRING = ImmutableMap.<String, String>builder()
-                .put("gcc", "")
-                .put("clang", "clang")
-                .build();
-    }
-
-    public NdkHandler(File projectDir, NdkExtension ndkExtension) {
-        this.ndkExtension = ndkExtension;
-        ndkDirectory = findNdkDirectory(projectDir);
-    }
-
-    /**
-     * Toolchain name used by the NDK.
-     *
-     * This is the name of the folder containing the toolchain under $ANDROID_NDK_HOME/toolchain.
-     * e.g. for gcc targetting arm64_v8a, this method returns "aarch64-linux-android-4.9".
-     */
-    private static String getToolchainName(
-            String toolchain,
-            String toolchainVersion,
-            String platform) {
-        return PLATFORM_STRING.get(platform) + "-" + TOOLCHAIN_STRING.get(toolchain)
-                + toolchainVersion;
-    }
-
-    /**
-     * Determine the location of the NDK directory.
-     *
-     * The NDK directory can be set in the local.properties file or using the ANDROID_NDK_HOME
-     * environment variable.
-     */
-    private static File findNdkDirectory(File projectDir) {
-        File localProperties = new File(projectDir, FN_LOCAL_PROPERTIES);
-
-        if (localProperties.isFile()) {
-
-            Properties properties = new Properties();
-            InputStreamReader reader = null;
-            try {
-                //noinspection IOResourceOpenedButNotSafelyClosed
-                FileInputStream fis = new FileInputStream(localProperties);
-                reader = new InputStreamReader(fis, Charsets.UTF_8);
-                properties.load(reader);
-            } catch (FileNotFoundException ignored) {
-                // ignore since we check up front and we don't want to fail on it anyway
-                // in case there's an env var.
-            } catch (IOException e) {
-                throw new RuntimeException(String.format("Unable to read %1$s.", localProperties), e);
-            } finally {
-                try {
-                    Closeables.close(reader, true /* swallowIOException */);
-                } catch (IOException e) {
-                    // ignore.
-                }
-            }
-
-            String ndkDirProp = properties.getProperty("ndk.dir");
-            if (ndkDirProp != null) {
-                return new File(ndkDirProp);
-            }
-
-        } else {
-            String envVar = System.getenv("ANDROID_NDK_HOME");
-            if (envVar != null) {
-                return new File(envVar);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the directory of the NDK.
-     */
-    @Nullable
-    public File getNdkDirectory() {
-        return ndkDirectory;
-    }
-
-    NdkExtension getNdkExtension() {
-        return ndkExtension;
-    }
-
-    /**
-     * Return the path containing the prebuilt toolchain.
-     *
-     * @param toolchain        Name of the toolchain ["gcc", "clang"].
-     * @param toolchainVersion Version of the toolchain.
-     * @param platform         Target platform supported by the NDK.
-     * @return Directory containing the prebuilt toolchain.
-     */
-
-    public File getToolchainPath(String toolchain, String toolchainVersion, String platform) {
-        File prebuiltFolder;
-        if (toolchain.equals("gcc")) {
-            prebuiltFolder = new File(
-                    getNdkDirectory(),
-                    "toolchains/" + getToolchainName(toolchain, toolchainVersion, platform)
-                            + "/prebuilt");
-
-        } else if (toolchain.equals("clang")) {
-            prebuiltFolder = new File(
-                    getNdkDirectory(),
-                    "toolchains/llvm-" + toolchainVersion + "/prebuilt");
-        } else {
-            throw new InvalidUserDataException("Unrecognized toolchain: " + toolchain);
-        }
-
-        String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
-        String hostOs;
-        if (osName.contains("windows")) {
-            hostOs = "windows";
-        } else if (osName.contains("mac")) {
-            hostOs = "darwin";
-        } else {
-            hostOs = "linux";
-        }
-
-        // There should only be one directory in the prebuilt folder.  If there are more than one
-        // attempt to determine the right one based on the operating system.
-        File[] toolchainPaths = prebuiltFolder.listFiles(
-                new FileFilter() {
-                    @Override
-                    public boolean accept(File file) {
-                        return file.isDirectory();
-                    }
-                });
-
-        if (toolchainPaths == null) {
-            throw new InvalidUserDataException("Unable to find toolchain: "
-                    + prebuiltFolder);
-        }
-        if (toolchainPaths.length == 1) {
-            return toolchainPaths[0];
-        }
-
-        // Use 64-bit toolchain if available.
-        File toolchainPath = new File(prebuiltFolder, hostOs + "-x86_64");
-        if (toolchainPath.isDirectory()) {
-            return toolchainPath;
-        }
-
-        // Fallback to 32-bit if we can't find the 64-bit toolchain.
-        String osString = (osName.equals("windows")) ? hostOs : hostOs + "-x86";
-        toolchainPath = new File(prebuiltFolder, osString);
-        if (toolchainPath.isDirectory()) {
-            return toolchainPath;
-        } else {
-            throw new InvalidUserDataException("Unable to find toolchain prebuilt folder in: "
-                    + prebuiltFolder);
-        }
-    }
-
-    /**
-     * Returns the sysroot directory for the toolchain.
-     */
-    public String getSysroot(NativePlatform platform) {
-        return ndkDirectory + "/platforms/" + ndkExtension.getCompileSdkVersion()
-                + "/arch-" + ARCHITECTURE_STRING.get(platform.getName());
-    }
-
-    /**
-     * Return the directory containing prebuilt binaries such as gdbserver.
-     */
-    public File getPrebuiltDirectory(NativePlatform platform) {
-        return new File(
-                ndkDirectory, "prebuilt/android-" + ARCHITECTURE_STRING.get(platform.getName()));
-    }
-
-    /**
-     * Return true if compiledSdkVersion supports 64 bits ABI.
-     */
-    public boolean supports64Bits() {
-        String targetString = getNdkExtension().getCompileSdkVersion().replace("android-", "");
-        try {
-            return Integer.parseInt(targetString) >= 20;
-        } catch (NumberFormatException ignored) {
-            // "android-L" supports 64-bits.
-            return true;
-        }
-    }
-
-    /**
-     * Return the gcc version that will be used by the NDK.
-     *
-     * If the gcc toolchain is used, then it's simply the toolchain version requested by the user.
-     * If clang is used, then it depends the target abi.
-     */
-    public String getGccToolchainVersion(String abi) {
-        String toolchain = ndkExtension.getToolchain();
-        if (toolchain.equals("gcc")) {
-            return (toolchain.equals(NdkExtensionConvention.DEFAULT_TOOLCHAIN))
-                    ? ToolchainConfiguration.getDefaultToolchainVersion(toolchain, abi)
-                    : ndkExtension.getToolchainVersion();
-        } else {
-            return is64Bits(abi) ? DEFAULT_LLVM_GCC64_VERSION : DEFAULT_LLVM_GCC32_VERSION;
-        }
-    }
-
-    /**
-     * Returns a list of supported ABI.
-     */
-    public Collection<String> getSupportedAbis() {
-        return supports64Bits() ? ALL_ABI : ABI32;
-    }
-
-    /**
-     * Return whether the specified abi is 64 bits.
-     */
-    public static boolean is64Bits(String abi) {
-        return !ABI32.contains(abi);
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java
index a83954e..63127c5 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java
@@ -16,8 +16,11 @@
 
 package com.android.build.gradle.ndk.internal;
 
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
+
+import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidProject;
 import com.android.utils.StringHelper;
 import com.google.common.base.Joiner;
 
@@ -38,7 +41,7 @@
                 buildDir,
                 String.format(
                         "%s/objectFiles/%s/%s",
-                        AndroidProject.FD_INTERMEDIATES ,
+                        FD_INTERMEDIATES ,
                         binary.getName(),
                         sourceSetName));
     }
@@ -68,14 +71,47 @@
         }
     }
 
-    public static String getOutputDirectoryName(NativeBinarySpec binary) {
+    public static String getNdkBuildTaskName(@NonNull NativeBinarySpec binary) {
+        return getTaskName(binary, "ndkBuild");
+    }
+
+    /**
+     * Return the name of the directory that will contain the final output of the native binary.
+     */
+    public static String getOutputDirectoryName(String buildType, String productFlavor, String abi) {
         return Joiner.on(File.separator).join(
-                AndroidProject.FD_INTERMEDIATES,
+                FD_INTERMEDIATES,
                 "binaries",
-                binary.getName(),
+                buildType,
+                productFlavor,
+                "lib",
+                abi);
+    }
+
+    public static String getOutputDirectoryName(NativeBinarySpec binary) {
+        return getOutputDirectoryName(
                 binary.getBuildType().getName(),
                 binary.getFlavor().getName(),
-                "lib",
+                binary.getTargetPlatform().getName());
+    }
+
+    /**
+     * Return the name of the directory that will contain the native library with debug symbols.
+     */
+    public static String getDebugLibraryDirectoryName(String buildType, String productFlavor, String abi) {
+        return Joiner.on(File.separator).join(
+                FD_INTERMEDIATES,
+                "binaries",
+                buildType,
+                productFlavor,
+                "obj",
+                abi);
+    }
+
+    public static String getDebugLibraryDirectoryName(NativeBinarySpec binary) {
+        return getDebugLibraryDirectoryName(
+                binary.getBuildType().getName(),
+                binary.getFlavor().getName(),
                 binary.getTargetPlatform().getName());
     }
 
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.groovy
deleted file mode 100644
index 53a456c..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.groovy
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.TaskContainer
-import org.gradle.nativeplatform.SharedLibraryBinarySpec;
-
-/**
- * Configuration to setup STL for NDK.
- */
-public class StlConfiguration {
-    static final String DEFAULT_STL = "system"
-    static final String[] VALID_STL = [
-            "system",
-            "stlport_static",
-            "stlport_shared",
-            "gnustl_static",
-            "gnustl_shared",
-            "gabi++_static",
-            "gabi++_shared",
-            "c++_static",
-            "c++_shared",
-    ]
-
-    static final Map<String, Collection<String>> STL_SOURCES = [
-            "system" : [
-                    "system/include"
-            ],
-            "stlport" : [
-                    "stlport/stlport",
-                    "gabi++/include",
-            ],
-            "gnustl" : [
-                    "gnu-libstdc++",
-                    "gnu-libstdc++/4.6/include",
-                    "gnu-libstdc++/4.6/libs/armeabi-v7a/include",
-                    "gnu-libstdc++/4.6/include/backward",
-            ],
-            "gabi++" : [
-                    "gabi++",
-                    "gabi++/include",
-            ],
-            "c++" : [
-                    "../android/support/include",
-                    "llvm-libc++",
-                    "../android/compiler-rt",
-                    "llvm-libc++/libcxx/include",
-                    "gabi++/include",
-                    "../android/support/include",
-            ],
-    ]
-
-    public static File getStlBaseDirectory(NdkHandler ndkHandler) {
-        return new File(ndkHandler.getNdkDirectory(), "sources/cxx-stl/");
-    }
-
-    public static Collection<String> getStlSources(NdkHandler ndkHandler, String stl) {
-        String stlBase = getStlBaseDirectory(ndkHandler);
-        String stlName = stl.equals("system") ? "system" : stl.substring(0, stl.indexOf('_'));
-        return STL_SOURCES[stlName].collect { String sourceDir ->
-            stlBase.toString() + "/" + sourceDir
-        }
-    }
-
-
-    public static void checkStl(String stl) {
-        if (!VALID_STL.contains(stl)) {
-            throw new InvalidUserDataException("Invalid STL: $stl")
-        }
-    }
-
-    public static void apply(
-            NdkHandler ndkHandler,
-            String stl,
-            TaskContainer tasks,
-            File buildDir,
-            SharedLibraryBinarySpec binary) {
-        StlNativeToolSpecification stlConfig =
-                new StlNativeToolSpecification(ndkHandler, stl, binary.targetPlatform)
-        stlConfig.apply(binary)
-
-        if (stl.endsWith("_shared")) {
-            Task copySharedLib = tasks.create(
-                    name: NdkNamingScheme.getTaskName(binary, "copy", "StlSo"),
-                    type: Copy) {
-                from(stlConfig.getStlLib(binary.targetPlatform.name))
-                into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)))
-            }
-            binary.builtBy copySharedLib
-        }
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.java
new file mode 100644
index 0000000..d9da1ab
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.NdkHandler;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ListMultimap;
+
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.Copy;
+import org.gradle.model.ModelMap;
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Configuration to setup STL for NDK.
+ */
+public class StlConfiguration {
+
+    private static final String DEFAULT_STL = "system";
+
+    private static final List<String> VALID_STL = ImmutableList.of(
+            "system",
+            "stlport_static",
+            "stlport_shared",
+            "gnustl_static",
+            "gnustl_shared",
+            "gabi++_static",
+            "gabi++_shared",
+            "c++_static",
+            "c++_shared");
+
+    private static final ListMultimap<String, String> STL_SOURCES =
+            ImmutableListMultimap.<String, String>builder()
+                    .putAll("system", ImmutableList.of(
+                            "system/include"))
+                    .putAll("stlport", ImmutableList.of(
+                            "stlport/stlport",
+                            "gabi++/include"))
+                    .putAll("gnustl", ImmutableList.of(
+                            "gnu-libstdc++",
+                            "gnu-libstdc++/4.6/include",
+                            "gnu-libstdc++/4.6/libs/armeabi-v7a/include",
+                            "gnu-libstdc++/4.6/include/backward"))
+                    .putAll("gabi++", ImmutableList.of(
+                            "gabi++",
+                            "gabi++/include"))
+                    .putAll("c++", ImmutableList.of(
+                            "../android/support/include",
+                            "llvm-libc++",
+                            "../android/compiler-rt",
+                            "llvm-libc++/libcxx/include",
+                            "gabi++/include",
+                            "../android/support/include"))
+                    .build();
+
+    public static File getStlBaseDirectory(NdkHandler ndkHandler) {
+        return new File(ndkHandler.getNdkDirectory(), "sources/cxx-stl/");
+    }
+
+    public static Collection<String> getStlSources(NdkHandler ndkHandler, String stl) {
+        final File stlBase = getStlBaseDirectory(ndkHandler);
+        String stlName = stl.equals("system") ? "system" : stl.substring(0, stl.indexOf('_'));
+
+        ImmutableList.Builder<String> builder = ImmutableList.builder();
+        for (String sourceDir : STL_SOURCES.get(stlName)) {
+            builder.add(stlBase.toString() + "/" + sourceDir);
+        }
+        return builder.build();
+    }
+
+    public static void checkStl(String stl) {
+        if (!VALID_STL.contains(stl)) {
+            throw new InvalidUserDataException("Invalid STL: " + stl);
+        }
+    }
+
+    public static void createStlCopyTask(
+            @NonNull ModelMap<Task> tasks,
+            @NonNull final NativeBinarySpec binary,
+            @NonNull final File buildDir,
+            @NonNull NdkHandler ndkHandler,
+            @NonNull String stl,
+            @NonNull String buildTaskName) {
+        if (stl.endsWith("_shared")) {
+            final StlNativeToolSpecification stlConfig = new StlNativeToolSpecification(ndkHandler,
+                    stl, binary.getTargetPlatform());
+
+            final String copyTaskName = NdkNamingScheme.getTaskName(binary, "copy", "StlSo");
+            tasks.create(copyTaskName, Copy.class, new Action<Copy>() {
+                @Override
+                public void execute(Copy copy) {
+                    copy.from(stlConfig.getStlLib(binary.getTargetPlatform().getName()));
+                    copy.into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)));
+
+                }
+            });
+            tasks.named(buildTaskName, new Action<Task>() {
+                @Override
+                public void execute(Task task) {
+                    task.dependsOn(copyTaskName);
+                }
+            });
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java
index b401374..758b0e2 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java
@@ -16,6 +16,8 @@
 
 package com.android.build.gradle.ndk.internal;
 
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
 import com.google.common.collect.Lists;
 
 import org.gradle.nativeplatform.platform.NativePlatform;
@@ -53,31 +55,14 @@
 
         List<String> cppFlags = Lists.newArrayList();
 
-        List<String> includeDirs = Lists.newArrayList();
-        if (stlName.equals("system")) {
-            includeDirs.add("system/include");
-        } else if (stlName.equals("stlport")) {
-            includeDirs.add("stlport/stlport");
-            includeDirs.add("gabi++/include");
-        } else if (stlName.equals("gnustl")) {
-            String gccToolchainVersion = ndkHandler.getGccToolchainVersion(platform.getName());
-            includeDirs.add("gnu-libstdc++/" + gccToolchainVersion + "/include");
-            includeDirs.add("gnu-libstdc++/" + gccToolchainVersion +
-                    "/libs/" + platform.getName() + "/include");
-            includeDirs.add("gnu-libstdc++/" + gccToolchainVersion +
-                    "/include/backward");
-        } else if (stlName.equals("gabi++")) {
-            includeDirs.add("gabi++/include");
-        } else if (stlName.equals("c++")) {
-            includeDirs.add("llvm-libc++/libcxx/include");
-            includeDirs.add("gabi++/include");
-            includeDirs.add("../android/support/include");
+        if (stlName.equals("c++")) {
             cppFlags.add("-std=c++11");
         }
 
-        for (String dir : includeDirs) {
-            cppFlags.add("-I" +
-                    new File(StlConfiguration.getStlBaseDirectory(ndkHandler), dir).toString());
+        List<File> includeDirs = ndkHandler.getStlIncludes(stlName, Abi.getByName(
+                platform.getName()));
+        for (File dir : includeDirs) {
+            cppFlags.add("-I" + dir.toString());
         }
         return cppFlags;
     }
@@ -97,7 +82,7 @@
         if (stlName.equals("stlport")) {
             stlLib = "stlport";
         } else if (stlName.equals("gnustl")) {
-            stlLib = "gnu-libstdc++/" + ndkHandler.getGccToolchainVersion(abi);
+            stlLib = "gnu-libstdc++/" + ndkHandler.getGccToolchainVersion(Abi.getByName(abi));
         } else if (stlName.equals("gabi++")) {
             stlLib = "gabi++";
         } else if (stlName.equals("c++")) {
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.groovy
deleted file mode 100644
index 0dc78ce..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.groovy
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 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.ndk.internal
-
-import com.android.SdkConstants
-import org.gradle.nativeplatform.platform.NativePlatform
-import org.gradle.nativeplatform.toolchain.Clang
-import org.gradle.nativeplatform.toolchain.Gcc
-import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
-import org.gradle.platform.base.PlatformContainer
-
-/**
- * Action to configure toolchain for native binaries.
- */
-class ToolchainConfiguration {
-
-    private static final String DEFAULT_GCC32_VERSION="4.6"
-    private static final String DEFAULT_GCC64_VERSION="4.9"
-    private static final String DEFAULT_LLVM_VERSION="3.4"
-
-    private static final GCC_PREFIX = [
-            (SdkConstants.ABI_INTEL_ATOM) : "i686-linux-android",
-            (SdkConstants.ABI_INTEL_ATOM64) : "x86_64-linux-android",
-            (SdkConstants.ABI_ARMEABI_V7A) : "arm-linux-androideabi",
-            (SdkConstants.ABI_ARMEABI) : "arm-linux-androideabi",
-            (SdkConstants.ABI_ARM64_V8A) : "aarch64-linux-android",
-            (SdkConstants.ABI_MIPS) : "mipsel-linux-android",
-            (SdkConstants.ABI_MIPS64) : "mips64el-linux-android"
-    ]
-
-    public static void configurePlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
-        List<String> abiList = ndkHandler.getSupportedAbis();
-        for (String abi : abiList) {
-            NativePlatform platform = platforms.maybeCreate(abi, NativePlatform)
-
-            // All we care is the name of the platform.  It doesn't matter what the
-            // architecture is, but it must be set to non-x86 so that it does not match
-            // the default supported platform.
-            platform.architecture "ppc"
-            platform.operatingSystem "linux"
-        }
-    }
-
-    /**
-     * Return the default version of the specified toolchain for a target abi.
-     */
-    public static String getDefaultToolchainVersion(String toolchain, String abi) {
-        if (NdkHandler.is64Bits(abi)) {
-            return (toolchain.equals("gcc")) ? DEFAULT_GCC64_VERSION : DEFAULT_LLVM_VERSION
-        } else {
-            return (toolchain.equals("gcc")) ? DEFAULT_GCC32_VERSION : DEFAULT_LLVM_VERSION
-        }
-    }
-
-    /**
-     * Configure toolchain for a platform.
-     */
-    public static void configureToolchain(
-            NativeToolChainRegistry toolchains,
-            String toolchainName,
-            String toolchainVersion,
-            NdkHandler ndkHandler) {
-        toolchains.create("ndk-" + toolchainName, toolchainName.equals("gcc") ? Gcc : Clang) {
-            // Configure each platform.
-            List<String> abiList = ndkHandler.getSupportedAbis();
-            for (String abi: abiList) {
-                String platform = abi
-                String localToolchainVersion = toolchainVersion
-
-                if (localToolchainVersion.equals(NdkExtensionConvention.DEFAULT_TOOLCHAIN_VERSION)) {
-                    localToolchainVersion = getDefaultToolchainVersion(toolchainName, platform);
-                }
-
-                target(platform) {
-                    if (toolchainName.equals("gcc")) {
-                        cCompiler.setExecutable("${GCC_PREFIX[platform]}-gcc")
-                        cppCompiler.setExecutable("${GCC_PREFIX[platform]}-g++")
-                        linker.setExecutable("${GCC_PREFIX[platform]}-g++")
-                        assembler.setExecutable("${GCC_PREFIX[platform]}-as")
-                        staticLibArchiver.setExecutable("${GCC_PREFIX[platform]}-ar")
-                    }
-
-                    // By default, gradle will use -Xlinker to pass arguments to the linker.
-                    // Removing it as it prevents -sysroot from being properly set.
-                    linker.withArguments { List<String> args ->
-                        args.removeAll("-Xlinker")
-                    }
-
-                    String bin = (
-                            ndkHandler.getToolchainPath(toolchainName, localToolchainVersion, platform).
-                                    toString()
-                                    + "/bin")
-                    path bin
-                }
-            }
-        }
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.java
new file mode 100644
index 0000000..b500610
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 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.ndk.internal;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.core.Toolchain;
+
+import org.gradle.api.Action;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.Clang;
+import org.gradle.nativeplatform.toolchain.Gcc;
+import org.gradle.nativeplatform.toolchain.GccCompatibleToolChain;
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
+import org.gradle.platform.base.PlatformContainer;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Action to configure toolchain for native binaries.
+ */
+public class ToolchainConfiguration {
+
+    public static void configurePlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
+        for (Abi abi : ndkHandler.getSupportedAbis()) {
+            NativePlatform platform = platforms.maybeCreate(abi.getName(), NativePlatform.class);
+
+            // All we care is the name of the platform.  It doesn't matter what the
+            // architecture is, but it must be set to non-x86 so that it does not match
+            // the default supported platform.
+            platform.architecture("ppc");
+            platform.operatingSystem("linux");
+        }
+
+    }
+
+    /**
+     * Configure toolchain for a platform.
+     */
+    public static void configureToolchain(
+            NativeToolChainRegistry toolchainRegistry,
+            final String toolchainName,
+            final NdkHandler ndkHandler) {
+        final Toolchain ndkToolchain = Toolchain.getByName(toolchainName);
+        toolchainRegistry.create("ndk-" + toolchainName,
+                toolchainName.equals("gcc") ? Gcc.class : Clang.class,
+                new Action<GccCompatibleToolChain>() {
+                    @Override
+                    public void execute(GccCompatibleToolChain toolchain) {
+                        // Configure each platform.
+                        for (Abi it : ndkHandler.getSupportedAbis()) {
+                            final Abi abi = it;
+                            toolchain.target(abi.getName(), new Action<GccPlatformToolChain>() {
+                                @Override
+                                public void execute(GccPlatformToolChain targetPlatform) {
+                                    if (Toolchain.GCC.equals(ndkToolchain)) {
+                                        String gccPrefix = abi.getGccExecutablePrefix();
+                                        targetPlatform.getcCompiler()
+                                                .setExecutable(gccPrefix + "-gcc");
+                                        targetPlatform.getCppCompiler()
+                                                .setExecutable(gccPrefix + "-g++");
+                                        targetPlatform.getLinker()
+                                                .setExecutable(gccPrefix + "-g++");
+                                        targetPlatform.getAssembler()
+                                                .setExecutable(gccPrefix + "-as");
+                                        targetPlatform.getStaticLibArchiver()
+                                                .setExecutable(gccPrefix + "-ar");
+                                    }
+
+                                    // By default, gradle will use -Xlinker to pass arguments to the linker.
+                                    // Removing it as it prevents -sysroot from being properly set.
+                                    targetPlatform.getLinker().withArguments(
+                                            new Action<List<String>>() {
+                                                @Override
+                                                public void execute(List<String> args) {
+                                                    args.removeAll(Collections.singleton("-Xlinker"));
+                                                }
+                                            });
+                                }
+
+                            });
+                            toolchain.path(ndkHandler.getCCompiler(abi).getParentFile());
+                        }
+                    }
+                });
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.groovy
deleted file mode 100644
index 4295259..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.groovy
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2014 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.build.gradle.internal.tasks.DefaultAndroidTask
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.ndk.internal.NdkHandler
-import com.android.build.gradle.ndk.internal.StlConfiguration
-import com.google.common.base.Charsets
-import com.google.common.collect.Sets
-import com.google.common.io.Files
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.TaskAction
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.nativeplatform.NativeBinarySpec
-
-/**
- * Task to create gdb.setup for native code debugging.
- */
-class GdbSetupTask extends DefaultAndroidTask {
-    @Input
-    NdkHandler ndkHandler
-
-    @Input
-    NdkExtension extension
-
-    @Input
-    NativeBinarySpec binary
-
-    @Input
-    File outputDir
-
-    @TaskAction
-    void taskAction() {
-        File gdbSetupFile = new File(outputDir, "gdb.setup")
-
-        StringBuilder sb = new StringBuilder()
-
-        sb.append("set solib-search-path ${outputDir.toString()}\n")
-        sb.append("directory ")
-        sb.append("${ndkHandler.getSysroot(binary.targetPlatform)}/usr/include ")
-
-        Set<String> sources = Sets.newHashSet();
-        binary.getSource().withType(CSourceSet) { sourceSet ->
-            sources.addAll(sourceSet.source.srcDirs*.toString())
-        }
-        binary.getSource().withType(CppSourceSet) { sourceSet ->
-            sources.addAll(sourceSet.source.srcDirs*.toString())
-        }
-        sources.addAll(StlConfiguration.getStlSources(ndkHandler, extension.stl))
-        sb.append(sources.join(" "))
-
-        if (!outputDir.exists()) {
-            outputDir.mkdirs()
-        }
-        Files.write(sb.toString(), gdbSetupFile, Charsets.UTF_8)
-    }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.java
new file mode 100644
index 0000000..3e3866e
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 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.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.ndk.internal.StlConfiguration;
+import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Task to create gdb.setup for native code debugging.
+ */
+public class GdbSetupTask extends DefaultTask {
+
+    private NdkHandler ndkHandler;
+
+    private NdkConfig extension;
+
+    private NativeBinarySpec binary;
+
+    private File outputDir;
+
+
+    // ----- PUBLIC TASK API -----
+
+    @Input
+    public File getOutputDir() {
+        return outputDir;
+    }
+
+    public void setOutputDir(File outputDir) {
+        this.outputDir = outputDir;
+    }
+
+    // ----- PRIVATE TASK API -----
+
+    @Input
+    public NdkHandler getNdkHandler() {
+        return ndkHandler;
+    }
+
+    public void setNdkHandler(NdkHandler ndkHandler) {
+        this.ndkHandler = ndkHandler;
+    }
+
+    @Input
+    public NdkConfig getExtension() {
+        return extension;
+    }
+
+    public void setExtension(NdkConfig extension) {
+        this.extension = extension;
+    }
+
+    @Input
+    public NativeBinarySpec getBinary() {
+        return binary;
+    }
+
+    public void setBinary(NativeBinarySpec binary) {
+        this.binary = binary;
+    }
+
+    @TaskAction
+    public void taskAction() {
+        File gdbSetupFile = new File(outputDir, "gdb.setup");
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("set solib-search-path ")
+                .append(outputDir.toString())
+                .append("\n")
+                .append("directory ")
+                .append(ndkHandler.getSysroot(Abi.getByName(binary.getTargetPlatform().getName())))
+                .append("/usr/include ");
+
+        final Set<String> sources = Sets.newHashSet();
+        binary.getSource().withType(CSourceSet.class, new Action<CSourceSet>() {
+            @Override
+            public void execute(CSourceSet sourceSet) {
+                for (File src : sourceSet.getSource().getSrcDirs()) {
+                    sources.add(src.toString());
+                }
+            }
+        });
+        binary.getSource().withType(CppSourceSet.class, new Action<CppSourceSet>() {
+            @Override
+            public void execute(CppSourceSet sourceSet) {
+                for (File src : sourceSet.getSource().getSrcDirs()) {
+                    sources.add(src.toString());
+                }
+            }
+        });
+        sources.addAll(StlConfiguration.getStlSources(ndkHandler, extension.getStl()));
+        sb.append(Joiner.on(' ').join(sources));
+
+        if (!outputDir.exists()) {
+            outputDir.mkdirs();
+        }
+
+        try {
+            Files.write(sb.toString(), gdbSetupFile, Charsets.UTF_8);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/StripDebugSymbolTask.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/StripDebugSymbolTask.java
new file mode 100644
index 0000000..ff5b96b
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/StripDebugSymbolTask.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 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.LoggerWrapper;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.process.GradleProcessExecutor;
+import com.android.build.gradle.ndk.internal.NdkNamingScheme;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessInfoBuilder;
+
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+
+import java.io.File;
+
+/**
+ * Task to remove debug symbols from a native library.
+ */
+public class StripDebugSymbolTask extends DefaultTask {
+
+    private File stripCommand;
+
+    private File inputFile;
+
+    private File outputFile;
+
+    // ----- PUBLIC API -----
+
+    @Input
+    public File getStripCommand() {
+        return stripCommand;
+    }
+
+    public void setStripCommand(File stripCommand) {
+        this.stripCommand = stripCommand;
+    }
+
+    @Optional
+    @InputFile
+    public File getInputFile() {
+        // If source set is empty, the file debuggable library is not generated.
+        return inputFile.exists() ? inputFile : null;
+    }
+
+    public void setInputFile(File inputFile) {
+        this.inputFile = inputFile;
+    }
+
+    @OutputFile
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    // ----- PRIVATE API -----
+
+    @TaskAction
+    void taskAction() throws ProcessException {
+        if (getInputFile() == null) {
+            return;
+        }
+
+        if (!outputFile.getParentFile().exists()) {
+            outputFile.getParentFile().mkdirs();
+        }
+
+        ProcessInfoBuilder builder = new ProcessInfoBuilder();
+        builder.setExecutable(stripCommand);
+        builder.addArgs("--strip-unneeded");
+        builder.addArgs("-o");
+        builder.addArgs(outputFile.toString());
+        builder.addArgs(inputFile.toString());
+        new GradleProcessExecutor(getProject()).execute(
+                builder.createProcess(),
+                new LoggedProcessOutputHandler(new LoggerWrapper(getLogger())));
+    }
+
+    // ----- ConfigAction -----
+
+    public static class ConfigAction implements Action<StripDebugSymbolTask> {
+        @NonNull
+        private final SharedLibraryBinarySpec binary;
+        @NonNull
+        private final File buildDir;
+        @NonNull
+        private final NdkHandler handler;
+
+        public ConfigAction(
+                @NonNull SharedLibraryBinarySpec binary,
+                @NonNull File buildDir,
+                @NonNull NdkHandler handler) {
+            this.binary = binary;
+            this.buildDir = buildDir;
+            this.handler = handler;
+        }
+
+        @Override
+        public void execute(@NonNull StripDebugSymbolTask task) {
+            File debugLib = binary.getSharedLibraryFile();
+            task.setInputFile(debugLib);
+            task.setOutputFile(new File(
+                    buildDir,
+                    NdkNamingScheme.getOutputDirectoryName(binary) + "/"
+                            + debugLib.getName()));
+            task.setStripCommand(handler.getStripCommand(
+                    Abi.getByName(binary.getTargetPlatform().getName())));
+            task.dependsOn(binary);
+        }
+    }
+}
diff --git a/build-system/gradle/build.gradle b/build-system/gradle/build.gradle
index 23875d1..5bcf41f 100644
--- a/build-system/gradle/build.gradle
+++ b/build-system/gradle/build.gradle
@@ -1,16 +1,8 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'groovy'
 apply plugin: 'clone-artifacts'
 apply plugin: 'idea'
 apply plugin: 'jacoco'
+apply plugin: 'license-report'
 
 dependencies {
     compile project(':base:gradle-core')
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/AppExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppExtension.java
new file mode 100644
index 0000000..35bde14
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppExtension.java
@@ -0,0 +1,48 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.ApplicationVariant;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * 'android' extension for 'com.android.application' project.
+ */
+public class AppExtension extends TestedExtension {
+
+    private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList
+            = new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class);
+
+    public AppExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+            @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+                signingConfigs, extraModelInfo, isLibrary);
+    }
+
+    /**
+     * Returns the list of Application variants. Since the collections is built after evaluation, it
+     * should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
+        return applicationVariantList;
+    }
+
+    @Override
+    public void addVariant(BaseVariant variant) {
+        applicationVariantList.add((ApplicationVariant) variant);
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
index 9ef7d06..aba323e 100644
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
@@ -25,7 +25,6 @@
 import com.android.builder.core.AndroidBuilder
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.tasks.TaskContainer
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
 
@@ -49,7 +48,7 @@
     protected TaskManager createTaskManager(
             Project project,
             AndroidBuilder androidBuilder,
-            BaseExtension extension,
+            AndroidConfig extension,
             SdkHandler sdkHandler,
             DependencyManager dependencyManager,
             ToolingModelBuilderRegistry toolingRegistry) {
@@ -68,7 +67,7 @@
     }
 
     @Override
-    protected VariantFactory getVariantFactory() {
+    protected VariantFactory createVariantFactory() {
         return new ApplicationVariantFactory(instantiator, androidBuilder, extension)
     }
 }
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
new file mode 100644
index 0000000..b4f4bb0
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
@@ -0,0 +1,799 @@
+/*
+ * 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;
+
+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.VariantFilter;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.SourceSetSourceProviderWrapper;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.model.SourceProvider;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.repository.FullRevision;
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Base 'android' extension for all android plugins.
+ *
+ * <p>This is never used directly. Instead,
+ *<ul>
+ * <li>Plugin <code>com.android.application</code> uses {@link AppExtension}</li>
+ * <li>Plugin <code>com.android.library</code> uses {@link LibraryExtension}</li>
+ * <li>Plugin <code>com.android.test</code> uses {@link TestedExtension}</li>
+ * </ul>
+ */
+@SuppressWarnings("UnnecessaryInheritDoc")
+public abstract class BaseExtension implements AndroidConfig {
+
+    private String target;
+    private FullRevision buildToolsRevision;
+    private List<LibraryRequest> libraryRequests = Lists.newArrayList();
+
+    /** Default config, shared by all flavors. */
+    final ProductFlavor defaultConfig;
+
+    /** Options for aapt, tool for packaging resources. */
+    final AaptOptions aaptOptions;
+
+    /** Lint options. */
+    final LintOptions lintOptions;
+
+    /** Dex options. */
+    final DexOptions dexOptions;
+
+    /** Options for running tests. */
+    final TestOptions testOptions;
+
+    /** Compile options */
+    final CompileOptions compileOptions;
+
+    /** Packaging options. */
+    final PackagingOptions packagingOptions;
+
+    /** Options to control resources preprocessing. Not finalized yet.*/
+    final PreprocessingOptions preprocessingOptions;
+
+    /** JaCoCo options. */
+    final JacocoExtension jacoco;
+
+    /**
+     * APK splits options.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
+     */
+    final Splits splits;
+
+    /** All product flavors used by this project. */
+    final NamedDomainObjectContainer<CoreProductFlavor> productFlavors;
+
+    /** Build types used by this project. */
+    final NamedDomainObjectContainer<BuildType> buildTypes;
+
+    /** Signing configs used by this project. */
+    final NamedDomainObjectContainer<SigningConfig> signingConfigs;
+
+    private ExtraModelInfo extraModelInfo;
+
+    protected Project project;
+
+    /** Adb options */
+    final AdbOptions adbOptions;
+
+    /** A prefix to be used when creating new resources. Used by Studio */
+    String resourcePrefix;
+
+    List<String> flavorDimensionList;
+
+    private String defaultPublishConfig = "release";
+    private boolean publishNonDefault = false;
+
+    private Action<VariantFilter> variantFilter;
+
+    private final List<DeviceProvider> deviceProviderList = Lists.newArrayList();
+    private final List<TestServer> testServerList = Lists.newArrayList();
+
+    private final AndroidBuilder androidBuilder;
+
+    private final SdkHandler sdkHandler;
+
+    protected Logger logger;
+
+    private boolean isWritable = true;
+
+    /**
+     * The source sets container.
+     */
+    final NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer;
+
+    BaseExtension(
+            @NonNull final ProjectInternal project,
+            @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder,
+            @NonNull SdkHandler sdkHandler,
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+            @NonNull ExtraModelInfo extraModelInfo,
+            final boolean isLibrary) {
+        this.androidBuilder = androidBuilder;
+        this.sdkHandler = sdkHandler;
+        this.buildTypes = buildTypes;
+        //noinspection unchecked
+        this.productFlavors = (NamedDomainObjectContainer) productFlavors;
+        this.signingConfigs = signingConfigs;
+        this.extraModelInfo = extraModelInfo;
+        this.project = project;
+
+        logger = Logging.getLogger(this.getClass());
+
+        defaultConfig = instantiator.newInstance(ProductFlavor.class, BuilderConstants.MAIN,
+                project, instantiator, project.getLogger());
+
+        aaptOptions = instantiator.newInstance(AaptOptions.class);
+        dexOptions = instantiator.newInstance(DexOptions.class);
+        lintOptions = instantiator.newInstance(LintOptions.class);
+        testOptions = instantiator.newInstance(TestOptions.class);
+        compileOptions = instantiator.newInstance(CompileOptions.class);
+        packagingOptions = instantiator.newInstance(PackagingOptions.class);
+        preprocessingOptions = instantiator.newInstance(PreprocessingOptions.class);
+        jacoco = instantiator.newInstance(JacocoExtension.class);
+        adbOptions = instantiator.newInstance(AdbOptions.class);
+        splits = instantiator.newInstance(Splits.class, instantiator);
+
+        sourceSetsContainer = project.container(AndroidSourceSet.class,
+                new AndroidSourceSetFactory(instantiator, project, isLibrary));
+
+        sourceSetsContainer.whenObjectAdded(new Action<AndroidSourceSet>() {
+            @Override
+            public void execute(AndroidSourceSet sourceSet) {
+                ConfigurationContainer configurations = project.getConfigurations();
+
+                createConfiguration(
+                        configurations,
+                        sourceSet.getCompileConfigurationName(),
+                        "Classpath for compiling the " + sourceSet.getName() + " sources.");
+
+                String packageConfigDescription;
+                if (isLibrary) {
+                    packageConfigDescription
+                            = "Classpath only used when publishing '" + sourceSet.getName() + "'.";
+                } else {
+                    packageConfigDescription
+                            = "Classpath packaged with the compiled '" + sourceSet.getName() + "' classes.";
+                }
+                createConfiguration(
+                        configurations,
+                        sourceSet.getPackageConfigurationName(),
+                        packageConfigDescription);
+
+                createConfiguration(
+                        configurations,
+                        sourceSet.getProvidedConfigurationName(),
+                        "Classpath for only compiling the " + sourceSet.getName() + " sources.");
+
+                createConfiguration(
+                        configurations,
+                        sourceSet.getWearAppConfigurationName(),
+                        "Link to a wear app to embed for object '" + sourceSet.getName() + "'.");
+
+                sourceSet.setRoot(String.format("src/%s", sourceSet.getName()));
+            }
+        });
+
+        sourceSetsContainer.create(defaultConfig.getName());
+    }
+
+    /**
+     * Disallow further modification on the extension.
+     */
+    public void disableWrite() {
+        isWritable = false;
+    }
+
+    protected void checkWritability() {
+        if (!isWritable) {
+            throw new GradleException(
+                    "Android tasks have already been created.\n" +
+                            "This happens when calling android.applicationVariants,\n" +
+                            "android.libraryVariants or android.testVariants.\n" +
+                            "Once these methods are called, it is not possible to\n" +
+                            "continue configuring the model.");
+        }
+    }
+
+    protected void createConfiguration(
+            @NonNull ConfigurationContainer configurations,
+            @NonNull String configurationName,
+            @NonNull String configurationDescription) {
+        logger.info("Creating configuration {}", configurationName);
+
+        Configuration configuration = configurations.findByName(configurationName);
+        if (configuration == null) {
+            configuration = configurations.create(configurationName);
+        }
+        configuration.setVisible(false);
+        configuration.setDescription(configurationDescription);
+    }
+
+    /**
+     * Sets the compile SDK version, based on full SDK version string, e.g.
+     * <code>android-21</code> for Lollipop.
+     */
+    public void compileSdkVersion(String version) {
+        checkWritability();
+        this.target = version;
+    }
+
+    /**
+     * Sets the compile SDK version, based on API level, e.g. 21 for Lollipop.
+     */
+    public void compileSdkVersion(int apiLevel) {
+        compileSdkVersion("android-" + apiLevel);
+    }
+
+    public void setCompileSdkVersion(int apiLevel) {
+        compileSdkVersion(apiLevel);
+    }
+
+    public void setCompileSdkVersion(String target) {
+        compileSdkVersion(target);
+    }
+
+    /**
+     * Request the use a of Library. The library is then added to the classpath.
+     * @param name the name of the library.
+     */
+    public void useLibrary(String name) {
+        useLibrary(name, true);
+    }
+
+    /**
+     * Request the use a of Library. The library is then added to the classpath.
+     * @param name the name of the library.
+     * @param required if using the library requires a manifest entry, the  entry will
+     * indicate that the library is not required.
+     */
+    public void useLibrary(String name, boolean required) {
+        libraryRequests.add(new LibraryRequest(name, required));
+    }
+
+    public void buildToolsVersion(String version) {
+        checkWritability();
+        buildToolsRevision = FullRevision.parseRevision(version);
+    }
+
+    /**
+     * <strong>Required.</strong> Version of the build tools to use.
+     *
+     * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
+     * back may give a slightly different string.
+     */
+    @Override
+    public String getBuildToolsVersion() {
+        return buildToolsRevision.toString();
+    }
+
+    public void setBuildToolsVersion(String version) {
+        buildToolsVersion(version);
+    }
+
+    /**
+     * Configures the build types.
+     */
+    public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
+        checkWritability();
+        action.execute(buildTypes);
+    }
+
+    /**
+     * Configures the product flavors.
+     */
+    public void productFlavors(Action<? super NamedDomainObjectContainer<CoreProductFlavor>> action) {
+        checkWritability();
+        action.execute(productFlavors);
+    }
+
+    /**
+     * Configures the signing configs.
+     */
+    public void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {
+        checkWritability();
+        action.execute(signingConfigs);
+    }
+
+    public void flavorDimensions(String... dimensions) {
+        checkWritability();
+        flavorDimensionList = Arrays.asList(dimensions);
+    }
+
+    /**
+     * Configures the source sets. Note that the Android plugin uses its own implementation of
+     * source sets, {@link AndroidSourceSet}.
+     */
+    public void sourceSets(Action<NamedDomainObjectContainer<AndroidSourceSet>> action) {
+        checkWritability();
+        action.execute(sourceSetsContainer);
+    }
+
+    /**
+     * All source sets. Note that the Android plugin uses its own implementation of
+     * source sets, {@link AndroidSourceSet}.
+     */
+    @Override
+    public NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
+        return sourceSetsContainer;
+    }
+
+    /**
+     * The default configuration, inherited by all build flavors (if any are defined).
+     */
+    public void defaultConfig(Action<ProductFlavor> action) {
+        checkWritability();
+        action.execute(defaultConfig);
+    }
+
+    /**
+     * Configures aapt options.
+     */
+    public void aaptOptions(Action<AaptOptions> action) {
+        checkWritability();
+        action.execute(aaptOptions);
+    }
+
+    /**
+     * Configures dex options.
+     */
+    public void dexOptions(Action<DexOptions> action) {
+        checkWritability();
+        action.execute(dexOptions);
+    }
+
+    /**
+     * Configure lint options.
+     */
+    public void lintOptions(Action<LintOptions> action) {
+        checkWritability();
+        action.execute(lintOptions);
+    }
+
+    /** Configures the test options. */
+    public void testOptions(Action<TestOptions> action) {
+        checkWritability();
+        action.execute(testOptions);
+    }
+
+    /**
+     * Configures compile options.
+     */
+    public void compileOptions(Action<CompileOptions> action) {
+        checkWritability();
+        action.execute(compileOptions);
+    }
+
+    /**
+     * Configures packaging options.
+     */
+    public void packagingOptions(Action<PackagingOptions> action) {
+        checkWritability();
+        action.execute(packagingOptions);
+    }
+
+    /**
+     * Configures preprocessing options.
+     */
+    public void preprocessingOptions(Action<PreprocessingOptions> action) {
+        checkWritability();
+        action.execute(preprocessingOptions);
+    }
+
+    /**
+     * Configures JaCoCo options.
+     */
+    public void jacoco(Action<JacocoExtension> action) {
+        checkWritability();
+        action.execute(jacoco);
+    }
+
+    /**
+     * Configures adb options.
+     */
+    public void adbOptions(Action<AdbOptions> action) {
+        checkWritability();
+        action.execute(adbOptions);
+    }
+
+    /**
+     * Configures APK splits.
+     */
+    public void splits(Action<Splits> action) {
+        checkWritability();
+        action.execute(splits);
+    }
+
+    public void deviceProvider(DeviceProvider deviceProvider) {
+        checkWritability();
+        deviceProviderList.add(deviceProvider);
+    }
+
+    @Override
+    @NonNull
+    public List<DeviceProvider> getDeviceProviders() {
+        return deviceProviderList;
+    }
+
+    public void testServer(TestServer testServer) {
+        checkWritability();
+        testServerList.add(testServer);
+    }
+
+    @Override
+    @NonNull
+    public List<TestServer> getTestServers() {
+        return testServerList;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends CoreProductFlavor> getProductFlavors() {
+        return productFlavors;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends CoreBuildType> getBuildTypes() {
+        return buildTypes;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<? extends com.android.builder.model.SigningConfig> getSigningConfigs() {
+        return signingConfigs;
+    }
+
+    public void defaultPublishConfig(String value) {
+        setDefaultPublishConfig(value);
+    }
+
+    public void publishNonDefault(boolean value) {
+        publishNonDefault = value;
+    }
+
+    /**
+     * Name of the configuration used to build the default artifact of this project.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
+     * Referencing a Library</a>
+     */
+    @Override
+    public String getDefaultPublishConfig() {
+        return defaultPublishConfig;
+    }
+
+    public void setDefaultPublishConfig(String value) {
+        defaultPublishConfig = value;
+    }
+
+    /**
+     * Whether to publish artifacts for all configurations, not just the default one.
+     *
+     * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
+     * Referencing a Library</a>
+     */
+    @Override
+    public boolean getPublishNonDefault() {
+        return publishNonDefault;
+    }
+
+    public void variantFilter(Action<VariantFilter> filter) {
+        setVariantFilter(filter);
+    }
+
+    public void setVariantFilter(Action<VariantFilter> filter) {
+        variantFilter = filter;
+    }
+
+    /**
+     * A variant filter to control which variants are excluded.
+     * <p>The filter is an {@link Action} which is passed a single object of type
+     * {@link com.android.build.gradle.internal.api.VariantFilter}. It should set the
+     * {@link VariantFilter#setIgnore(boolean)} flag to filter out the given variant.
+     */
+    @Override
+    public Action<VariantFilter> getVariantFilter() {
+        return variantFilter;
+    }
+
+    @Override
+    public AdbOptions getAdbOptions() {
+        return adbOptions;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getResourcePrefix() {
+        return resourcePrefix;
+    }
+
+    @Override
+    public List<String> getFlavorDimensionList() {
+        return flavorDimensionList;
+    }
+
+    @Override
+    public boolean getGeneratePureSplits() {
+        return generatePureSplits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Beta
+    public PreprocessingOptions getPreprocessingOptions() {
+        return preprocessingOptions;
+    }
+
+    public void resourcePrefix(String prefix) {
+        resourcePrefix = prefix;
+    }
+
+    public abstract void addVariant(BaseVariant variant);
+
+    public void registerArtifactType(@NonNull String name,
+                                     boolean isTest,
+                                     int artifactType) {
+        extraModelInfo.registerArtifactType(name, isTest, artifactType);
+    }
+
+    public void registerBuildTypeSourceProvider(
+            @NonNull String name,
+            @NonNull BuildType buildType,
+            @NonNull SourceProvider sourceProvider) {
+        extraModelInfo.registerBuildTypeSourceProvider(name, buildType, sourceProvider);
+    }
+
+    public void registerProductFlavorSourceProvider(
+            @NonNull String name,
+            @NonNull CoreProductFlavor productFlavor,
+            @NonNull SourceProvider sourceProvider) {
+        extraModelInfo.registerProductFlavorSourceProvider(name, productFlavor, sourceProvider);
+    }
+
+    public void registerJavaArtifact(
+            @NonNull String name,
+            @NonNull BaseVariant variant,
+            @NonNull String assembleTaskName,
+            @NonNull String javaCompileTaskName,
+            @NonNull Collection<File> generatedSourceFolders,
+            @NonNull Iterable<String> ideSetupTaskNames,
+            @NonNull Configuration configuration,
+            @NonNull File classesFolder,
+            @NonNull File javaResourceFolder,
+            @Nullable SourceProvider sourceProvider) {
+        extraModelInfo.registerJavaArtifact(name, variant, assembleTaskName,
+                javaCompileTaskName, generatedSourceFolders, ideSetupTaskNames,
+                configuration, classesFolder, javaResourceFolder, sourceProvider);
+    }
+
+    public void registerMultiFlavorSourceProvider(
+            @NonNull String name,
+            @NonNull String flavorName,
+            @NonNull SourceProvider sourceProvider) {
+        extraModelInfo.registerMultiFlavorSourceProvider(name, flavorName, sourceProvider);
+    }
+
+    @NonNull
+    public SourceProvider wrapJavaSourceSet(@NonNull SourceSet sourceSet) {
+        return new SourceSetSourceProviderWrapper(sourceSet);
+    }
+
+    /**
+     * <strong>Required.</strong> Compile SDK version.
+     *
+     * <p>Your code will be compiled against the android.jar from this API level. You should
+     * generally use the most up-to-date SDK version here. Use the Lint tool to make sure you don't
+     * use APIs not available in earlier platform version without checking.
+     *
+     * <p>Setter can be called with a string like "android-21" or a number.
+     *
+     * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
+     * back may give a slightly different string.
+     */
+    @Override
+    public String getCompileSdkVersion() {
+        return target;
+    }
+
+    @Override
+    public FullRevision getBuildToolsRevision() {
+        return buildToolsRevision;
+    }
+
+    @Override
+    public Collection<LibraryRequest> getLibraryRequests() {
+        return libraryRequests;
+    }
+
+    public File getSdkDirectory() {
+        return sdkHandler.getSdkFolder();
+    }
+
+    public File getNdkDirectory() {
+        return sdkHandler.getNdkFolder();
+    }
+
+    public List<File> getBootClasspath() {
+        ensureTargetSetup();
+        return androidBuilder.getBootClasspath();
+    }
+
+    public File getAdbExe() {
+        return sdkHandler.getSdkInfo().getAdb();
+    }
+
+    public File getDefaultProguardFile(String name) {
+        File sdkDir = sdkHandler.getAndCheckSdkFolder();
+        return new File(sdkDir,
+                SdkConstants.FD_TOOLS + File.separatorChar
+                        + SdkConstants.FD_PROGUARD + File.separatorChar
+                        + name);
+    }
+
+    // ---------------
+    // TEMP for compatibility
+
+    // by default, we do not generate pure splits
+    boolean generatePureSplits = false;
+
+    public void generatePureSplits(boolean flag) {
+        if (flag) {
+            logger.warn("Pure splits are not supported by PlayStore yet.");
+        }
+        this.generatePureSplits = flag;
+    }
+
+    private boolean enforceUniquePackageName = true;
+
+    public void enforceUniquePackageName(boolean value) {
+        if (!value) {
+            LoggingUtil.displayDeprecationWarning(logger, project, "Support for libraries with same package name is deprecated and will be removed in a future release.");
+        }
+        enforceUniquePackageName = value;
+    }
+
+    public void setEnforceUniquePackageName(boolean value) {
+        enforceUniquePackageName(value);
+    }
+
+    @Override
+    public boolean getEnforceUniquePackageName() {
+        return enforceUniquePackageName;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public CoreProductFlavor getDefaultConfig() {
+        return defaultConfig;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public AaptOptions getAaptOptions() {
+        return aaptOptions;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public CompileOptions getCompileOptions() {
+        return compileOptions;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public DexOptions getDexOptions() {
+        return dexOptions;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public JacocoExtension getJacoco() {
+        return jacoco;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public LintOptions getLintOptions() {
+        return lintOptions;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PackagingOptions getPackagingOptions() {
+        return packagingOptions;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Splits getSplits() {
+        return splits;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public TestOptions getTestOptions() {
+        return testOptions;
+    }
+
+    private void ensureTargetSetup() {
+        // check if the target has been set.
+        TargetInfo targetInfo = androidBuilder.getTargetInfo();
+        if (targetInfo == null) {
+            sdkHandler.initTarget(
+                    getCompileSdkVersion(),
+                    buildToolsRevision,
+                    libraryRequests,
+                    androidBuilder);
+        }
+    }
+
+    // For compatibility with LibraryExtension.
+    @Override
+    public Boolean getPackageBuildConfig() {
+        throw new GradleException("packageBuildConfig is not supported.");
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
deleted file mode 100755
index b2a21b3..0000000
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
+++ /dev/null
@@ -1,461 +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.build.gradle
-import com.android.annotations.Nullable
-import com.android.annotations.VisibleForTesting
-import com.android.build.gradle.internal.BadPluginException
-import com.android.build.gradle.internal.DependencyManager
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LibraryCache
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskContainerAdaptor
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantManager
-import com.android.build.gradle.internal.coverage.JacocoPlugin
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.BuildTypeFactory
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.dsl.SigningConfigFactory
-import com.android.build.gradle.internal.model.ModelBuilder
-import com.android.build.gradle.internal.process.GradleJavaProcessExecutor
-import com.android.build.gradle.internal.process.GradleProcessExecutor
-import com.android.build.gradle.internal.profile.RecordingBuildListener
-import com.android.build.gradle.internal.profile.SpanRecorders
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.build.gradle.tasks.JillTask
-import com.android.build.gradle.tasks.PreDex
-import com.android.builder.Version
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.DefaultBuildType
-import com.android.builder.internal.compiler.JackConversionCache
-import com.android.builder.internal.compiler.PreDexCache
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.ProcessRecorderFactory
-import com.android.builder.profile.ThreadRecorder
-import com.android.builder.sdk.TargetInfo
-import com.android.ide.common.blame.output.BlameAwareLoggedProcessOutputHandler
-import com.android.ide.common.internal.ExecutorSingleton
-import com.android.utils.ILogger
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.tooling.BuildException
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-import java.security.MessageDigest
-import java.util.jar.Manifest
-import java.util.regex.Pattern
-
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static com.google.common.base.Preconditions.checkState
-import static java.io.File.separator
-/**
- * Base class for all Android plugins
- */
-@CompileStatic
-public abstract class BasePlugin {
-
-    private static final String GRADLE_MIN_VERSION = "2.2"
-    public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.[2-9].*")
-    private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
-            "com.android.build.gradle.overrideVersionCheck"
-
-    // default retirement age in days since its inception date for RC or beta versions.
-    private static final int DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE = 40
-
-    protected BaseExtension extension
-
-    protected VariantManager variantManager
-
-    protected TaskManager taskManager
-
-    protected Project project
-
-    protected SdkHandler sdkHandler
-
-    protected AndroidBuilder androidBuilder
-
-    protected Instantiator instantiator
-
-    private ToolingModelBuilderRegistry registry
-
-    private JacocoPlugin jacocoPlugin
-
-    private LoggerWrapper loggerWrapper
-
-    private ExtraModelInfo extraModelInfo
-
-    private String creator
-
-    private boolean hasCreatedTasks = false
-
-    protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
-        this.instantiator = instantiator
-        this.registry = registry
-        creator = "Android Gradle " + Version.ANDROID_GRADLE_PLUGIN_VERSION
-        verifyRetirementAge()
-
-        ModelBuilder.clearCaches();
-    }
-
-    /**
-     * Verify that this plugin execution is within its public time range.
-     */
-    private void verifyRetirementAge() {
-
-        Manifest manifest;
-        URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
-        try {
-            URL url = cl.findResource("META-INF/MANIFEST.MF");
-            manifest = new Manifest(url.openStream());
-        } catch (IOException ignore) {
-            return;
-        }
-
-        String inceptionDateAttr = manifest.mainAttributes.getValue("Inception-Date")
-        // when running in unit tests, etc... the manifest entries are absent.
-        if (inceptionDateAttr == null) {
-            return;
-        }
-        def items = inceptionDateAttr.split(':')
-        GregorianCalendar inceptionDate = new GregorianCalendar(Integer.parseInt(items[0]),
-                Integer.parseInt(items[1]), Integer.parseInt(items[2]));
-
-        int retirementAge = getRetirementAge(manifest.mainAttributes.getValue("Plugin-Version"))
-
-        if (retirementAge == -1) {
-            return;
-        }
-        Calendar now = GregorianCalendar.getInstance()
-        int days = now.minus(inceptionDate)
-        if (days > retirementAge) {
-            // this plugin is too old.
-            String dailyOverride = System.getenv("ANDROID_DAILY_OVERRIDE")
-            MessageDigest cript = MessageDigest.getInstance("SHA-1")
-            cript.reset()
-            // encode the day, not the current time.
-            cript.update(
-                    "${now.get(Calendar.YEAR)}:${now.get(Calendar.MONTH)}:${now.get(Calendar.DATE)}"
-                            .getBytes("utf8"))
-            String overrideValue = new BigInteger(1, cript.digest()).toString(16)
-            if (dailyOverride == null) {
-                String message = """
-                    Plugin is too old, please update to a more recent version,
-                    or set ANDROID_DAILY_OVERRIDE environment variable to
-                    \"${overrideValue}\""""
-                System.err.println(message)
-                throw new RuntimeException(message)
-            } else {
-                if (!dailyOverride.equals(overrideValue)) {
-                    String message = """
-                    Plugin is too old and ANDROID_DAILY_OVERRIDE value is
-                    also outdated, please use new value :
-                    \"${overrideValue}\""""
-                    System.err.println(message)
-                    throw new RuntimeException(message)
-                }
-            }
-        }
-    }
-
-    private static int getRetirementAge(@Nullable String version) {
-        if (version == null || version.contains("rc") || version.contains("beta")
-                || version.contains("alpha")) {
-            return DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE
-        }
-        return -1;
-    }
-
-    protected abstract Class<? extends BaseExtension> getExtensionClass()
-    protected abstract VariantFactory getVariantFactory()
-    protected abstract TaskManager createTaskManager(
-            Project project,
-            AndroidBuilder androidBuilder,
-            BaseExtension extension,
-            SdkHandler sdkHandler,
-            DependencyManager dependencyManager,
-            ToolingModelBuilderRegistry toolingRegistry)
-
-    /**
-     * Return whether this plugin creates Android library.  Should be overridden if true.
-     */
-    protected boolean isLibrary() {
-        return false;
-    }
-
-    @VisibleForTesting
-    VariantManager getVariantManager() {
-        return variantManager
-    }
-
-    protected ILogger getLogger() {
-        if (loggerWrapper == null) {
-            loggerWrapper = new LoggerWrapper(project.logger)
-        }
-
-        return loggerWrapper
-    }
-
-
-    protected void apply(Project project) {
-        this.project = project
-        ProcessRecorderFactory.initialize(logger, project.rootProject.
-                file("profiler" + System.currentTimeMillis() + ".json"))
-        project.gradle.addListener(new RecordingBuildListener(ThreadRecorder.get()));
-
-        SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE) {
-            configureProject()
-        }
-
-        SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSTION_CREATION) {
-            createExtension()
-        }
-
-        SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION) {
-            createTasks()
-        }
-    }
-
-    protected void configureProject() {
-        checkGradleVersion()
-        extraModelInfo = new ExtraModelInfo(project)
-        sdkHandler = new SdkHandler(project, logger)
-        androidBuilder = new AndroidBuilder(
-                project == project.rootProject ? project.name : project.path,
-                creator,
-                new GradleProcessExecutor(project),
-                new GradleJavaProcessExecutor(project),
-                new BlameAwareLoggedProcessOutputHandler(getLogger(),
-                        extraModelInfo.getErrorFormatMode()),
-                logger,
-                verbose)
-
-        project.apply plugin: JavaBasePlugin
-
-        project.apply plugin: JacocoPlugin
-        jacocoPlugin = project.plugins.getPlugin(JacocoPlugin)
-
-        project.tasks.getByName("assemble").description =
-                "Assembles all variants of all applications and secondary packages."
-
-        // call back on execution. This is called after the whole build is done (not
-        // after the current project is done).
-        // This is will be called for each (android) projects though, so this should support
-        // being called 2+ times.
-        project.gradle.buildFinished {
-            ExecutorSingleton.shutdown()
-            sdkHandler.unload()
-            SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_BUILD_FINISHED) {
-                PreDexCache.getCache().clear(
-                        project.rootProject.file(
-                                "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"),
-                        logger)
-                JackConversionCache.getCache().clear(
-                        project.rootProject.file(
-                                "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"),
-                        logger)
-                LibraryCache.getCache().unload()
-            }
-            ProcessRecorderFactory.shutdown();
-        }
-
-        project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
-            for (Task task : taskGraph.allTasks) {
-                if (task instanceof PreDex) {
-                    PreDexCache.getCache().load(
-                            project.rootProject.file(
-                                    "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"))
-                    break;
-                } else if (task instanceof JillTask) {
-                    JackConversionCache.getCache().load(
-                            project.rootProject.file(
-                                    "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"))
-                    break;
-                }
-            }
-        }
-    }
-
-    private void createExtension() {
-        def buildTypeContainer = project.container(BuildType,
-                new BuildTypeFactory(instantiator, project, project.getLogger()))
-        def productFlavorContainer = project.container(GroupableProductFlavor,
-                new GroupableProductFlavorFactory(instantiator, project, project.getLogger()))
-        def signingConfigContainer = project.container(SigningConfig,
-                new SigningConfigFactory(instantiator))
-
-        extension = project.extensions.create('android', getExtensionClass(),
-                (ProjectInternal) project, instantiator, androidBuilder, sdkHandler,
-                buildTypeContainer, productFlavorContainer, signingConfigContainer,
-                extraModelInfo, isLibrary())
-
-        // create the default mapping configuration.
-        project.configurations.create("default-mapping").description = "Configuration for default mapping artifacts."
-        project.configurations.create("default-metadata").description = "Metadata for the produced APKs."
-
-        DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo)
-        taskManager = createTaskManager(
-                project,
-                androidBuilder,
-                extension,
-                sdkHandler,
-                dependencyManager,
-                registry)
-
-        VariantFactory variantFactory = getVariantFactory()
-        variantManager = new VariantManager(
-                project,
-                androidBuilder,
-                extension,
-                variantFactory,
-                taskManager,
-                instantiator)
-
-        // Register a builder for the custom tooling model
-        ModelBuilder modelBuilder = new ModelBuilder(
-                androidBuilder, variantManager, taskManager, extension, extraModelInfo, isLibrary())
-        registry.register(modelBuilder);
-
-        // map the whenObjectAdded callbacks on the containers.
-        signingConfigContainer.whenObjectAdded { SigningConfig signingConfig ->
-            variantManager.addSigningConfig((SigningConfig) signingConfig)
-        }
-
-        buildTypeContainer.whenObjectAdded { DefaultBuildType buildType ->
-            variantManager.addBuildType((BuildType) buildType)
-        }
-
-        productFlavorContainer.whenObjectAdded { GroupableProductFlavor productFlavor ->
-            variantManager.addProductFlavor(productFlavor)
-        }
-
-        // map whenObjectRemoved on the containers to throw an exception.
-        signingConfigContainer.whenObjectRemoved {
-            throw new UnsupportedOperationException("Removing signingConfigs is not supported.")
-        }
-        buildTypeContainer.whenObjectRemoved {
-            throw new UnsupportedOperationException("Removing build types is not supported.")
-        }
-        productFlavorContainer.whenObjectRemoved {
-            throw new UnsupportedOperationException("Removing product flavors is not supported.")
-        }
-
-        // create default Objects, signingConfig first as its used by the BuildTypes.
-        variantFactory.createDefaultComponents(buildTypeContainer, productFlavorContainer, signingConfigContainer)
-    }
-
-    private void createTasks() {
-        SpanRecorders.record(project, ExecutionType.TASK_MANAGER_CREATE_TASKS) {
-            taskManager.createTasksBeforeEvaluate(new TaskContainerAdaptor(project.getTasks()))
-        }
-
-        project.afterEvaluate {
-            ensureTargetSetup()
-            SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS) {
-                createAndroidTasks(false)
-            }
-        }
-    }
-
-    private void checkGradleVersion() {
-        if (!GRADLE_ACCEPTABLE_VERSIONS.matcher(project.getGradle().gradleVersion).matches()) {
-            boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY)
-            File file = new File("gradle" + separator + "wrapper" + separator +
-                    "gradle-wrapper.properties");
-            String errorMessage = 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);
-            if (allowNonMatching) {
-                getLogger().warning(errorMessage)
-                getLogger().warning("As %s is set, continuing anyways.",
-                        GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY)
-            } else {
-                throw new BuildException(errorMessage, null)
-            }
-        }
-    }
-
-    @VisibleForTesting
-    final void createAndroidTasks(boolean force) {
-        // Make sure unit tests set the required fields.
-        checkState(extension.getBuildToolsRevision() != null, "buildToolsVersion is not specified.")
-        checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified.")
-
-        // get current plugins and look for the default Java plugin.
-        if (project.plugins.hasPlugin(JavaPlugin.class)) {
-            throw new BadPluginException(
-                    "The 'java' plugin has been applied, but it is not compatible with the Android plugins.")
-        }
-
-        // don't do anything if the project was not initialized.
-        // Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return.
-        // This is because project don't get evaluated in the unit test setup.
-        // See AppPluginDslTest
-        if (!force
-                && (!project.state.executed || project.state.failure != null)
-                && SdkHandler.sTestSdkFolder == null) {
-            return
-        }
-
-        if (hasCreatedTasks) {
-            return
-        }
-        hasCreatedTasks = true
-
-        extension.disableWrite()
-
-        // setup SDK repositories.
-        for (File file : sdkHandler.sdkLoader.repositories) {
-            project.repositories.maven { MavenArtifactRepository repo ->
-                repo.url = file.toURI()
-            }
-        }
-
-        taskManager.createMockableJarTask()
-        SpanRecorders.record(project, ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS) {
-            variantManager.createAndroidTasks()
-        }
-    }
-
-    private boolean isVerbose() {
-        return project.logger.isEnabled(LogLevel.INFO)
-    }
-
-    private void ensureTargetSetup() {
-        // check if the target has been set.
-        TargetInfo targetInfo = androidBuilder.getTargetInfo()
-        if (targetInfo == null) {
-            sdkHandler.initTarget(
-                    extension.getCompileSdkVersion(),
-                    extension.buildToolsRevision,
-                    androidBuilder)
-        }
-    }
-}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.java
new file mode 100755
index 0000000..85bb8c6
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.java
@@ -0,0 +1,714 @@
+/*
+ * 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;
+
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+import static com.google.common.base.Preconditions.checkState;
+import static java.io.File.separator;
+
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.build.gradle.internal.ApiObjectFactory;
+import com.android.build.gradle.internal.BadPluginException;
+import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.ExecutionConfigurationUtil;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LibraryCache;
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.NativeLibraryFactoryImpl;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskContainerAdaptor;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.coverage.JacocoPlugin;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.BuildTypeFactory;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.ProductFlavorFactory;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.build.gradle.internal.dsl.SigningConfigFactory;
+import com.android.build.gradle.internal.model.ModelBuilder;
+import com.android.build.gradle.internal.process.GradleJavaProcessExecutor;
+import com.android.build.gradle.internal.process.GradleProcessExecutor;
+import com.android.build.gradle.internal.profile.RecordingBuildListener;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.build.gradle.tasks.JillTask;
+import com.android.build.gradle.tasks.PreDex;
+import com.android.builder.Version;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.internal.compiler.JackConversionCache;
+import com.android.builder.internal.compiler.PreDexCache;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.ProcessRecorderFactory;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+import com.android.builder.sdk.TargetInfo;
+import com.android.ide.common.internal.ExecutorSingleton;
+import com.android.utils.ILogger;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.gradle.BuildListener;
+import org.gradle.BuildResult;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.execution.TaskExecutionGraphListener;
+import org.gradle.api.initialization.Settings;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.tasks.StopExecutionException;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.tooling.BuildException;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.Manifest;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for all Android plugins
+ */
+public abstract class BasePlugin {
+
+    private static final String GRADLE_MIN_VERSION = "2.2";
+    public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.[2-9].*");
+    private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
+            "com.android.build.gradle.overrideVersionCheck";
+    private static final String SKIP_PATH_CHECK_PROPERTY =
+            "com.android.build.gradle.overridePathCheck";
+    /** default retirement age in days since its inception date for RC or beta versions. */
+    private static final int DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE_IN_DAYS = 40;
+
+
+    protected BaseExtension extension;
+
+    protected VariantManager variantManager;
+
+    protected TaskManager taskManager;
+
+    protected Project project;
+
+    protected SdkHandler sdkHandler;
+
+    private NdkHandler ndkHandler;
+
+    protected AndroidBuilder androidBuilder;
+
+    protected Instantiator instantiator;
+
+    protected VariantFactory variantFactory;
+
+    private ToolingModelBuilderRegistry registry;
+
+    private JacocoPlugin jacocoPlugin;
+
+    private LoggerWrapper loggerWrapper;
+
+    private ExtraModelInfo extraModelInfo;
+
+    private String creator;
+
+    private boolean hasCreatedTasks = false;
+
+    protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
+        this.instantiator = instantiator;
+        this.registry = registry;
+        creator = "Android Gradle " + Version.ANDROID_GRADLE_PLUGIN_VERSION;
+        verifyRetirementAge();
+
+        ModelBuilder.clearCaches();
+    }
+
+    /**
+     * Verify that this plugin execution is within its public time range.
+     */
+    private void verifyRetirementAge() {
+
+        Manifest manifest;
+        URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
+        try {
+            URL url = cl.findResource("META-INF/MANIFEST.MF");
+            manifest = new Manifest(url.openStream());
+        } catch (IOException ignore) {
+            return;
+        }
+
+        String inceptionDateAttr = manifest.getMainAttributes().getValue("Inception-Date");
+        // when running in unit tests, etc... the manifest entries are absent.
+        if (inceptionDateAttr == null) {
+            return;
+        }
+        List<String> items = ImmutableList.copyOf(Splitter.on(':').split(inceptionDateAttr));
+        GregorianCalendar inceptionDate = new GregorianCalendar(Integer.parseInt(items.get(0)),
+                Integer.parseInt(items.get(1)), Integer.parseInt(items.get(2)));
+
+        int retirementAgeInDays =
+                getRetirementAgeInDays(manifest.getMainAttributes().getValue("Plugin-Version"));
+
+        if (retirementAgeInDays == -1) {
+            return;
+        }
+        Calendar now = GregorianCalendar.getInstance();
+        long nowTimestamp = now.getTimeInMillis();
+        long inceptionTimestamp = inceptionDate.getTimeInMillis();
+        long days = TimeUnit.DAYS.convert(nowTimestamp - inceptionTimestamp, TimeUnit.MILLISECONDS);
+        if (days > retirementAgeInDays) {
+            // this plugin is too old.
+            String dailyOverride = System.getenv("ANDROID_DAILY_OVERRIDE");
+            final MessageDigest crypt;
+            try {
+                crypt = MessageDigest.getInstance("SHA-1");
+            } catch (NoSuchAlgorithmException e) {
+                return;
+            }
+            crypt.reset();
+            // encode the day, not the current time.
+            try {
+                crypt.update(String.format("%1$s:%2$s:%3$s",
+                        now.get(Calendar.YEAR),
+                        now.get(Calendar.MONTH),
+                        now.get(Calendar.DATE)).getBytes("utf8"));
+            } catch (UnsupportedEncodingException e) {
+                return;
+            }
+            String overrideValue = new BigInteger(1, crypt.digest()).toString(16);
+            if (dailyOverride == null) {
+                String message = "Plugin is too old, please update to a more recent version, or " +
+                    "set ANDROID_DAILY_OVERRIDE environment variable to \"" + overrideValue + '"';
+                System.err.println(message);
+                throw new RuntimeException(message);
+            } else {
+                if (!dailyOverride.equals(overrideValue)) {
+                    String message = "Plugin is too old and ANDROID_DAILY_OVERRIDE value is " +
+                    "also outdated, please use new value :\"" + overrideValue + '"';
+                    System.err.println(message);
+                    throw new RuntimeException(message);
+                }
+            }
+        }
+    }
+
+    private static int getRetirementAgeInDays(@Nullable String version) {
+        if (version == null || version.contains("rc") || version.contains("beta")
+                || version.contains("alpha")) {
+            return DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE_IN_DAYS;
+        }
+        return -1;
+    }
+
+    protected abstract Class<? extends BaseExtension> getExtensionClass();
+    protected abstract VariantFactory createVariantFactory();
+    protected abstract TaskManager createTaskManager(
+            Project project,
+            AndroidBuilder androidBuilder,
+            AndroidConfig extension,
+            SdkHandler sdkHandler,
+            DependencyManager dependencyManager,
+            ToolingModelBuilderRegistry toolingRegistry);
+
+    /**
+     * Return whether this plugin creates Android library.  Should be overridden if true.
+     */
+    protected boolean isLibrary() {
+        return false;
+    }
+
+    @VisibleForTesting
+    VariantManager getVariantManager() {
+        return variantManager;
+    }
+
+    protected ILogger getLogger() {
+        if (loggerWrapper == null) {
+            loggerWrapper = new LoggerWrapper(project.getLogger());
+        }
+
+        return loggerWrapper;
+    }
+
+
+    protected void apply(Project project) throws IOException {
+        this.project = project;
+
+        ExecutionConfigurationUtil.setThreadPoolSize(project);
+        checkPathForErrors();
+        checkModulesForErrors();
+
+        List<Recorder.Property> propertyList = Lists.newArrayList(
+                new Recorder.Property("plugin_version", Version.ANDROID_GRADLE_PLUGIN_VERSION),
+                new Recorder.Property("next_gen_plugin", "false"),
+                new Recorder.Property("gradle_version", project.getGradle().getGradleVersion())
+        );
+        String benchmarkName = AndroidGradleOptions.getBenchmarkName(project);
+        if (benchmarkName != null) {
+            propertyList.add(new Recorder.Property("benchmark_name", benchmarkName));
+        }
+        String benchmarkMode = AndroidGradleOptions.getBenchmarkMode(project);
+        if (benchmarkMode != null) {
+            propertyList.add(new Recorder.Property("benchmark_mode", benchmarkMode));
+        }
+
+        ProcessRecorderFactory.initialize(
+                getLogger(),
+                project.getRootProject().file("profiler" + System.currentTimeMillis() + ".json"),
+                propertyList);
+        project.getGradle().addListener(new RecordingBuildListener(ThreadRecorder.get()));
+
+
+        ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        configureProject();
+                        return null;
+                    }
+                }, new Recorder.Property("project", project.getName()));
+
+        ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSTION_CREATION,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createExtension();
+                        return null;
+                    }
+                }, new Recorder.Property("project", project.getName()));
+
+        ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        createTasks();
+                        return null;
+                    }
+                }, new Recorder.Property("project", project.getName()));
+    }
+
+    protected void configureProject() {
+        checkGradleVersion();
+        extraModelInfo = new ExtraModelInfo(project, isLibrary());
+        sdkHandler = new SdkHandler(project, getLogger());
+        androidBuilder = new AndroidBuilder(
+                project == project.getRootProject() ? project.getName() : project.getPath(),
+                creator,
+                new GradleProcessExecutor(project),
+                new GradleJavaProcessExecutor(project),
+                extraModelInfo,
+                getLogger(),
+                isVerbose());
+
+        project.getPlugins().apply(JavaBasePlugin.class);
+
+        jacocoPlugin = project.getPlugins().apply(JacocoPlugin.class);
+
+        project.getTasks().getByName("assemble").setDescription(
+                "Assembles all variants of all applications and secondary packages.");
+
+        // call back on execution. This is called after the whole build is done (not
+        // after the current project is done).
+        // This is will be called for each (android) projects though, so this should support
+        // being called 2+ times.
+        project.getGradle().addBuildListener(new BuildListener() {
+            @Override
+            public void buildStarted(Gradle gradle) { }
+
+            @Override
+            public void settingsEvaluated(Settings settings) { }
+
+            @Override
+            public void projectsLoaded(Gradle gradle) { }
+
+            @Override
+            public void projectsEvaluated(Gradle gradle) { }
+
+            @Override
+            public void buildFinished(BuildResult buildResult) {
+                ExecutorSingleton.shutdown();
+                sdkHandler.unload();
+                ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
+                        new Recorder.Block() {
+                            @Override
+                            public Void call() throws Exception {
+                                PreDexCache.getCache().clear(
+                                        new File(project.getRootProject().getBuildDir(),
+                                                FD_INTERMEDIATES + "/dex-cache/cache.xml"),
+                                        getLogger());
+                                JackConversionCache.getCache().clear(
+                                        new File(project.getRootProject().getBuildDir(),
+                                                FD_INTERMEDIATES + "/jack-cache/cache.xml"),
+                                        getLogger());
+                                LibraryCache.getCache().unload();
+                                return null;
+                            }
+                        }, new Recorder.Property("project", project.getName()));
+
+                try {
+                    ProcessRecorderFactory.shutdown();
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+        project.getGradle().getTaskGraph().addTaskExecutionGraphListener(
+                new TaskExecutionGraphListener() {
+                    @Override
+                    public void graphPopulated(TaskExecutionGraph taskGraph) {
+                        for (Task task : taskGraph.getAllTasks()) {
+                            if (task instanceof PreDex) {
+                                PreDexCache.getCache().load(
+                                        new File(project.getRootProject().getBuildDir(),
+                                                FD_INTERMEDIATES + "/dex-cache/cache.xml"));
+                                break;
+                            } else if (task instanceof JillTask) {
+                                JackConversionCache.getCache().load(
+                                        new File(project.getRootProject().getBuildDir(),
+                                                FD_INTERMEDIATES + "/jack-cache/cache.xml"));
+                                break;
+                            }
+                        }
+                    }
+                });
+    }
+
+
+    private void createExtension() {
+        final NamedDomainObjectContainer<BuildType> buildTypeContainer = project.container(
+                BuildType.class,
+                new BuildTypeFactory(instantiator, project, project.getLogger()));
+        final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer = project.container(
+                ProductFlavor.class,
+                new ProductFlavorFactory(instantiator, project, project.getLogger()));
+        final NamedDomainObjectContainer<SigningConfig>  signingConfigContainer = project.container(
+                SigningConfig.class,
+                new SigningConfigFactory(instantiator));
+
+        extension = project.getExtensions().create("android", getExtensionClass(),
+                project, instantiator, androidBuilder, sdkHandler,
+                buildTypeContainer, productFlavorContainer, signingConfigContainer,
+                extraModelInfo, isLibrary());
+
+        // create the default mapping configuration.
+        project.getConfigurations().create("default-mapping")
+                .setDescription("Configuration for default mapping artifacts.");
+        project.getConfigurations().create("default-metadata")
+                .setDescription("Metadata for the produced APKs.");
+
+        DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo);
+        taskManager = createTaskManager(
+                project,
+                androidBuilder,
+                extension,
+                sdkHandler,
+                dependencyManager,
+                registry);
+
+        variantFactory = createVariantFactory();
+        variantManager = new VariantManager(
+                project,
+                androidBuilder,
+                extension,
+                variantFactory,
+                taskManager,
+                instantiator);
+
+        ndkHandler = new NdkHandler(
+                project.getRootDir(),
+                null, /* compileSkdVersion, this will be set in afterEvaluate */
+                "gcc",
+                "" /*toolchainVersion*/);
+
+        // Register a builder for the custom tooling model
+        ModelBuilder modelBuilder = new ModelBuilder(
+                androidBuilder,
+                variantManager,
+                taskManager,
+                extension,
+                extraModelInfo,
+                ndkHandler,
+                new NativeLibraryFactoryImpl(ndkHandler),
+                isLibrary());
+        registry.register(modelBuilder);
+
+        // map the whenObjectAdded callbacks on the containers.
+        signingConfigContainer.whenObjectAdded(new Action<SigningConfig>() {
+            @Override
+            public void execute(SigningConfig signingConfig) {
+                variantManager.addSigningConfig(signingConfig);
+            }
+        });
+
+
+        buildTypeContainer.whenObjectAdded(new Action<BuildType>() {
+            @Override
+            public void execute(BuildType buildType) {
+            SigningConfig signingConfig = signingConfigContainer.findByName(BuilderConstants.DEBUG);
+            buildType.init(signingConfig);
+            variantManager.addBuildType(buildType);
+            }
+        });
+
+        productFlavorContainer.whenObjectAdded(new Action<ProductFlavor>() {
+            @Override
+            public void execute(ProductFlavor productFlavor) {
+                variantManager.addProductFlavor(productFlavor);
+            }
+        });
+
+        // map whenObjectRemoved on the containers to throw an exception.
+        signingConfigContainer.whenObjectRemoved(
+                new UnsupportedAction("Removing signingConfigs is not supported."));
+        buildTypeContainer.whenObjectRemoved(
+                new UnsupportedAction("Removing build types is not supported."));
+        productFlavorContainer.whenObjectRemoved(
+                new UnsupportedAction("Removing product flavors is not supported."));
+
+        // create default Objects, signingConfig first as its used by the BuildTypes.
+        variantFactory.createDefaultComponents(
+                buildTypeContainer, productFlavorContainer, signingConfigContainer);
+    }
+
+    private static class UnsupportedAction implements Action<Object> {
+
+        private final String message;
+
+        UnsupportedAction(String message) {
+            this.message = message;
+        }
+
+        @Override
+        public void execute(Object o) {
+            throw new UnsupportedOperationException(message);
+        }
+    }
+
+    private void createTasks() {
+        ThreadRecorder.get().record(ExecutionType.TASK_MANAGER_CREATE_TASKS,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        taskManager.createTasksBeforeEvaluate(
+                                new TaskContainerAdaptor(project.getTasks()));
+                        return null;
+                    }
+                },
+                new Recorder.Property("project", project.getName()));
+
+        project.afterEvaluate(new Action<Project>() {
+            @Override
+            public void execute(Project project) {
+                ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
+                        new Recorder.Block<Void>() {
+                            @Override
+                            public Void call() throws Exception {
+                                createAndroidTasks(false);
+                                return null;
+                            }
+                        },
+                        new Recorder.Property("project", project.getName()));
+            }
+        });
+    }
+
+    private void checkGradleVersion() {
+        if (!GRADLE_ACCEPTABLE_VERSIONS.matcher(project.getGradle().getGradleVersion()).matches()) {
+            boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+            File file = new File("gradle" + separator + "wrapper" + separator +
+                    "gradle-wrapper.properties");
+            String errorMessage = 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().getGradleVersion(), file.getAbsolutePath(),
+                GRADLE_MIN_VERSION);
+            if (allowNonMatching) {
+                getLogger().warning(errorMessage);
+                getLogger().warning("As %s is set, continuing anyways.",
+                        GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+            } else {
+                throw new BuildException(errorMessage, null);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    final void createAndroidTasks(boolean force) {
+        // Make sure unit tests set the required fields.
+        checkState(extension.getBuildToolsRevision() != null, "buildToolsVersion is not specified.");
+        checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified.");
+
+        ndkHandler.setCompileSdkVersion(extension.getCompileSdkVersion());
+
+        // get current plugins and look for the default Java plugin.
+        if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
+            throw new BadPluginException(
+                    "The 'java' plugin has been applied, but it is not compatible with the Android plugins.");
+        }
+
+        ensureTargetSetup();
+
+        // don't do anything if the project was not initialized.
+        // Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return.
+        // This is because project don't get evaluated in the unit test setup.
+        // See AppPluginDslTest
+        if (!force
+                && (!project.getState().getExecuted() ||  project.getState().getFailure()!= null)
+                && SdkHandler.sTestSdkFolder == null) {
+            return;
+        }
+
+        if (hasCreatedTasks) {
+            return;
+        }
+        hasCreatedTasks = true;
+
+        extension.disableWrite();
+
+        ThreadRecorder.get().record(
+                ExecutionType.GENERAL_CONFIG,
+                Recorder.EmptyBlock,
+                new Recorder.Property("build_tools_version",
+                        extension.getBuildToolsRevision().toString()));
+
+        // setup SDK repositories.
+        for (final File file : sdkHandler.getSdkLoader().getRepositories()) {
+            project.getRepositories().maven(new Action<MavenArtifactRepository>() {
+                @Override
+                public void execute(MavenArtifactRepository mavenArtifactRepository) {
+                    mavenArtifactRepository.setUrl(file.toURI());
+                }
+            });
+        }
+
+        taskManager.createMockableJarTask();
+        ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS,
+                new Recorder.Block<Void>() {
+                    @Override
+                    public Void call() throws Exception {
+                        variantManager.createAndroidTasks();
+                        ApiObjectFactory apiObjectFactory = new ApiObjectFactory(
+                                androidBuilder, extension, variantFactory, instantiator);
+                        for (BaseVariantData variantData : variantManager.getVariantDataList())  {
+                            apiObjectFactory.create(variantData);
+                        }
+                        return null;
+                    }
+                }, new Recorder.Property("project", project.getName()));
+    }
+
+    private boolean isVerbose() {
+        return project.getLogger().isEnabled(LogLevel.INFO);
+    }
+
+    private void ensureTargetSetup() {
+        // check if the target has been set.
+        TargetInfo targetInfo = androidBuilder.getTargetInfo();
+        if (targetInfo == null) {
+            if (extension.getCompileOptions() == null) {
+                throw new GradleException("Calling getBootClasspath before compileSdkVersion");
+            }
+
+            sdkHandler.initTarget(
+                    extension.getCompileSdkVersion(),
+                    extension.getBuildToolsRevision(),
+                    extension.getLibraryRequests(),
+                    androidBuilder);
+        }
+    }
+
+    /**
+     * Check the sub-projects structure :
+     * So far, checks that 2 modules do not have the same identification (group+name).
+     */
+    private void checkModulesForErrors() {
+        Project rootProject = project.getRootProject();
+        Map<String, Project> subProjectsById = new HashMap<String, Project>();
+        for (Project subProject : rootProject.getAllprojects()) {
+            String id = subProject.getGroup().toString() + ":" + subProject.getName();
+            if (subProjectsById.containsKey(id)) {
+                String message = String.format(
+                        "Your project contains 2 or more modules with the same " +
+                        "identification %1$s\n" +
+                        "at \"%2$s\" and \"%3$s\".\n" +
+                        "You must use different identification (either name or group) for " +
+                        "each modules.",
+                        id,
+                        subProjectsById.get(id).getPath(),
+                        subProject.getPath() );
+                throw new StopExecutionException(message);
+            } else {
+                subProjectsById.put(id, subProject);
+            }
+        }
+    }
+
+    private void checkPathForErrors() {
+        // See if the user disabled the check:
+        if (Boolean.getBoolean(SKIP_PATH_CHECK_PROPERTY)) {
+            return;
+        }
+
+        if (project.hasProperty(SKIP_PATH_CHECK_PROPERTY)
+                && project.property(SKIP_PATH_CHECK_PROPERTY) instanceof String
+                && Boolean.valueOf((String) project.property(SKIP_PATH_CHECK_PROPERTY))) {
+            return;
+        }
+
+        // See if we're on Windows:
+        if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
+            return;
+        }
+
+        // See if the path contains non-ASCII characters.
+        if (CharMatcher.ASCII.matchesAllOf(project.getRootDir().getAbsolutePath())) {
+            return;
+        }
+
+        String message = "Your project path contains non-ASCII characters. This will most likely " +
+                "cause the build to fail on Windows. Please move your project to a different " +
+                "directory. See http://b.android.com/95744 for details. " +
+                "This warning can be disabled by using the command line flag -D" +
+                SKIP_PATH_CHECK_PROPERTY + "=true, or adding the line " +
+                SKIP_PATH_CHECK_PROPERTY + "=true' to gradle.properties file " +
+                "in the project directory.";
+
+        throw new StopExecutionException(message);
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.java
new file mode 100644
index 0000000..bb48a2d
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.java
@@ -0,0 +1,73 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.api.LibraryVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * 'android' extension for 'com.android.library' project.
+ */
+public class LibraryExtension extends TestedExtension {
+
+    private final DefaultDomainObjectSet<LibraryVariant> libraryVariantList
+            = new DefaultDomainObjectSet<LibraryVariant>(LibraryVariant.class);
+
+    private boolean packageBuildConfig = true;
+
+    public LibraryExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+            @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+                signingConfigs, extraModelInfo, isLibrary);
+    }
+
+    /**
+     * Returns the list of library variants. Since the collections is built after evaluation, it
+     * should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    public DefaultDomainObjectSet<LibraryVariant> getLibraryVariants() {
+        return libraryVariantList;
+    }
+
+    @Override
+    public void addVariant(BaseVariant variant) {
+        libraryVariantList.add((LibraryVariant) variant);
+    }
+
+    // ---------------
+    // TEMP for compatibility
+    // STOPSHIP Remove in 1.0
+
+    public void packageBuildConfig(boolean value) {
+        if (!value) {
+            LoggingUtil.displayDeprecationWarning(logger, project,
+                    "Support for not packaging BuildConfig is deprecated and will be removed in 1.0");
+        }
+
+        packageBuildConfig = value;
+    }
+
+    public void setPackageBuildConfig(boolean value) {
+        packageBuildConfig(value);
+    }
+
+    @Override
+    public Boolean getPackageBuildConfig() {
+        return packageBuildConfig;
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
index c01ebc9..df69746 100644
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
@@ -15,7 +15,6 @@
  */
 package com.android.build.gradle
 
-import com.android.build.gradle.internal.ApplicationTaskManager
 import com.android.build.gradle.internal.DependencyManager
 import com.android.build.gradle.internal.LibraryTaskManager
 import com.android.build.gradle.internal.SdkHandler
@@ -26,7 +25,6 @@
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.Task
-import org.gradle.api.tasks.TaskContainer
 import org.gradle.internal.reflect.Instantiator
 import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
 
@@ -54,7 +52,7 @@
     }
 
     @Override
-    protected VariantFactory getVariantFactory() {
+    protected VariantFactory createVariantFactory() {
         return new LibraryVariantFactory(
                 instantiator,
                 androidBuilder,
@@ -70,7 +68,7 @@
     protected TaskManager createTaskManager(
             Project project,
             AndroidBuilder androidBuilder,
-            BaseExtension extension,
+            AndroidConfig extension,
             SdkHandler sdkHandler,
             DependencyManager dependencyManager,
             ToolingModelBuilderRegistry toolingRegistry) {
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestExtension.java
new file mode 100644
index 0000000..d17a3ca
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestExtension.java
@@ -0,0 +1,88 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.ApplicationVariant;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * 'android' extension for 'com.android.test' project.
+ */
+public class TestExtension extends BaseExtension implements TestAndroidConfig {
+
+    private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList
+            = new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class);
+
+    private String targetProjectPath = null;
+
+    private String targetVariant = "debug";
+
+    public TestExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+            @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+                signingConfigs, extraModelInfo, isLibrary);
+    }
+
+    /**
+     * Returns the list of Application variants. Since the collections is built after evaluation, it
+     * should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
+        return applicationVariantList;
+    }
+
+    @Override
+    public void addVariant(BaseVariant variant) {
+        applicationVariantList.add((ApplicationVariant) variant);
+    }
+
+    /**
+     * Returns the Gradle path of the project that this test project tests.
+     */
+    @Override
+    public String getTargetProjectPath() {
+        return targetProjectPath;
+    }
+
+    public void setTargetProjectPath(String targetProjectPath) {
+        checkWritability();
+        this.targetProjectPath = targetProjectPath;
+    }
+
+    public void targetProjectPath(String targetProjectPath) {
+        setTargetProjectPath(targetProjectPath);
+    }
+
+    /**
+     * Returns the variant of the tested project.
+     *
+     * Default is 'debug'
+     */
+    @Override
+    public String getTargetVariant() {
+        return targetVariant;
+    }
+
+    public void setTargetVariant(String targetVariant) {
+        checkWritability();
+        this.targetVariant = targetVariant;
+    }
+
+    public void targetVariant(String targetVariant) {
+        setTargetVariant(targetVariant);
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy
index 35cc6e6..01bbd24 100644
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy
@@ -48,7 +48,7 @@
     protected TaskManager createTaskManager(
             Project project,
             AndroidBuilder androidBuilder,
-            BaseExtension extension,
+            AndroidConfig extension,
             SdkHandler sdkHandler,
             DependencyManager dependencyManager,
             ToolingModelBuilderRegistry toolingRegistry) {
@@ -67,7 +67,7 @@
     }
 
     @Override
-    protected VariantFactory getVariantFactory() {
+    protected VariantFactory createVariantFactory() {
         return new TestVariantFactory(instantiator, androidBuilder, extension)
     }
 }
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestedExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestedExtension.java
new file mode 100644
index 0000000..7cdb20b
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestedExtension.java
@@ -0,0 +1,90 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.TestVariant;
+import com.android.build.gradle.api.UnitTestVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+
+/**
+ * base 'android' extension for plugins that have a test component.
+ */
+public abstract class TestedExtension extends BaseExtension implements TestedAndroidConfig {
+
+    private final DomainObjectSet<TestVariant> testVariantList =
+            new DefaultDomainObjectSet<TestVariant>(TestVariant.class);
+
+    private final DomainObjectSet<UnitTestVariant> unitTestVariantList =
+            new DefaultDomainObjectSet<UnitTestVariant>(UnitTestVariant.class);
+
+    private String testBuildType = "debug";
+
+    public TestedExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+            @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+            @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+            @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+            @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+            @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+        super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+                signingConfigs, extraModelInfo, isLibrary);
+
+        getSourceSets().create(ANDROID_TEST.getPrefix());
+        getSourceSets().create(UNIT_TEST.getPrefix());
+    }
+
+    /**
+     * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+     * it should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    @Override
+    @NonNull
+    public DomainObjectSet<TestVariant> getTestVariants() {
+        return testVariantList;
+    }
+
+    public void addTestVariant(TestVariant testVariant) {
+        testVariantList.add(testVariant);
+    }
+
+    /**
+     * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+     * it should be used with Gradle's <code>all</code> iterator to process future items.
+     */
+    @Override
+    @NonNull
+    public DomainObjectSet<UnitTestVariant> getUnitTestVariants() {
+        return unitTestVariantList;
+    }
+
+    public void addUnitTestVariant(UnitTestVariant testVariant) {
+        unitTestVariantList.add(testVariant);
+    }
+
+    /**
+     * Name of the build type that will be used when running Android (on-device) tests.
+     *
+     * <p>Defaults to "debug".
+     */
+    @Override
+    @NonNull
+    public String getTestBuildType() {
+        return testBuildType;
+    }
+
+    public void setTestBuildType(String testBuildType) {
+        this.testBuildType = testBuildType;
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/ApiObjectFactory.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/ApiObjectFactory.java
new file mode 100644
index 0000000..53e383c
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/ApiObjectFactory.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 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.build.gradle.BaseExtension;
+import com.android.build.gradle.TestedAndroidConfig;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
+import com.android.build.gradle.internal.api.TestVariantImpl;
+import com.android.build.gradle.internal.api.TestedVariant;
+import com.android.build.gradle.internal.api.UnitTestVariantImpl;
+import com.android.build.gradle.internal.variant.ApplicationVariantFactory;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.build.gradle.internal.variant.TestedVariantData;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+
+/**
+ * Factory to create ApiObject from VariantData.
+ */
+public class ApiObjectFactory {
+    @NonNull
+    private final AndroidBuilder androidBuilder;
+    @NonNull
+    private final BaseExtension extension;
+    @NonNull
+    private final VariantFactory variantFactory;
+    @NonNull
+    private final Instantiator instantiator;
+    @NonNull
+    private final ReadOnlyObjectProvider readOnlyObjectProvider = new ReadOnlyObjectProvider();
+
+    public ApiObjectFactory(
+            @NonNull AndroidBuilder androidBuilder,
+            @NonNull BaseExtension extension,
+            @NonNull VariantFactory variantFactory,
+            @NonNull Instantiator instantiator) {
+        this.androidBuilder = androidBuilder;
+        this.extension = extension;
+        this.variantFactory = variantFactory;
+        this.instantiator = instantiator;
+    }
+
+    public void create(BaseVariantData<?> variantData) {
+        if (variantData.getType().isForTesting()) {
+            // Testing variants are handled together with their "owners".
+            return;
+        }
+
+        BaseVariant variantApi =
+                variantFactory.createVariantApi(variantData, readOnlyObjectProvider);
+
+        if (variantFactory.hasTestScope()) {
+            TestVariantData androidTestVariantData =
+                    ((TestedVariantData) variantData).getTestVariantData(ANDROID_TEST);
+
+            if (androidTestVariantData != null) {
+                TestVariantImpl androidTestVariant = instantiator.newInstance(
+                        TestVariantImpl.class,
+                        androidTestVariantData,
+                        variantApi,
+                        androidBuilder,
+                        readOnlyObjectProvider);
+
+                // add the test output.
+                ApplicationVariantFactory.createApkOutputApiObjects(
+                        instantiator,
+                        androidTestVariantData,
+                        androidTestVariant);
+
+                ((TestedAndroidConfig) extension).getTestVariants().add(androidTestVariant);
+                ((TestedVariant) variantApi).setTestVariant(androidTestVariant);
+            }
+
+            TestVariantData unitTestVariantData =
+                    ((TestedVariantData) variantData).getTestVariantData(UNIT_TEST);
+            if (unitTestVariantData != null) {
+                UnitTestVariantImpl unitTestVariant = instantiator.newInstance(
+                        UnitTestVariantImpl.class,
+                        unitTestVariantData,
+                        variantApi,
+                        androidBuilder,
+                        readOnlyObjectProvider);
+
+                ((TestedAndroidConfig) extension).getUnitTestVariants().add(unitTestVariant);
+                ((TestedVariant) variantApi).setUnitTestVariant(unitTestVariant);
+            }
+        }
+
+        // Only add the variant API object to the domain object set once it's been fully
+        // initialized.
+        extension.addVariant(variantApi);
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/NativeLibraryFactoryImpl.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/NativeLibraryFactoryImpl.java
new file mode 100644
index 0000000..ccbaeba
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/NativeLibraryFactoryImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 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.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.model.NativeLibraryFactory;
+import com.android.build.gradle.internal.model.NativeLibraryImpl;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.tasks.NdkCompile;
+import com.android.builder.model.NativeLibrary;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of NativeLibraryFactory for gradle plugin.
+ */
+public class NativeLibraryFactoryImpl implements NativeLibraryFactory {
+
+    @NonNull
+    final NdkHandler ndkHandler;
+
+    public NativeLibraryFactoryImpl(
+            @NonNull NdkHandler ndkHandler) {
+        this.ndkHandler = ndkHandler;
+    }
+
+    @NonNull
+    @Override
+    public Optional<NativeLibrary> create(
+            @NonNull VariantScope scope,
+            @NonNull String toolchainName, @NonNull Abi abi) {
+        BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+        if (!scope.getGlobalScope().getProject().hasProperty(NdkCompile.USE_DEPRECATED_NDK)) {
+            return Optional.absent();
+        }
+
+        CoreNdkOptions ndkConfig = variantData.getVariantConfiguration().getNdkConfig();
+
+        String sysrootFlag = "--sysroot=" + ndkHandler.getSysroot(abi);
+        List<String> cFlags = ndkConfig.getcFlags() == null
+                ? ImmutableList.of(sysrootFlag)
+                : ImmutableList.of(sysrootFlag, ndkConfig.getcFlags());
+
+        // The DSL currently do not support all options available in the model such as the
+        // include dirs and the defines.  Therefore, just pass an empty collection for now.
+        return Optional.<NativeLibrary>of(new NativeLibraryImpl(
+                ndkConfig.getModuleName(),
+                toolchainName,
+                abi.getName(),
+                Collections.<File>emptyList(),  /*cIncludeDirs*/
+                Collections.<File>emptyList(),  /*cppIncludeDirs*/
+                Collections.<File>emptyList(),  /*cSystemIncludeDirs*/
+                ndkHandler.getStlIncludes(ndkConfig.getStl(), abi),
+                Collections.<String>emptyList(),  /*cDefines*/
+                Collections.<String>emptyList(),  /*cppDefines*/
+                cFlags,
+                cFlags,  // TODO: NdkConfig should allow cppFlags to be set separately.
+                ImmutableList.of(scope.getNdkDebuggableLibraryFolders(abi))));
+    }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorFactory.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorFactory.java
new file mode 100644
index 0000000..bb5ed02
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorFactory.java
@@ -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.
+ */
+
+package com.android.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.Project;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * Factory to create ProductFlavor object using an {@link Instantiator} to add
+ * the DSL methods.
+ */
+public class ProductFlavorFactory implements NamedDomainObjectFactory<ProductFlavor> {
+
+    @NonNull
+    private final Instantiator instantiator;
+    @NonNull
+    private final Project project;
+    @NonNull
+    private final Logger logger;
+
+    public ProductFlavorFactory(@NonNull Instantiator instantiator,
+            @NonNull Project project,
+            @NonNull Logger logger) {
+        this.instantiator = instantiator;
+        this.project = project;
+        this.logger = logger;
+    }
+
+    @Override
+    public ProductFlavor create(String name) {
+        return instantiator.newInstance(ProductFlavor.class,
+                name, project, instantiator, logger);
+    }
+}
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
index 524cdee..66a133c 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
@@ -37,7 +37,7 @@
 
     @Override
     protected void setUp() throws Exception {
-        SdkHandler.testSdkFolder = new File("foo")
+        SdkHandler.testSdkFolder = new File(System.getenv("ANDROID_HOME"))
     }
 
     public void testBasic() {
@@ -47,7 +47,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
         }
 
@@ -77,7 +77,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion = 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
         }
 
@@ -104,7 +104,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion "android-15"
+            compileSdkVersion "android-" + COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
         }
 
@@ -131,7 +131,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             sourceSets {
@@ -153,7 +153,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
             testBuildType "staging"
 
@@ -193,7 +193,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             productFlavors {
@@ -236,7 +236,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             flavorDimensions   "dimension1", "dimension2"
@@ -299,7 +299,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
         }
 
@@ -318,7 +318,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             buildTypes {
@@ -356,7 +356,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             buildTypes {
@@ -419,7 +419,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             buildTypes {
@@ -472,11 +472,11 @@
             assertEquals(
                     "target compatibility for ${version}",
                     expectedLanguageLevel.toString(),
-                    project.compileReleaseJava.targetCompatibility)
+                    project.compileReleaseJavaWithJavac.targetCompatibility)
             assertEquals(
                     "source compatibility for ${version}",
                     expectedLanguageLevel.toString(),
-                    project.compileReleaseJava.sourceCompatibility)
+                    project.compileReleaseJavaWithJavac.sourceCompatibility)
         }
 
         for (useJack in [true, false]) {
@@ -484,16 +484,16 @@
             String originalVersion = System.getProperty(propName)
             try{
                 System.setProperty(propName, '1.7')
-                testLanguageLevel(15, JavaVersion.VERSION_1_6, useJack)
-                testLanguageLevel(21, JavaVersion.VERSION_1_7, useJack)
+                testLanguageLevel('android-15', JavaVersion.VERSION_1_6, useJack)
                 testLanguageLevel('android-21', JavaVersion.VERSION_1_7, useJack)
-                testLanguageLevel('Google:GoogleInc:22', JavaVersion.VERSION_1_7, useJack)
+                testLanguageLevel('android-21', JavaVersion.VERSION_1_7, useJack)
+                testLanguageLevel('Google Inc.:Google APIs:22', JavaVersion.VERSION_1_7, useJack)
 
                 System.setProperty(propName, '1.6')
                 testLanguageLevel(15, JavaVersion.VERSION_1_6, useJack)
                 testLanguageLevel(21, JavaVersion.VERSION_1_6, useJack)
                 testLanguageLevel('android-21', JavaVersion.VERSION_1_6, useJack)
-                testLanguageLevel('Google:GoogleInc:22', JavaVersion.VERSION_1_6, useJack)
+                testLanguageLevel('Google Inc.:Google APIs:22', JavaVersion.VERSION_1_6, useJack)
             } finally {
                 System.setProperty(propName, originalVersion)
             }
@@ -506,7 +506,7 @@
 
         project.apply plugin: 'com.android.application'
         project.android {
-            compileSdkVersion 21
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             compileOptions {
@@ -519,30 +519,10 @@
 
         assertEquals(
                 JavaVersion.VERSION_1_6.toString(),
-                project.compileReleaseJava.targetCompatibility)
+                project.compileReleaseJavaWithJavac.targetCompatibility)
         assertEquals(
                 JavaVersion.VERSION_1_6.toString(),
-                project.compileReleaseJava.sourceCompatibility)
-    }
-
-    public void testSettingLanguageLevelFromCompileSdk_unknownVersion() {
-        Project project = ProjectBuilder.builder().withProjectDir(
-                new File(testDir, "${FOLDER_TEST_PROJECTS}/basic")).build()
-
-        project.apply plugin: 'com.android.application'
-        project.android {
-            compileSdkVersion 'foo'
-            buildToolsVersion '20.0.0'
-        }
-        AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
-        plugin.createAndroidTasks(false)
-
-        assertEquals(
-                JavaVersion.VERSION_1_6.toString(),
-                project.compileReleaseJava.targetCompatibility)
-        assertEquals(
-                JavaVersion.VERSION_1_6.toString(),
-                project.compileReleaseJava.sourceCompatibility)
+                project.compileReleaseJavaWithJavac.sourceCompatibility)
     }
 
     public void testMockableJarName() {
@@ -552,7 +532,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion "Google Inc.:Google APIs:21"
+            compileSdkVersion "Google Inc.:Google APIs:" + COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
         }
 
@@ -570,7 +550,7 @@
 
         project.apply plugin: 'com.android.application'
         project.android {
-            compileSdkVersion 21
+            compileSdkVersion COMPILE_SDK_VERSION
             buildToolsVersion '20.0.0'
 
             compileOptions {
@@ -583,7 +563,59 @@
 
         assertEquals(
                 "foo",
-                project.compileReleaseJava.options.encoding)
+                project.compileReleaseJavaWithJavac.options.encoding)
+    }
+
+    public void testInstrumentationRunnerArguments_merging() throws Exception {
+        Project project = ProjectBuilder.builder().withProjectDir(
+                new File(testDir, "${FOLDER_TEST_PROJECTS}/basic")).build()
+
+        project.apply plugin: 'com.android.application'
+
+        project.android {
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion '20.0.0'
+
+            defaultConfig {
+                testInstrumentationRunnerArguments(value: "default", size: "small")
+            }
+
+            productFlavors {
+                f1 {
+                }
+
+                f2  {
+                    testInstrumentationRunnerArgument "value", "f2"
+                }
+
+                f3  {
+                    testInstrumentationRunnerArguments["otherValue"] = "f3"
+                }
+
+                f4  {
+                    testInstrumentationRunnerArguments(otherValue: "f4.1")
+                    testInstrumentationRunnerArguments = [otherValue: "f4.2"]
+                }
+            }
+        }
+
+        AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
+        plugin.createAndroidTasks(false)
+
+        def variantsData = plugin.variantManager.variantDataList
+        Map<String, GradleVariantConfiguration> variantMap =
+                variantsData.collectEntries {[it.name, it.variantConfiguration]}
+
+        def expectedArgs = [
+                f1Debug: [value: "default", size: "small"],
+                f2Debug: [value: "f2", size: "small"],
+                f3Debug: [value: "default", size: "small", otherValue: "f3"],
+                f4Debug: [value: "default", size: "small", otherValue: "f4.2"],
+        ]
+
+        expectedArgs.each { name, expected ->
+            assert expected == variantMap[name].instrumentationRunnerArguments
+        }
     }
 
     private static void checkTestedVariant(@NonNull String variantName,
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
index fa0232e..d189740 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
@@ -41,7 +41,7 @@
 
     @Override
     protected void setUp() throws Exception {
-        SdkHandler.testSdkFolder = new File("foo")
+        SdkHandler.testSdkFolder = new File(System.getenv("ANDROID_HOME"))
     }
 
     public void testBasic() {
@@ -49,10 +49,10 @@
                 new File(testDir, "${FOLDER_TEST_PROJECTS}/basic")).build()
 
         project.apply plugin: 'com.android.application'
-
+1
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
         }
 
         AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
@@ -79,8 +79,8 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
 
             signingConfigs {
                 fakeConfig {
@@ -125,8 +125,8 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
             testBuildType "staging"
 
             buildTypes {
@@ -162,8 +162,8 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
 
             productFlavors {
                 flavor1 {
@@ -199,8 +199,8 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
 
             flavorDimensions   "dimension1", "dimension2"
 
@@ -264,8 +264,8 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
 
             signingConfigs {
                 one {
@@ -365,8 +365,8 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
 
             signingConfigs {
                 debug {
@@ -391,7 +391,7 @@
         project.apply plugin: 'com.android.application'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion COMPILE_SDK_VERSION
 
             signingConfigs {
                 foo.initWith(owner.signingConfigs.debug)
@@ -419,8 +419,8 @@
         project.apply plugin: 'java'
 
         project.android {
-            compileSdkVersion 15
-            buildToolsVersion "19"
+            compileSdkVersion COMPILE_SDK_VERSION
+            buildToolsVersion BUILD_TOOL_VERSION
         }
 
         AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy
index 2e49da3..1c7690c 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy
@@ -31,7 +31,7 @@
 
     @Override
     protected void setUp() throws Exception {
-        SdkHandler.testSdkFolder = new File("foo")
+        SdkHandler.testSdkFolder = new File(System.getenv("ANDROID_HOME"))
     }
 
     public void testBasic() {
@@ -41,7 +41,7 @@
         project.apply plugin: 'com.android.library'
 
         project.android {
-            compileSdkVersion 15
+            compileSdkVersion 21
         }
 
         project.afterEvaluate {
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy
index 9885fd5..e69ec0e 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy
@@ -19,7 +19,7 @@
 import com.android.build.gradle.AppPlugin
 import com.android.build.gradle.internal.test.BaseTest
 import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultBuildType
+import com.android.builder.model.BuildType
 import org.gradle.api.Project
 import org.gradle.testfixtures.ProjectBuilder
 
@@ -40,7 +40,7 @@
 
         AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
 
-        DefaultBuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.DEBUG).buildType
+        BuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.DEBUG).buildType
 
         assertTrue(type.isDebuggable())
         assertFalse(type.isJniDebuggable())
@@ -62,7 +62,7 @@
 
         AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
 
-        DefaultBuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.RELEASE).buildType
+        BuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.RELEASE).buildType
 
         assertFalse(type.isDebuggable())
         assertFalse(type.isJniDebuggable())
@@ -74,7 +74,8 @@
         Project project = ProjectBuilder.builder().withProjectDir(
                 new File(testDir, "basic")).build()
 
-        BuildType object1 = new BuildType("foo", project, project.getLogger())
+        com.android.build.gradle.internal.dsl.BuildType object1 =
+                new com.android.build.gradle.internal.dsl.BuildType("foo", project, project.getLogger())
 
         // change every value from their default.
         object1.setDebuggable(true)
@@ -89,7 +90,8 @@
         object1.setShrinkResources(true)
         object1.setUseJack(Boolean.FALSE)
 
-        BuildType object2 = new BuildType(object1.name, project, project.getLogger())
+        com.android.build.gradle.internal.dsl.BuildType object2 =
+                new com.android.build.gradle.internal.dsl.BuildType(object1.name, project, project.getLogger())
         object2.initWith(object1)
 
         assertEquals(object1, object2)
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
index fb3ecc2..aff91e4 100755
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
@@ -26,6 +26,9 @@
  */
 public abstract class BaseTest extends TestCase {
 
+    protected final static int COMPILE_SDK_VERSION = 21;
+    protected static final String BUILD_TOOL_VERSION = "22.0.1";
+
     public static final String FOLDER_TEST_PROJECTS = "test-projects";
 
     protected File sdkDir;
diff --git a/build-system/integration-test/build.gradle b/build-system/integration-test/build.gradle
index 48b35f7..36a3527 100644
--- a/build-system/integration-test/build.gradle
+++ b/build-system/integration-test/build.gradle
@@ -8,21 +8,36 @@
 dependencies {
     compile gradleApi()
     compile localGroovy()
-    compile project(':base:builder-model')
-    compile project(':base:builder')
-    compile project(':base:sdk-common')
-    compile "com.google.truth:truth:0.24"
-    testCompile "org.jacoco:org.jacoco.agent:0.7.4.201502262128"
+    testCompile project(':base:builder-model')
+    testCompile project(':base:builder')
+    testCompile project(':base:sdk-common')
+    testCompile 'com.google.truth:truth:0.26'
+    testCompile 'org.jacoco:org.jacoco.agent:0.7.4.201502262128'
+
+    // Add dependency on plugin code.  Exclude transitive dependencies to avoid conflict due to
+    // Groovy versions.
+    testCompile(project(':base:gradle-core')) {
+        transitive = false
+    }
+    testCompile(project(':base:gradle')) {
+        transitive = false
+    }
+    testCompile(project(':base:gradle-experimental')) {
+        transitive = false
+    }
 }
 
 def testEnvironment = [
     PROJECT_BUILD_DIR: project.buildDir,
     CUSTOM_REPO: rootProject.file("../out/repo"),
     CUSTOM_GRADLE: System.env.CUSTOM_GRADLE ?: rootProject.ext.buildVersion,
+    CUSTOM_EXPERIMENTAL_GRADLE: System.env.CUSTOM_EXPERIMENTAL_GRADLE ?:rootProject.ext.experimentalVersion,
     ANDROID_HOME: System.env.ANDROID_HOME,
     ANDROID_NDK_HOME: System.env.ANDROID_NDK_HOME,
     CUSTOM_BUILDTOOLS: System.env.CUSTOM_BUILDTOOLS,
     CUSTOM_JACK: System.env.CUSTOM_JACK,
+    DEBUG_INNER_TEST: System.env.DEBUG_INNER_TEST,
+    RECORD_SPANS: System.env.RECORD_SPANS,
 ].findAll { it.value != null }
 
 // These tasks will not depend on publishLocal, so they will run integration
@@ -39,12 +54,22 @@
     systemProperties['jar.path'] = jar.archivePath
     environment = testEnvironment
 
+    // Always run the task, when requested.
+    outputs.upToDateWhen { false }
+
     forkEvery = 1
     maxParallelForks = Runtime.runtime.availableProcessors() / 2
 
     useJUnit {
+        if (System.properties['test.includeCategories'] != null) {
+            def categories = System.properties['test.includeCategories'].split(',')
+            String defaultPackage = "com.android.build.gradle.integration.common.category."
+            categories = categories.collect { it.charAt(0).isUpperCase() ? defaultPackage + it : it }
+            includeCategories categories as String[]
+        }
         excludeCategories "com.android.build.gradle.integration.common.category.DeviceTests"
     }
+    exclude "com/android/build/gradle/integration/performance/**"
 }
 
 task connectedIntegrationTest(type: Test)
@@ -59,10 +84,21 @@
                     "and a device."
     group = "verification"
     systemProperties['jar.path'] = jar.archivePath
-    environment = testEnvironment
+    // Add to, rather than replace the environment, so that TEST_CLASSPATH_DEPENDENCY,
+    // REMOTE_TEST_PROVIDER, ADDITIONAL_TEST_CUSTOM_REPO and any dependencies of the remote test
+    // provider are present in the test environment only for these tests.
+    environment testEnvironment
+
+    // Always run the task, when requested.
+    outputs.upToDateWhen { false }
+
+    forkEvery= 1
+    maxParallelForks = System.env.containsKey("REMOTE_TEST_PROVIDER") ? Runtime.runtime.availableProcessors() : 1
+
     useJUnit {
         includeCategories "com.android.build.gradle.integration.common.category.DeviceTests"
     }
+    exclude "com/android/build/gradle/integration/performance/**"
 }
 
 task performanceTest(type: Test) {
@@ -86,12 +122,14 @@
 test.dependsOn ':publishLocal'
 connectedIntegrationTest.dependsOn ':publishLocal'
 performanceTest.dependsOn ':publishLocal'
-check.dependsOn performanceTest
 
 jacocoTestReport {
+    sourceSets project(':base:gradle-experimental').sourceSets.main
     sourceSets project(':base:gradle').sourceSets.main
+    sourceSets project(':base:gradle-core').sourceSets.main
     sourceSets project(':base:builder').sourceSets.main
     sourceSets project(':base:builder-model').sourceSets.main
+    sourceSets project(':base:builder-test-api').sourceSets.main
 }
 
 // Due to memory constraints, apply jacoco only when jacocoTestReport is invoked.  Make sure to
diff --git a/build-system/integration-test/integration-test.iml b/build-system/integration-test/integration-test.iml
index bf9181c..7438448 100644
--- a/build-system/integration-test/integration-test.iml
+++ b/build-system/integration-test/integration-test.iml
@@ -4,6 +4,7 @@
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/test/groovy" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy
index 609e273..49bacce 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy
@@ -20,14 +20,11 @@
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
 import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
-import com.android.builder.model.SyncIssue
 import groovy.transform.CompileStatic
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
 
 /**
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy
index db3be84..c88394e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy
@@ -42,10 +42,12 @@
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("abiPureSplits")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         model = project.executeAndReturnModel("clean", "assembleDebug")
     }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy
index 2d1a744..2f8918e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy
@@ -23,6 +23,7 @@
 import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
 import com.android.builder.model.AndroidProject
 import com.google.common.base.Joiner
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -34,6 +35,7 @@
 /**
  * Check resources in androidTest are available in the generated R.java.
  */
+@CompileStatic
 class AndroidTestResourcesTest {
     private static AndroidTestApp testApp = new HelloWorldApp()
     static {
@@ -140,7 +142,7 @@
     @Test
     @Category(DeviceTests.class)
     public void "check test layout can be used in device tests"() {
-        appProject.execute("connectedAndroidTest")
+        appProject.executeConnectedCheck()
     }
 
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ApplicationIdInLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ApplicationIdInLibsTest.groovy
new file mode 100644
index 0000000..9b5dcaa
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ApplicationIdInLibsTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ApkHelper
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidArtifactOutput
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertNotNull
+
+/**
+ * Tests for @{applicationId} placeholder presence in library manifest files.
+ * Such placeholders should be left intact until the library is merged into a consuming application
+ * with a known application Id.
+ */
+@CompileStatic
+class ApplicationIdInLibsTest {
+    static Map<String, AndroidProject> models
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("applicationIdInLibsTest")
+            .create()
+
+    @BeforeClass
+    static void setup() {
+        models = project.executeAndReturnMultiModel("clean",
+                ":examplelibrary:generateDebugAndroidTestSources", "app:assembleDebug")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+        models = null
+    }
+
+    @Test
+    public void "test library placeholder substitution in final apk"() throws Exception {
+
+        // Load the custom model for the project
+        Collection<Variant> variants = models.get(":app").getVariants()
+        assertEquals("Variant Count", 2 , variants.size())
+
+        // get the main artifact of the debug artifact
+        Variant debugVariant = ModelHelper.getVariant(variants, "flavorDebug")
+        assertNotNull("debug Variant null-check", debugVariant)
+        AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+        assertNotNull("Debug main info null-check", debugMainArtifact)
+
+        // get the outputs.
+        Collection<AndroidArtifactOutput> debugOutputs = debugMainArtifact.getOutputs()
+        assertNotNull(debugOutputs)
+
+        assertEquals(1, debugOutputs.size())
+        AndroidArtifactOutput output = debugOutputs.iterator().next()
+        assertEquals(1, output.getOutputs().size())
+
+        List<String> apkBadging =
+                ApkHelper.getApkBadging(output.getOutputs().iterator().next().getOutputFile());
+
+        for (String line : apkBadging) {
+            if (line.contains("uses-permission: name=" +
+                    "'com.example.manifest_merger_example.flavor.permission.C2D_MESSAGE'")) {
+                return;
+            }
+        }
+        Assert.fail("failed to find the permission with the right placeholder substitution.")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy
index 1493f9b..707d562 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
  * Ensures that archivesBaseName setting on android project is used when choosing the apk file
  * names
  */
+@CompileStatic
 class ArchivesBaseNameTest {
 
     @ClassRule
@@ -56,8 +58,11 @@
 
     @Test
     void "check model failed to load"() {
-        File outputFile = models.get(":").getVariants().get(0).getMainArtifact().getOutputs()
-                .get(0).getMainOutputFile().getOutputFile()
+        File outputFile = models.get(":").getVariants().iterator().next()
+                .getMainArtifact()
+                .getOutputs().iterator().next()
+                .getMainOutputFile()
+                .getOutputFile()
 
         assertThat(outputFile.getName().startsWith("random_apk_name"))
     }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy
index c06402e..64019fe 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy
@@ -28,6 +28,8 @@
 import com.android.builder.model.SourceProvider
 import com.android.builder.model.SourceProviderContainer
 import com.android.builder.model.Variant
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -41,9 +43,10 @@
 /**
  * Assemble tests for artifactApi.
  */
+@CompileStatic
 class ArtifactApiTest {
     // Unit test variants produce an extra Java artifact.
-    private static final DEFAULT_EXTRA_JAVA_ARTIFACTS = 1
+    private static final int DEFAULT_EXTRA_JAVA_ARTIFACTS = 1
 
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -174,4 +177,12 @@
             assertFalse(deps.getJavaLibraries().isEmpty())
         }
     }
+
+    @Test
+    public void backwardsCompatible() throws Exception {
+        // ATTENTION Author and Reviewers - please make sure required changes to the build file
+        // are backwards compatible before updating this test.
+        assertThat(FileUtils.sha1(project.file("build.gradle")))
+                .isEqualTo("cf6fa23a32f342718b1f342fc97846f56665a155")
+    }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoDensitySplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoDensitySplitTest.groovy
new file mode 100644
index 0000000..0b5932e
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoDensitySplitTest.groovy
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidArtifactOutput
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+import static org.junit.Assert.assertEquals
+
+/**
+ * MultiAPK test where densities are obtained automatically.
+ */
+@CompileStatic
+class AutoDensitySplitTest {
+    static AndroidProject model
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("densitySplit")
+            .create()
+
+    @BeforeClass
+    static void setUp() {
+        project.getBuildFile() << """android {
+          splits {
+            density {
+              enable true
+              auto true
+              compatibleScreens 'small', 'normal', 'large', 'xlarge'
+            }
+          }
+        }"""
+        model = project.executeAndReturnModel("clean", "assembleDebug")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+        model = null
+    }
+
+    @Test
+    void lint() {
+        project.execute("lint")
+    }
+
+    @Test
+    void testPackaging() {
+        for (Variant variant : model.getVariants()) {
+            AndroidArtifact mainArtifact = variant.getMainArtifact()
+            if (!variant.getBuildType().equalsIgnoreCase("Debug")) {
+                continue
+            }
+            assertEquals(5, mainArtifact.getOutputs().size())
+
+            File mdpiApk = project.getApk("mdpi", "debug")
+            assertThatZip(mdpiApk).contains("res/drawable-mdpi-v4/other.png")
+        }
+    }
+
+    @Test
+    void "check version code in apk"() {
+        File universalApk = project.getApk("universal", "debug")
+        assertThatApk(universalApk).hasVersionCode(112)
+        assertThatApk(universalApk).hasVersionName("version 112")
+
+        File mdpiApk = project.getApk("mdpi", "debug")
+        assertThatApk(mdpiApk).hasVersionCode(212)
+        assertThatApk(mdpiApk).hasVersionName("version 212")
+
+        File hdpiApk = project.getApk("hdpi", "debug")
+        assertThatApk(hdpiApk).hasVersionCode(312)
+        assertThatApk(hdpiApk).hasVersionName("version 312")
+
+        File xhdpiApk = project.getApk("xhdpi", "debug")
+        assertThatApk(xhdpiApk).hasVersionCode(412)
+        assertThatApk(xhdpiApk).hasVersionName("version 412")
+
+        File xxhdiApk = project.getApk("xxhdpi", "debug")
+        assertThatApk(xxhdiApk).hasVersionCode(512)
+        assertThatApk(xxhdiApk).hasVersionName("version 512")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy
index bda2d28..db856c2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy
@@ -15,17 +15,15 @@
  */
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.android.build.gradle.integration.common.utils.ApkHelper
 import com.android.build.gradle.integration.common.utils.ModelHelper
 import com.android.builder.model.AndroidArtifact
 import com.android.builder.model.AndroidArtifactOutput
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
 import com.google.common.collect.Sets
-import com.google.common.truth.Truth
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -35,7 +33,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-
 /**
  * Test to ensure that "auto" resConfig setting only package application's languages.
  */
@@ -50,6 +47,7 @@
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.getBuildFile() << """android {
               defaultConfig {
                 versionCode 12
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy
index 1880153..ac985d5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy
@@ -24,6 +24,7 @@
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
 import com.google.common.truth.Truth
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -36,6 +37,7 @@
 /**
  * Test to ensure that "auto" resConfig setting only package application's languages.
  */
+@CompileStatic
 class AutoResConfig {
 
     static AndroidProject model
@@ -47,6 +49,7 @@
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.getBuildFile() << """android {
               defaultConfig {
                 versionCode 12
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy
index 0085d90..3d6dfee 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy
@@ -24,6 +24,7 @@
 import com.android.builder.model.ProductFlavorContainer
 import com.android.builder.model.SourceProviderContainer
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -36,6 +37,7 @@
 /**
  * Assemble tests for basicMultiFlavors
  */
+@CompileStatic
 class BasicMultiFlavorTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy
index b696944..7b41d85 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.category.SmokeTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ModelHelper
 import com.android.build.gradle.integration.common.utils.SigningConfigHelper
@@ -42,11 +43,13 @@
 /**
  * Assemble tests for basic.
  */
+@Category(SmokeTests.class)
 @CompileStatic
 class BasicTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("basic")
+            .withoutNdk()
             .create()
 
     static public AndroidProject model
@@ -85,8 +88,12 @@
                 model.getAaptOptions().getFailOnMissingConfigEntry())
 
         JavaCompileOptions javaCompileOptions = model.getJavaCompileOptions()
-        assertEquals("1.6", javaCompileOptions.getSourceCompatibility())
-        assertEquals("1.6", javaCompileOptions.getTargetCompatibility())
+        // since source and target compatibility are not explicitly set in the build.gradle,
+        // the default value should be the JDK version used to build against.
+        assertEquals(System.getProperty("java.specification.version"),
+                javaCompileOptions.getSourceCompatibility())
+        assertEquals(System.getProperty("java.specification.version"),
+                javaCompileOptions.getTargetCompatibility())
         assertEquals("UTF-8", javaCompileOptions.getEncoding())
     }
 
@@ -150,12 +157,13 @@
     @Test
     @Category(DeviceTests.class)
     void install() {
+        GradleTestProject.assumeLocalDevice();
         project.execute("installDebug", "uninstallAll")
     }
 
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy
index 3683068..df29fac 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy
@@ -35,6 +35,7 @@
 import com.android.builder.model.Variant
 import com.google.common.collect.Maps
 import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -52,6 +53,7 @@
 /**
  * Assemble tests for basic that loads the model but doesn't build.
  */
+@CompileStatic
 class BasicTest2 {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -282,12 +284,13 @@
     @Test
     @Category(DeviceTests.class)
     void install() {
+        GradleTestProject.assumeLocalDevice();
         project.execute("installDebug", "uninstallAll")
     }
 
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BootClasspathTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BootClasspathTest.groovy
new file mode 100644
index 0000000..54e6e2d
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BootClasspathTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Test BaseExtension.getBootClasspath can be use before afterEvaluate.
+ */
+@CompileStatic
+class BootClasspathTest {
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldApp())
+            .create()
+
+    @Before
+    public void setUp() {
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+}
+
+task checkBootClasspath {
+    assert android.getBootClasspath() != null
+}
+"""
+    }
+
+    @Test
+    public void "check getBootClasspath can be called"() {
+        project.execute("checkBootClasspath")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy
index 8f65b20..30eec74 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy
@@ -28,7 +28,8 @@
 import com.google.common.base.Charsets
 import com.google.common.collect.Maps
 import com.google.common.io.Files
-import junit.framework.Assert
+import groovy.transform.CompileStatic
+import org.junit.Assert
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -41,6 +42,7 @@
  * Test for BuildConfig field declared in build type, flavors, and variant and how they
  * override each other
  */
+@CompileStatic
 class BuildConfigTest {
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
@@ -121,7 +123,7 @@
   public static final String BUILD_TYPE = "debug";
   public static final String FLAVOR = "flavor1";
   public static final int VERSION_CODE = 1;
-  public static final String VERSION_NAME = "";
+  public static final String VERSION_NAME = "1.0";
   // Fields from the variant
   public static final int VALUE_VARIANT = 1000;
   // Fields from build type: debug
@@ -159,7 +161,7 @@
   public static final String BUILD_TYPE = "debug";
   public static final String FLAVOR = "flavor2";
   public static final int VERSION_CODE = 1;
-  public static final String VERSION_NAME = "";
+  public static final String VERSION_NAME = "1.0";
   // Fields from the variant
   public static final int VALUE_VARIANT = 1000;
   // Fields from build type: debug
@@ -197,7 +199,7 @@
   public static final String BUILD_TYPE = "release";
   public static final String FLAVOR = "flavor1";
   public static final int VERSION_CODE = 1;
-  public static final String VERSION_NAME = "";
+  public static final String VERSION_NAME = "1.0";
   // Fields from product flavor: flavor1
   public static final int VALUE_DEBUG = 10;
   public static final int VALUE_FLAVOR = 10;
@@ -233,7 +235,7 @@
   public static final String BUILD_TYPE = "release";
   public static final String FLAVOR = "flavor2";
   public static final int VERSION_CODE = 1;
-  public static final String VERSION_NAME = "";
+  public static final String VERSION_NAME = "1.0";
   // Fields from product flavor: flavor2
   public static final int VALUE_DEBUG = 20;
   public static final int VALUE_FLAVOR = 20;
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy
index 05ea823..ac203e4 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy
@@ -18,7 +18,9 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.google.common.collect.ImmutableList
 import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -26,8 +28,13 @@
 import java.util.regex.Matcher
 import java.util.regex.Pattern
 
-import static org.junit.Assert.assertTrue
+import static com.google.common.truth.Truth.assert_
 
+@CompileStatic
+/**
+ * Tests to ensure that changing the build tools version in the build.gradle will trigger
+ * re-execution of some tasks even if no source file change was detected.
+ */
 class BuildToolsTest {
 
     private static final Pattern UP_TO_DATE_PATTERN = ~/:(\S+)\s+UP-TO-DATE/
@@ -35,13 +42,19 @@
     private static final Pattern INPUT_CHANGED_PATTERN =
             ~/Value of input property 'buildToolsVersion' has changed for task ':(\S+)'/
 
-    private static final String[] tasks = [
-            "preDexDebug", "dexDebug", "compileDebugAidl", "compileDebugRenderscript",
+    private static final String[] COMMON_TASKS = [
+            "compileDebugAidl", "compileDebugRenderscript",
             "mergeDebugResources", "processDebugResources",
-            "preDexRelease", "dexRelease", "compileReleaseAidl", "compileReleaseRenderscript",
+            "compileReleaseAidl", "compileReleaseRenderscript",
             "mergeReleaseResources", "processReleaseResources"
     ]
 
+    private static final List<String> JAVAC_TASKS = ImmutableList.builder().add(COMMON_TASKS)
+            .add("preDexDebug").add("dexDebug").add("preDexRelease").add("dexRelease").build()
+    private static final List<String> JACK_TASKS = ImmutableList.builder().add(COMMON_TASKS)
+            .add("jillDebugRuntimeLibraries").add("jillDebugPackagedLibraries")
+            .add("jillReleaseRuntimeLibraries").add("jillReleasePackagedLibraries").build()
+
     @Rule
     public GradleTestProject project = GradleTestProject.builder()
             .fromTestApp(new HelloWorldApp())
@@ -67,32 +80,35 @@
         project.execute("assemble")
 
         Set<String> skippedTasks = getTasksMatching(UP_TO_DATE_PATTERN, project.stdout)
-        for (String task : tasks) {
-            assertTrue(String.format("Expecting task %s to be UP-TO-DATE" , task),
-                    skippedTasks.contains(task))
-        }
+        assert_().withFailureMessage("Expecting tasks to be UP-TO-DATE").that(skippedTasks)
+                .containsAllIn(GradleTestProject.USE_JACK ? JACK_TASKS : JAVAC_TASKS)
     }
 
     @Test
     public void invalidateBuildTools() {
         project.execute("assemble")
+        // Change our build tools version to 22.0.1 unless it is already the current version,
+        // in that case, downgrade to 21.1.2.
+        // The point is, change the build tools version from what it was when the "assemble" task
+        // was executed right before this comment.
+        String newBuildToolVersion =
+                GradleTestProject.DEFAULT_BUILD_TOOL_VERSION.contentEquals("22.0.1") ?
+                        "21.1.2" : "22.0.1"
 
         project.getBuildFile() << """
 apply plugin: 'com.android.application'
 
 android {
     compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-    buildToolsVersion "19.1.0"
+    buildToolsVersion '$newBuildToolVersion'
 }
 """
 
         project.stdout.reset()
         project.execute("assemble")
         Set<String> affectedTasks = getTasksMatching(INPUT_CHANGED_PATTERN, project.stdout)
-        for (String task : tasks) {
-            assertTrue(String.format("Expecting task %s to be invalidated", task),
-                    affectedTasks.contains(task))
-        }
+        assert_().withFailureMessage("Expecting tasks to be invalidated").that(affectedTasks)
+                .containsAllIn(GradleTestProject.USE_JACK ? JACK_TASKS : JAVAC_TASKS)
     }
 
     private static Set<String> getTasksMatching(Pattern pattern, ByteArrayOutputStream output) {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy
index cd5fbbf..5559724 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ModelHelper
@@ -34,10 +33,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
-
-
 /**
  * Test drive for the CombinedAbiDensityPureSplits samples test.
  */
@@ -47,10 +42,12 @@
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("combinedAbiDensityPureSplits")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         model = project.executeAndReturnModel("clean", "assembleDebug")
     }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy
index d726701..80606da 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ModelHelper
@@ -33,7 +32,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-
 /**
  * test driver for combined density and language pure splits test.
  */
@@ -48,6 +46,7 @@
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         model = project.executeAndReturnModel("clean", "assembleDebug")
     }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy
index ff311c3..0078173 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ApkHelper
@@ -35,7 +34,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-
 /**
  * Tests that produce density pure splits but leave languages in the main APK.
  */
@@ -50,6 +48,7 @@
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.getBuildFile() << """android {
           splits {
             language {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy
index f0faa35..684b121 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ApkHelper
@@ -35,8 +34,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-import static org.junit.Assert.fail;
-
 /**
  * Tests that create pure splits for language but keep drawable resources in the main APK.
  */
@@ -51,6 +48,7 @@
 
     @BeforeClass
     static void setup() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.getBuildFile() << """android {
             splits {
                 density {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy
index db4f889..9479622 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy
@@ -22,6 +22,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.JavaLibrary
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -33,6 +34,7 @@
 /**
  * Assemble tests for customArtifactDep.
  */
+@CompileStatic
 class CustomArtifactDepTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy
index fa59f50..38e7a28 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-
-
 package com.android.build.gradle.integration.application
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
@@ -25,6 +23,7 @@
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
 import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -34,7 +33,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-
 /**
  * Assemble tests for class densitySplitInL
  .
@@ -50,6 +48,7 @@
 
     @BeforeClass
     static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         model = project.executeAndReturnModel("clean", "assembleDebug")
     }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy
index 55fb264..b930382 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy
@@ -75,10 +75,8 @@
             }
             assertEquals(5, mainArtifact.getOutputs().size())
 
-            for (AndroidArtifactOutput output : mainArtifact.getOutputs()) {
-                assertThatZip(output.getMainOutputFile().getOutputFile())
-                        .contains("res/drawable-mdpi-v4/other.png")
-            }
+            File mdpiApk = project.getApk("mdpi", "debug")
+            assertThatZip(mdpiApk).contains("res/drawable-mdpi-v4/other.png")
         }
     }
 
@@ -154,6 +152,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy
index 4b8542e..8377beb 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-
-
-
-
 package com.android.build.gradle.integration.application
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 
+@CompileStatic
 class DensitySplitWithPublishNonDefaultTest {
 
     @Rule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy
index fba9160..d5ca959 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for dependencies.
  */
+@CompileStatic
 class DependenciesTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy
index 432a1b9..e81aab5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy
@@ -14,11 +14,10 @@
  * limitations under the License.
  */
 
-
-
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +26,7 @@
 /**
  * Assemble tests for dependenciesWithVariants.
  */
+@CompileStatic
 class DependenciesWithVariantsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy
index 7f86afb..ad17ddf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy
@@ -16,6 +16,7 @@
 
 package com.android.build.gradle.integration.application
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.gradle.tooling.BuildException
 import org.junit.AfterClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for dependencyChecker.
  */
+@CompileStatic
 class DependencyCheckerTest {
     @ClassRule
     static public GradleTestProject httpClientProject = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCyclesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCyclesTest.groovy
new file mode 100644
index 0000000..97c6ed0
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCyclesTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.gradle.tooling.BuildException
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import static org.junit.Assert.fail
+
+/**
+ * Check that we recognize dependency cycles.
+ */
+@CompileStatic
+class DependencyCyclesTest {
+
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("applibtest")
+            .captureStdErr(true)
+            .create()
+
+    @Test
+    public void cycle() {
+        project.file("lib/build.gradle") << """
+dependencies {
+    compile project(':app')
+}
+"""
+
+        try {
+            project.execute("clean", ":app:assemble")
+            fail("should throw")
+        } catch (BuildException e) {
+            // expected.
+        }
+
+        String output = project.stderr.toString()
+        assertThat(output).contains(":app -> :lib -> :app")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy
index 54658d1..218957b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for emptySplit.
  */
+@CompileStatic
 class EmptySplitTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy
index 8c63819..356024b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy
@@ -154,7 +154,7 @@
         AndroidProject model = modelMap.get(':app2')
         assertNotNull(model)
 
-        SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+        SyncIssue issue = assertThat(model).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_DEPENDENCY_IS_APK,
                 'project:app1:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy
index bca6937..d40c63b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-
-
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
+import com.google.common.base.Charsets
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,7 +29,14 @@
 
 /**
  * Integration test for extracting annotations.
+ * <p>
+ * Tip: To execute just this test after modifying the annotations extraction code:
+ * <pre>
+ *     $ cd tools
+ *     $ ./gradlew :base:i:test -Dtest.single=ExtractAnnotationTest
+ * </pre>
  */
+@CompileStatic
 class ExtractAnnotationTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -49,44 +56,18 @@
     @Test
     void "check extract annotation"() {
         File debugFileOutput = project.file("build/$AndroidProject.FD_INTERMEDIATES/annotations/debug")
+        File classesJar = project.file("build/$AndroidProject.FD_INTERMEDIATES/bundles/debug/classes.jar")
         File file = new File(debugFileOutput, "annotations.zip")
 
         //noinspection SpellCheckingInspection
         String expectedContent = (""
                 + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                 + "<root>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest ExtractTest(int, java.lang.String) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest ExtractTest(int, java.lang.String) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.StringRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest int getHiddenMethod()\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest int getPrivate()\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
                 + "  <item name=\"com.android.tests.extractannotations.ExtractTest int getVisibility()\">\n"
                 + "    <annotation name=\"android.support.annotation.IntDef\">\n"
                 + "      <val name=\"value\" val=\"{com.android.tests.extractannotations.ExtractTest.VISIBLE, com.android.tests.extractannotations.ExtractTest.INVISIBLE, com.android.tests.extractannotations.ExtractTest.GONE, 5, 17, com.android.tests.extractannotations.Constants.CONSTANT_1}\" />\n"
                 + "    </annotation>\n"
                 + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest int resourceTypeMethod(int, int)\">\n"
-                + "    <annotation name=\"android.support.annotation.StringRes\" />\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest int resourceTypeMethod(int, int) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.DrawableRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest int resourceTypeMethod(int, int) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "    <annotation name=\"android.support.annotation.ColorRes\" />\n"
-                + "  </item>\n"
-                // This item should be removed when I start supporting @hide
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest java.lang.Object getPackagePrivate()\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
                 + "  <item name=\"com.android.tests.extractannotations.ExtractTest java.lang.String getStringMode(int)\">\n"
                 + "    <annotation name=\"android.support.annotation.StringDef\">\n"
                 + "      <val name=\"value\" val=\"{com.android.tests.extractannotations.ExtractTest.STRING_1, com.android.tests.extractannotations.ExtractTest.STRING_2, &quot;literalValue&quot;, &quot;concatenated&quot;}\" />\n"
@@ -103,15 +84,6 @@
                 + "      <val name=\"value\" val=\"{com.android.tests.extractannotations.Constants.CONSTANT_1, com.android.tests.extractannotations.Constants.CONSTANT_2}\" />\n"
                 + "    </annotation>\n"
                 + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest void resourceTypeMethodWithTypeArgs(java.util.Map&lt;java.lang.String,? extends java.lang.Number&gt;, T, int) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.StringRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest void resourceTypeMethodWithTypeArgs(java.util.Map&lt;java.lang.String,? extends java.lang.Number&gt;, T, int) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.DrawableRes\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest void resourceTypeMethodWithTypeArgs(java.util.Map&lt;java.lang.String,? extends java.lang.Number&gt;, T, int) 2\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
                 + "  <item name=\"com.android.tests.extractannotations.ExtractTest void testMask(int) 0\">\n"
                 + "    <annotation name=\"android.support.annotation.IntDef\">\n"
                 + "      <val name=\"flag\" val=\"true\" />\n"
@@ -124,9 +96,6 @@
                 + "      <val name=\"value\" val=\"{0, com.android.tests.extractannotations.Constants.CONSTANT_1, com.android.tests.extractannotations.Constants.CONSTANT_3}\" />\n"
                 + "    </annotation>\n"
                 + "  </item>\n"
-                + "  <item name=\"com.android.tests.extractannotations.ExtractTest.HiddenClass int getHiddenMember()\">\n"
-                + "    <annotation name=\"android.support.annotation.IdRes\" />\n"
-                + "  </item>\n"
                 + "  <item name=\"com.android.tests.extractannotations.TopLevelTypeDef\">\n"
                 + "    <annotation name=\"android.support.annotation.IntDef\">\n"
                 + "      <val name=\"flag\" val=\"true\" />\n"
@@ -135,11 +104,28 @@
                 + "  </item>\n"
                 + "</root>\n")
 
-
         assertThatZip(file).containsFileWithContent(
                 "com/android/tests/extractannotations/annotations.xml", expectedContent)
 
         // check the resulting .aar file to ensure annotations.zip inclusion.
         assertThatZip(project.getAar("debug")).contains("annotations.zip")
+
+        // Check typedefs removals:
+
+        // public typedef: should be present
+        assertThatZip(classesJar).contains(
+                "com/android/tests/extractannotations/ExtractTest\$Visibility.class")
+
+        // private/protected typedefs: should have been removed
+        assertThatZip(classesJar).doesNotContain(
+                "com/android/tests/extractannotations/ExtractTest\$Mask.class")
+        assertThatZip(classesJar).doesNotContain(
+                "com/android/tests/extractannotations/ExtractTest\$NonMaskType.class")
+
+        // Make sure the NonMask symbol (from a private typedef) is completely gone from the
+        // outer class
+        assertThatZip(classesJar).containsFileWithoutContent(
+                "com/android/tests/extractannotations/ExtractTest.class",
+                "NonMaskType".getBytes(Charsets.UTF_8));
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy
index 0defc8a..157bc12 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy
@@ -19,6 +19,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
 /**
  * Assemble tests for filteredOutBuildType.
  */
+@CompileStatic
 class FilteredOutBuildTypeTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy
index b18b33e..a322801 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy
@@ -19,6 +19,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -30,6 +31,7 @@
 /**
  * Assemble tests for filteredOutVariants.
  */
+@CompileStatic
 class FilteredOutVariantsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy
index d95302d..d700363 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for flavored.
  */
+@CompileStatic
 class FlavoredTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy
index 65bae99..ef71787 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy
@@ -27,6 +27,7 @@
 import com.android.builder.model.ProductFlavorContainer
 import com.android.builder.model.SourceProviderContainer
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -43,6 +44,7 @@
 /**
  * Assemble tests for flavors.
  */
+@CompileStatic
 class FlavorsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -124,6 +126,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy
index 27f5001..7f2f7f1 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy
@@ -20,6 +20,8 @@
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.JavaArtifact
 import com.android.builder.model.Variant
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -31,6 +33,7 @@
 /**
  * Assemble tests for genFolderApi2.
  */
+@CompileStatic
 class GenFolderApi2Test {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -72,7 +75,7 @@
             })
 
             // Unit testing artifact:
-            assertThat(variant.extraJavaArtifacts).hasSize(1)
+            assertThat(variant.getExtraJavaArtifacts()).hasSize(1)
             JavaArtifact unitTestArtifact = variant.extraJavaArtifacts.first()
             def sortedFolders = unitTestArtifact.generatedSourceFolders.sort()
             assertThat(sortedFolders).hasSize(2)
@@ -82,4 +85,12 @@
             assertThat(sortedFolders[1].absolutePath).endsWith("-2")
         }
     }
+
+    @Test
+    public void backwardsCompatible() throws Exception {
+        // ATTENTION Author and Reviewers - please make sure required changes to the build file
+        // are backwards compatible before updating this test.
+        assertThat(FileUtils.sha1(project.file("build.gradle")))
+                .isEqualTo("93b7507ce31a087a4efa4cae66474ad320e25b6c")
+    }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy
index 2a321b8..4a0240d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy
@@ -19,11 +19,14 @@
 import com.android.builder.model.AndroidArtifact
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
 import static org.junit.Assert.assertNotNull
@@ -31,6 +34,7 @@
 /**
  * Assemble tests for genFolderApi.
  */
+@CompileStatic
 class GenFolderApiTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -121,4 +125,12 @@
             assertTrue("custom generated res folder check", found)
         }
     }
+
+    @Test
+    public void backwardsCompatible() throws Exception {
+        // ATTENTION Author and Reviewers - please make sure required changes to the build file
+        // are backwards compatible before updating this test.
+        assertThat(FileUtils.sha1(project.file("build.gradle")))
+                .isEqualTo("0d39ec75ccdc0f4adbc95b69a639117246bc0574")
+    }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy
index 48f8327..fe15388 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy
@@ -17,20 +17,19 @@
 
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.google.common.collect.ImmutableList
-import org.gradle.tooling.BuildException
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
-
 /**
  * Test Jack integration.
  */
+@CompileStatic
 class JackTest {
     private final static List<String> JACK_OPTIONS = ImmutableList.of(
             "-PCUSTOM_JACK=1",
@@ -56,6 +55,7 @@
 
     @BeforeClass
     static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21, 1, 0)
         basic.execute(JACK_OPTIONS, "clean", "assembleDebug")
         minify.execute(JACK_OPTIONS, "clean", "assembleDebug")
         multiDex.execute(JACK_OPTIONS, "clean", "assembleDebug")
@@ -76,13 +76,13 @@
     @Test
     @Category(DeviceTests.class)
     void "basic connectedCheck"() {
-        basic.execute(JACK_OPTIONS, "connectedCheck")
+        basic.executeConnectedCheck(JACK_OPTIONS)
     }
 
     @Test
     @Category(DeviceTests.class)
     void "multiDex connectedCheck"() {
-        multiDex.execute(JACK_OPTIONS, "connectedCheck")
+        multiDex.executeConnectedCheck(JACK_OPTIONS)
     }
 
     @Test
@@ -90,8 +90,16 @@
         minify.execute("testMinified")
     }
 
-    @Test(expected = BuildException.class)
+    @Test
     void "minify unitTests with Jack"() {
-        minify.execute(JACK_OPTIONS, "testMinified")
+        minify.execute(JACK_OPTIONS, "clean", "testMinified")
+
+        // Make sure javac was run.
+        File classesDir = new File(minify.testDir, "/build/intermediates/classes/minified")
+        assert classesDir.exists()
+
+        // Make sure jack was not run.
+        File jillDir = new File(minify.testDir, "/build/intermediates/jill")
+        assert !jillDir.exists()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JarJarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JarJarTest.groovy
new file mode 100644
index 0000000..ad7f519
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JarJarTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+
+import com.android.build.gradle.integration.common.truth.ApkSubject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.google.common.collect.Iterators;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk;
+import static com.android.builder.core.BuilderConstants.DEBUG;
+import static org.junit.Assert.assertEquals;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidArtifactOutput;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Variant;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test for the jarjar integration.
+ */
+public class JarJarTest {
+
+    static AndroidProject model
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject(GradleTestProject.USE_JACK ? "jarjarWithJack" : "jarjarIntegration")
+            .create()
+
+    @BeforeClass
+    static void setUp() {
+        model = project.executeAndReturnModel("clean", "assembleDebug")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+        model = null
+    }
+
+    @Test
+    void "check repackaged gson library"() {
+        Collection<Variant> variants = model.getVariants()
+        assertEquals("Variant Count", 2, variants.size())
+
+        // get the main artifact of the debug artifact
+        Variant debugVariant = ModelHelper.getVariant(variants, DEBUG)
+        assertNotNull("debug Variant null-check", debugVariant)
+        AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+        assertNotNull("Debug main info null-check", debugMainArtifact)
+
+        // get the outputs.
+        Collection<AndroidArtifactOutput> debugOutputs = debugMainArtifact.getOutputs()
+        assertNotNull(debugOutputs)
+        assertEquals(1, debugOutputs.size())
+
+        // make sure the Gson library has been renamed and the original one is not present.
+        File outputFile = Iterators.getOnlyElement(debugOutputs.iterator()).mainOutputFile.
+                getOutputFile()
+        assertThatApk(outputFile).containsClass("Lcom/google/repacked/gson/Gson;");
+        assertThatApk(outputFile).doesNotContainClass("Lcom/google/gson/Gson;")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy
index b32c1b4..b751224 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
@@ -28,6 +29,7 @@
 /**
  * Integration tests for manifest merging.
  */
+@CompileStatic
 class ManifestMergingTest {
 
     @ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MessageRewriteTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MessageRewriteTest.groovy
new file mode 100644
index 0000000..6c90eb6
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MessageRewriteTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+
+import com.android.build.gradle.integration.common.fixture.TemporaryProjectModification
+import com.android.builder.model.AndroidProject
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.google.common.base.Charsets
+import com.google.common.io.Files
+import groovy.transform.CompileStatic
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+/**
+ * Tests the error message rewriting logic.
+ */
+@CompileStatic
+class MessageRewriteTest {
+
+    private static List<String> INVOKED_FROM_IDE_ARGS =
+            Collections.singletonList("-P" + AndroidProject.PROPERTY_INVOKED_FROM_IDE + "=true")
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("flavored")
+            .withoutNdk()
+            .captureStdOut(true)
+            .captureStdErr(true)
+            .create()
+
+    @BeforeClass
+    public static void assemble() {
+        project.execute('assembleDebug')
+    }
+
+    @Test
+    public void "invalid layout file"() {
+        TemporaryProjectModification.doTest(project) {
+            it.replaceInFile("src/main/res/layout/main.xml", "</LinearLayout>", "");
+            project.getStderr().reset()
+            project.executeExpectingFailure(INVOKED_FROM_IDE_ARGS, 'assembleF1Debug')
+            String err = project.getStderr().toString()
+            assertThat(err).contains("src/main/res/layout/main.xml")
+        }
+
+        project.execute('assembleDebug')
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy
index 47de17e..03443a2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy
@@ -23,6 +23,7 @@
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.ProductFlavorContainer
 import com.android.builder.model.SourceProviderContainer
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -37,6 +38,7 @@
 /**
  * Assemble tests for migrated.
  */
+@CompileStatic
 class MigratedTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -100,6 +102,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyLibAndAppWithJavaResTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyLibAndAppWithJavaResTest.groovy
new file mode 100644
index 0000000..669f194
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyLibAndAppWithJavaResTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 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.integration.application;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.truth.ApkSubject;
+import com.android.builder.model.AndroidProject;
+
+import org.junit.AfterClass
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import groovy.transform.CompileStatic
+
+import static org.junit.Assert.assertNotNull;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+
+/**
+ * Tests that ensure that java resources files accessed with a relative or absolute path are
+ * packaged correctly.
+ */
+@CompileStatic
+public class MinifyLibAndAppWithJavaResTest {
+
+    static Map<String, AndroidProject> models
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("minifyLibWithJavaRes")
+            .create()
+
+    @BeforeClass
+    static void setUp() {
+        models = project.executeAndReturnMultiModel("clean", "assemble")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+        models = null
+    }
+
+    @Test
+    void testDebugPackaging() {
+        File debugApk = project.getSubproject("app").getApk("debug")
+        assertNotNull(debugApk)
+        ApkSubject apkSubject = assertThatApk(debugApk);
+        // check that resources with relative path lookup code have a matching obfuscated package
+        // name.
+        apkSubject.contains("com/android/tests/util/resources.properties")
+        apkSubject.contains("com/android/tests/other/resources.properties")
+        // check that resources with absolute path lookup remain in the original package name.
+        apkSubject.contains("com/android/tests/util/another.properties")
+        apkSubject.contains("com/android/tests/other/some.xml")
+        apkSubject.contains("com/android/tests/other/another.properties")
+    }
+
+    @Test
+    void testReleasePackaging() {
+        Assume.assumeFalse("Ignore until Jack fixed proguard confusion", GradleTestProject.USE_JACK)
+        File releaseApk = project.getSubproject("app").getApk("release")
+        assertNotNull(releaseApk)
+        ApkSubject apkSubject = assertThatApk(releaseApk);
+        // check that resources with relative path lookup code have a matching obfuscated package
+        // name.
+        apkSubject.contains("com/android/tests/b/resources.properties")
+        apkSubject.contains("com/android/tests/a/resources.properties")
+        // check that resources with absolute path lookup remain in the original package name.
+        apkSubject.contains("com/android/tests/util/another.properties")
+        apkSubject.contains("com/android/tests/other/some.xml")
+        apkSubject.contains("com/android/tests/other/another.properties")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy
index 5fc3eb0..3c0a87a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy
@@ -65,7 +65,7 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 
     @Test
@@ -97,7 +97,6 @@
                 .findAll { !it.startsWith("org/hamcrest") }
 
         assertThat(testClassFiles).containsExactly(
-                "LICENSE.txt", // Comes from hamcrest, is not packaged.
                 "com/android/tests/basic/MainTest.class",
                 "com/android/tests/basic/UnusedTestClass.class",
                 "com/android/tests/basic/UsedTestClass.class",
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy
index 0b97a2e..cc983d5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy
@@ -15,9 +15,9 @@
  */
 
 package com.android.build.gradle.integration.application
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.builder.model.AndroidProject
 import com.android.builder.model.SyncIssue
 import groovy.transform.CompileStatic
 import org.junit.Before
@@ -55,7 +55,8 @@
     compile 'foo:bar:1.2.3'
 }
 """
-        assertThat(project.getSingleModelIgnoringSyncIssues()).issues().hasSingleIssue(
+        AndroidProject model = project.getSingleModelIgnoringSyncIssues()
+        assertThat(model).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_UNRESOLVED_DEPENDENCY,
                 'foo:bar:1.2.3')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy
index 8205a57..6528a3f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy
@@ -19,6 +19,7 @@
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.google.common.io.Files
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -31,6 +32,7 @@
 /**
  * Assemble tests for multiDex.
  */
+@CompileStatic
 class MultiDexTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -39,6 +41,7 @@
 
     @BeforeClass
     static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.execute("clean", "assembleDebug")
     }
 
@@ -71,6 +74,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy
index 5f1ed2a..d799126 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy
@@ -22,6 +22,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.JavaLibrary
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -32,6 +33,7 @@
 /**
  * Assemble tests for multiproject.
  */
+@CompileStatic
 class MultiProjectTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy
index de9d292..392e6c7 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for multires.
  */
+@CompileStatic
 class MultiresTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy
index b47a2fa..6c696eb 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy
@@ -32,10 +32,12 @@
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("ndkJniPureSplitLib")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
     static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.execute("clean", ":app:assembleDebug")
     }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy
index 1d4f49b..358b2c4 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy
@@ -17,17 +17,19 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.ClassRule
 import org.junit.Test
 
 import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static junit.framework.Assert.assertEquals
-import static junit.framework.Assert.assertNotSame
-import static junit.framework.Assert.assertTrue
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertNotSame
+import static org.junit.Assert.assertTrue
 
 /**
  * Integration test for the cruncherEnabled settings.
  */
+@CompileStatic
 class NoCruncherTest {
 
     @ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy
index 3cd4fa2..e19be55 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for noPreDex.
  */
+@CompileStatic
 class NoPreDexTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/OptionalLibraryTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/OptionalLibraryTest.groovy
new file mode 100644
index 0000000..6fb08be
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/OptionalLibraryTest.groovy
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 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.integration.application
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.SyncIssue
+import com.android.sdklib.IAndroidTarget
+import com.android.sdklib.SdkManager
+import com.android.utils.NullLogger
+import groovy.transform.CompileStatic
+import org.junit.After
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.SdkConstants.FN_FRAMEWORK_LIBRARY
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+/**
+ * Test for BuildConfig field declared in build type, flavors, and variant and how they
+ * override each other
+ */
+@CompileStatic
+class OptionalLibraryTest {
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldApp())
+            .create()
+
+    @After
+    void cleanUp() {
+        project = null
+    }
+
+    @Test
+    void "test unknown useLibrary trigger sync issue"() {
+        Assume.assumeNotNull("Next platform missing", System.getenv("ANDROID_NEXT_PLATFORM"));
+
+        project.getBuildFile() << """
+            apply plugin: 'com.android.application'
+
+            android {
+                compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+                buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+                useLibrary 'foo'
+
+            }
+            """.stripIndent()
+
+        AndroidProject project = project.getSingleModelIgnoringSyncIssues()
+
+        assertThat(project).hasSingleIssue(
+                SyncIssue.SEVERITY_ERROR,
+                SyncIssue.TYPE_OPTIONAL_LIB_NOT_FOUND,
+                'foo');
+    }
+
+    @Test
+    void "test using optional library"() {
+        Assume.assumeNotNull("Next platform missing", System.getenv("ANDROID_NEXT_PLATFORM"));
+
+        project.getBuildFile() << """
+            apply plugin: 'com.android.application'
+
+            android {
+                compileSdkVersion 23
+                buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+                useLibrary 'org.apache.http.legacy'
+
+            }
+            """.stripIndent()
+
+        AndroidProject project = project.getSingleModel()
+
+        // get the SDK folder
+        File sdkLocation = new File(System.getenv("ANDROID_HOME"))
+        SdkManager sdkManager = SdkManager.createManager(
+                sdkLocation.getAbsolutePath(),
+                new NullLogger())
+        IAndroidTarget target = sdkManager.getTargetFromHashString('android-23')
+
+        File targetLocation = new File(target.getLocation())
+
+        assertThat(project.getBootClasspath()).containsExactly(
+                new File(targetLocation, FN_FRAMEWORK_LIBRARY).getAbsolutePath(),
+                new File(targetLocation, "optional/org.apache.http.legacy.jar").getAbsolutePath())
+    }
+
+    @Test
+    void "test not using optional library"() {
+        Assume.assumeNotNull("Next platform missing", System.getenv("ANDROID_NEXT_PLATFORM"));
+
+        project.getBuildFile() << """
+            apply plugin: 'com.android.application'
+
+            android {
+                compileSdkVersion 23
+                buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+            }
+            """.stripIndent()
+
+        AndroidProject project = project.getSingleModel()
+
+        // get the SDK folder
+        File sdkLocation = new File(System.getenv("ANDROID_HOME"))
+        SdkManager sdkManager = SdkManager.createManager(
+                sdkLocation.getAbsolutePath(),
+                new NullLogger())
+        IAndroidTarget target = sdkManager.getTargetFromHashString('android-23')
+
+        File targetLocation = new File(target.getLocation())
+
+        assertThat(project.getBootClasspath()).containsExactly(
+                new File(targetLocation, FN_FRAMEWORK_LIBRARY).getAbsolutePath())
+    }
+
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy
index f462302..cfa0e28 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy
@@ -20,6 +20,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ImageHelper
 import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
 /**
  * Assemble tests for overlay1.
  */
+@CompileStatic
 class Overlay1Test {
 
     @ClassRule
@@ -63,7 +65,7 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy
index 33fcdb9..5ddb6d9 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy
@@ -20,6 +20,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ImageHelper
 import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
 /**
  * Assemble tests for overlay2.
  */
+@CompileStatic
 class Overlay2Test {
 
     @ClassRule
@@ -67,6 +69,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy
index 88d06c3..3206c65 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy
@@ -20,6 +20,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ImageHelper
 import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
 /**
  * Assemble tests for overlay3.
  */
+@CompileStatic
 class Overlay3Test {
 
     @ClassRule
@@ -52,7 +54,7 @@
         int RED = ImageHelper.RED
 
         File drawableOutput = project.file(
-                "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freebeta/debug/drawable")
+                "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freeBeta/debug/drawable")
 
         ImageHelper.checkImageColor(drawableOutput, "no_overlay.png", GREEN)
         ImageHelper.checkImageColor(drawableOutput, "debug_overlay.png", GREEN)
@@ -63,7 +65,7 @@
         ImageHelper.checkImageColor(drawableOutput, "free_normal_overlay.png", RED)
 
         drawableOutput = project.file(
-                "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freenormal/debug/drawable")
+                "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freeNormal/debug/drawable")
 
         ImageHelper.checkImageColor(drawableOutput, "no_overlay.png", GREEN)
         ImageHelper.checkImageColor(drawableOutput, "debug_overlay.png", GREEN)
@@ -74,7 +76,7 @@
         ImageHelper.checkImageColor(drawableOutput, "free_normal_overlay.png", GREEN)
 
         drawableOutput = project.file(
-                "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/paidbeta/debug/drawable")
+                "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/paidBeta/debug/drawable")
 
         ImageHelper.checkImageColor(drawableOutput, "no_overlay.png", GREEN)
         ImageHelper.checkImageColor(drawableOutput, "debug_overlay.png", GREEN)
@@ -93,6 +95,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy
index 07694a0..d07f43c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy
@@ -15,47 +15,198 @@
  */
 
 package com.android.build.gradle.integration.application
-import com.android.build.gradle.integration.common.category.DeviceTests
+
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import org.junit.AfterClass
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.google.common.io.Files
+import groovy.transform.CompileStatic
+import org.junit.Before
 import org.junit.BeforeClass
 import org.junit.ClassRule
+import org.junit.Rule
 import org.junit.Test
-import org.junit.experimental.categories.Category
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
 /**
  * Assemble tests for packagingOptions.
+ *
+ * Creates two jar files and test various packaging options.
  */
+@CompileStatic
 class PackagingOptionsTest {
+
+    // Projects to create jar files.
+    private static AndroidTestApp jarProject1 = new EmptyAndroidTestApp()
+    static {
+        jarProject1.addFile(new TestSourceFile("", "build.gradle", "apply plugin: 'java'"))
+        jarProject1.addFile(new TestSourceFile("src/main/resources", "conflict.txt", "foo"))
+    }
+    private static AndroidTestApp jarProject2 = new EmptyAndroidTestApp()
+    static {
+        jarProject2.addFile(new TestSourceFile("", "build.gradle", "apply plugin: 'java'"))
+        jarProject2.addFile(new TestSourceFile("src/main/resources", "conflict.txt", "foo"))
+        // add an extra file so that jar1 is different from jar2.
+        jarProject2.addFile(new TestSourceFile("src/main/resources", "dummy2.txt", "bar"))
+    }
+
     @ClassRule
-    static public GradleTestProject project = GradleTestProject.builder()
-            .fromTestProject("packagingOptions")
+    public static GradleTestProject jar1 = GradleTestProject.builder()
+            .fromTestApp(jarProject1)
+            .withName("jar1")
+            .create()
+    @ClassRule
+    public static GradleTestProject jar2 = GradleTestProject.builder()
+            .fromTestApp(jarProject2)
+            .withName("jar2")
             .create()
 
     @BeforeClass
-    static void setUp() {
+    static void createJars() {
+        jar1.execute("assemble")
+        jar2.execute("assemble")
+    }
+
+
+    // Main test project.
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldApp())
+            .create()
+
+    @Before
+    void setUp() {
+        Files.copy(jar1.file("build/libs/jar1.jar"), project.file("jar1.jar"))
+        Files.copy(jar2.file("build/libs/jar2.jar"), project.file("jar2.jar"))
+
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+}
+"""
+
+    }
+
+    @Test
+    void "check pickFirst"() {
+        project.getBuildFile() << """
+android {
+    packagingOptions {
+        pickFirst 'conflict.txt'
+    }
+}
+
+dependencies {
+    compile files('jar1.jar')
+    compile files('jar2.jar')
+}
+"""
         project.execute("clean", "assembleDebug")
-    }
-
-    @AfterClass
-    static void cleanUp() {
-        project = null
+        assertThatZip(project.getApk("debug")).contains("conflict.txt")
     }
 
     @Test
-    void lint() {
-        project.execute("lint")
+    void "check exclude"() {
+        project.getBuildFile() << """
+android {
+    packagingOptions {
+        exclude 'conflict.txt'
+    }
+}
+
+dependencies {
+    compile files('jar1.jar')
+    compile files('jar2.jar')
+}
+"""
+        project.execute("clean", "assembleDebug")
+        assertThatZip(project.getApk("debug")).doesNotContain("conflict.txt")
     }
 
     @Test
-    void "check packinging"() {
-        assertThatZip(project.getApk("debug")).contains("first_pick.txt")
+    void "check exclude on direct files"() {
+        project.getBuildFile() << """
+android {
+    packagingOptions {
+        exclude 'lib/x86/libconflict.so'
+        exclude 'conflict.txt'
+    }
+}
+"""
+        createFile('src/main/jniLibs/x86/libconflict.so')
+        createFile('src/main/resources/conflict.txt')
+        project.execute("clean", "assembleDebug")
+        assertThatZip(project.getApk("debug")).doesNotContain('lib/x86/libconflict.so')
+        assertThatZip(project.getApk("debug")).doesNotContain('conflict.txt')
     }
 
     @Test
-    @Category(DeviceTests.class)
-    void connectedCheck() {
-        project.execute("connectedCheck")
+    void "check merge on jar entries"() {
+        project.getBuildFile() << """
+android {
+    packagingOptions {
+        merge 'conflict.txt'
+    }
+}
+
+dependencies {
+    compile files('jar1.jar')
+    compile files('jar2.jar')
+}
+"""
+        project.execute("clean", "assembleDebug")
+
+        assertThatZip(project.getApk("debug")).containsFileWithContent("conflict.txt", "foofoo")
+    }
+
+    @Test
+    void "check merge on direct files"() {
+        project.getBuildFile() << """
+android {
+    packagingOptions {
+        // Doesn't make sense to merge native library, but it should work.
+        merge 'lib/x86/libconflict.so'
+    }
+}
+"""
+        createFile('src/main/jniLibs/x86/libconflict.so') << "foo"
+        createFile('src/debug/jniLibs/x86/libconflict.so') << "foo"
+        project.execute("clean", "assembleDebug")
+        assertThatZip(project.getApk("debug")).containsFileWithContent("lib/x86/libconflict.so", "foofoo")
+    }
+
+    @Test
+    void "check merge on a direct file and a jar entry"() {
+        project.getBuildFile() << """
+android {
+    packagingOptions {
+        merge 'conflict.txt'
+    }
+}
+
+dependencies {
+    compile files('jar1.jar')
+}
+"""
+        createFile('src/main/resources/conflict.txt') << "foo"
+        project.execute("clean", "assembleDebug")
+        assertThatZip(project.getApk("debug")).containsFileWithContent("conflict.txt", "foofoo")
+    }
+
+    /**
+     * Create a new empty file including its directories.
+     */
+    private File createFile(String filename) {
+        File newFile = project.file(filename)
+        newFile.getParentFile().mkdirs()
+        newFile.createNewFile()
+        assertThat(newFile).exists()
+        return newFile
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy
index ffeac1f..caceedf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy
@@ -20,6 +20,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
 import com.google.common.collect.ImmutableList
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,9 +30,8 @@
 /**
  * Assemble tests for parentLibTest
  */
+@CompileStatic
 public class ParentLibsTest {
-    static AndroidProject model
-
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("parentLibsTest")
@@ -39,14 +39,13 @@
 
     @BeforeClass
     static void setUp() {
-        model = project.execute(ImmutableList.of("-p", "app"),
+        project.execute(ImmutableList.of("-p", "app"),
                 "clean", "assembleDebug")
     }
 
     @AfterClass
     static void cleanUp() {
         project = null
-        model = null
     }
 
     @Test
@@ -57,6 +56,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute(ImmutableList.of("-p", "app"), "connectedCheck")
+        project.executeConnectedCheck(ImmutableList.of("-p", "app"))
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy
index a1ec339..4c0263f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for pkgOverride.
  */
+@CompileStatic
 class PkgOverrideTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy
index c97a466..4e266ac 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy
@@ -23,6 +23,7 @@
 import com.android.builder.model.AndroidArtifactOutput
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.Assert
 import org.junit.BeforeClass
@@ -35,6 +36,7 @@
 /**
  * Test for unresolved placeholders in libraries.
  */
+@CompileStatic
 class PlaceholderInLibsTest {
     static Map<String, AndroidProject> models
 
@@ -77,7 +79,7 @@
         assertEquals(1, output.getOutputs().size())
 
         List<String> apkBadging =
-                ApkHelper.getApkBadging(output.getOutputs().getAt(0).getOutputFile());
+                ApkHelper.getApkBadging(output.getOutputs().iterator().next().getOutputFile());
 
         for (String line : apkBadging) {
             if (line.contains("uses-permission: name=" +
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy
index 8f1f5ba..1bdf40d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for privateResources.
  */
+@CompileStatic
 class PrivateResourceTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -48,6 +50,8 @@
         String expected = """\
 string mylib_app_name
 string mylib_public_string
+string shared_name
+id shared_name
 """
         assertThatZip(project.getSubproject('mylibrary').getAar("release")).containsFileWithContent('public.txt', expected);
         assertThatZip(project.getSubproject('mylibrary').getAar("debug")).containsFileWithContent('public.txt', expected);
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy
index 7c04f7b..1916c2f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy
@@ -35,6 +35,7 @@
 
     @BeforeClass
     static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.execute("clean", "assembleDebug")
     }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy
index f03df6f..2340faf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy
@@ -21,6 +21,7 @@
 import com.android.builder.model.AndroidArtifactOutput
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -33,6 +34,7 @@
 /**
  * Assemble tests for renamedApk.
  */
+@CompileStatic
 class RenamedApkTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy
index ae7f452..5145c9a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for renderscriptMultiSrc.
  */
+@CompileStatic
 class RenderscriptMultiSrcTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy
index eadf409..4ae1e5f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for renderscript.
  */
+@CompileStatic
 class RenderscriptTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy
index c292651..06272b9 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy
@@ -16,12 +16,14 @@
 
 package com.android.build.gradle.integration.application
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
 /**
  * Integration test for uploadAchives with multiple projects.
  */
+@CompileStatic
 class RepoTest {
 
     @ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy
index 0cf6f42..155f81e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy
@@ -28,13 +28,14 @@
 import com.google.common.base.Charsets
 import com.google.common.collect.Maps
 import com.google.common.io.Files
-import junit.framework.Assert
+import groovy.transform.CompileStatic
+import org.junit.Assert
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 
-import static junit.framework.Assert.assertEquals
+import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 
@@ -42,6 +43,7 @@
  * Test for Res Values declared in build type, flavors, and variant and how they
  * override each other
  */
+@CompileStatic
 class ResValueTest {
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
@@ -117,13 +119,13 @@
     <!-- Automatically generated file. DO NOT MODIFY -->
 
     <!-- Values from the variant -->
-    <string name="VALUE_VARIANT">1000</string>
+    <string name="VALUE_VARIANT" translatable="false">1000</string>
     <!-- Values from build type: debug -->
-    <string name="VALUE_DEBUG">100</string>
+    <string name="VALUE_DEBUG" translatable="false">100</string>
     <!-- Values from product flavor: flavor1 -->
-    <string name="VALUE_FLAVOR">10</string>
+    <string name="VALUE_FLAVOR" translatable="false">10</string>
     <!-- Values from default config. -->
-    <string name="VALUE_DEFAULT">1</string>
+    <string name="VALUE_DEFAULT" translatable="false">1</string>
 
 </resources>
 """
@@ -149,13 +151,13 @@
     <!-- Automatically generated file. DO NOT MODIFY -->
 
     <!-- Values from the variant -->
-    <string name="VALUE_VARIANT">1000</string>
+    <string name="VALUE_VARIANT" translatable="false">1000</string>
     <!-- Values from build type: debug -->
-    <string name="VALUE_DEBUG">100</string>
+    <string name="VALUE_DEBUG" translatable="false">100</string>
     <!-- Values from product flavor: flavor2 -->
-    <string name="VALUE_FLAVOR">20</string>
+    <string name="VALUE_FLAVOR" translatable="false">20</string>
     <!-- Values from default config. -->
-    <string name="VALUE_DEFAULT">1</string>
+    <string name="VALUE_DEFAULT" translatable="false">1</string>
 
 </resources>
 """
@@ -181,11 +183,11 @@
     <!-- Automatically generated file. DO NOT MODIFY -->
 
     <!-- Values from product flavor: flavor1 -->
-    <string name="VALUE_DEBUG">10</string>
-    <string name="VALUE_FLAVOR">10</string>
-    <string name="VALUE_VARIANT">10</string>
+    <string name="VALUE_DEBUG" translatable="false">10</string>
+    <string name="VALUE_FLAVOR" translatable="false">10</string>
+    <string name="VALUE_VARIANT" translatable="false">10</string>
     <!-- Values from default config. -->
-    <string name="VALUE_DEFAULT">1</string>
+    <string name="VALUE_DEFAULT" translatable="false">1</string>
 
 </resources>
 """
@@ -211,11 +213,11 @@
     <!-- Automatically generated file. DO NOT MODIFY -->
 
     <!-- Values from product flavor: flavor2 -->
-    <string name="VALUE_DEBUG">20</string>
-    <string name="VALUE_FLAVOR">20</string>
-    <string name="VALUE_VARIANT">20</string>
+    <string name="VALUE_DEBUG" translatable="false">20</string>
+    <string name="VALUE_FLAVOR" translatable="false">20</string>
+    <string name="VALUE_VARIANT" translatable="false">20</string>
     <!-- Values from default config. -->
-    <string name="VALUE_DEFAULT">1</string>
+    <string name="VALUE_DEFAULT" translatable="false">1</string>
 
 </resources>
 """
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy
index e672f3d..917087a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy
@@ -21,6 +21,7 @@
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
 import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -33,6 +34,7 @@
 /**
  * Test resValue for string type is treated as String.
  */
+@CompileStatic
 class ResValueTypeTest {
     static AndroidTestApp app = new HelloWorldApp()
     static {
@@ -121,7 +123,7 @@
 
     <plurals name="resPlurals">s</plurals>
 
-    <string name="resString">00</string>
+    <string name="resString" translatable="false">00</string>
 
     <style name="resStyle">foo</style>
 
@@ -133,6 +135,7 @@
     @Test
     @Category(DeviceTests.class)
     void "check resValue is treated as string"() {
-        project.execute("clean", "connectedAndroidTest")
+        project.execute("clean")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy
index cf514a5..7b0f1d7 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Integration test for extracting RS enabled annotations.
  */
+@CompileStatic
 class RsEnabledAnnotationTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy
index 82f1881..01b3d5f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy
@@ -24,6 +24,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.JavaLibrary
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -36,10 +37,12 @@
 /**
  * Assemble tests for rsSupportMode.
  */
+@CompileStatic
 class RsSupportModeTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("rsSupportMode")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
     static AndroidProject model
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy
index 818515a..93f22bd 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy
@@ -21,6 +21,7 @@
 import com.google.common.base.Joiner
 import com.google.common.collect.Lists
 import com.google.common.io.ByteStreams
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -40,6 +41,7 @@
 /**
  * Assemble tests for shrink.
  */
+@CompileStatic
 class ShrinkTest {
 
     @ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy
index 1c7dc4f..16b7af7 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy
@@ -20,6 +20,7 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.google.common.collect.ImmutableList
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
@@ -33,6 +34,7 @@
 /**
  * Integration test with signing overrider.
  */
+@CompileStatic
 class SigningConfigTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy
index e5571d5..9b882bf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy
@@ -22,6 +22,7 @@
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Dependencies
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -35,6 +36,7 @@
 /**
  * Assemble tests for testWithDep that loads the model but doesn't build.
  */
+@CompileStatic
 class TestWithDepTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy
index 83cd61f..39463ce 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.application
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
@@ -24,6 +25,7 @@
 /**
  * Assemble tests for 3rdPartyTests.
  */
+@CompileStatic
 class ThirdPartyTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy
index 963c013..ba713a2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy
@@ -21,6 +21,7 @@
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Dependencies
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -33,6 +34,7 @@
 /**
  * Assemble tests for tictactoe.
  */
+@CompileStatic
 class TictactoeTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy
index 4d24560..a15808a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy
@@ -16,11 +16,10 @@
 
 package com.android.build.gradle.integration.application
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.ArtifactMetaData
 import com.android.builder.model.JavaArtifact
-import org.junit.Before
+import groovy.transform.CompileStatic
 import org.junit.Rule
 import org.junit.Test
 
@@ -29,28 +28,17 @@
 /**
  * Tests for the unit-tests related parts of the builder model.
  */
+@CompileStatic
 class UnitTestingModelTest {
 
     @Rule
     public GradleTestProject project = GradleTestProject.builder()
-            .fromTestApp(new HelloWorldApp())
+            .fromTestProject("unitTestingComplexProject")
             .create();
 
-    @Before
-    public void setUp() {
-        project.buildFile << """
-apply plugin: 'com.android.application'
-
-android {
-    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
-}
-"""
-    }
-
     @Test
     public void "Unit testing artifacts are included in the model"() {
-        AndroidProject model = project.singleModel
+        AndroidProject model = project.allModels[":app"]
 
         assertThat(model.extraArtifacts*.name).containsExactly(
                 AndroidProject.ARTIFACT_ANDROID_TEST,
@@ -95,13 +83,12 @@
 
     @Test
     public void flavors() throws Exception {
-        project.buildFile << """
+        project.getSubproject("app").buildFile << """
 android {
     productFlavors { paid; free }
 }
 """
-
-        AndroidProject model = project.singleModel
+        AndroidProject model = project.allModels[":app"]
 
         assertThat(model.productFlavors).hasSize(2)
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy
index 6ff0de7..9b39669 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy
@@ -16,14 +16,17 @@
 
 package com.android.build.gradle.integration.application
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.google.common.base.Charsets
+import com.android.utils.FileUtils
 import com.google.common.io.Files
 import groovy.transform.CompileStatic
+import org.junit.BeforeClass
 import org.junit.Rule
 import org.junit.Test
 
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertWithMessage
+import static com.google.common.base.Charsets.UTF_8
 /**
  * Tests for the PNG generation feature.
  */
@@ -34,23 +37,79 @@
             .fromTestProject("vectorDrawables")
             .create()
 
+    @BeforeClass
+    public static void checkBuildTools() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
+    }
+
     @Test
     public void "vector file is moved and PNGs are generated"() throws Exception {
         project.execute("clean", "assembleDebug")
         File apk = project.getApk("debug")
         assertThatApk(apk).containsResource("drawable/icon.png")
         assertThatApk(apk).doesNotContainResource("drawable/heart.xml")
-        assertThatApk(apk).containsResource("drawable-v21/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-v21/heart.xml")
+        assertThatApk(apk).containsResource("drawable-hdpi-v21/heart.xml")
         assertThatApk(apk).containsResource("drawable-hdpi-v4/heart.png")
         assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart.png")
+        assertThatApk(apk).containsResource("drawable-xhdpi-v21/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v22/no_need.png")
+        assertThatApk(apk).containsResource("drawable-v22/no_need.xml")
+
+        // Check HDPI. Test project contains the hdpi png, it should be used instead of the
+        // generated one.
+        File originalPng = new File(
+                project.testDir,
+                "src/main/res/drawable-hdpi/special_heart.png")
+        File generatedPng = new File(
+                project.testDir,
+                "build/generated/res/pngs/debug/drawable-hdpi/special_heart.png")
+        File pngToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable-hdpi/special_heart.png")
+
+        assertThat(generatedPng).doesNotExist()
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isEqualTo(FileUtils.sha1(originalPng))
+
+        // Check XHDPI.
+        generatedPng = new File(
+                project.testDir,
+                "build/generated/res/pngs/debug/drawable-xhdpi/special_heart.png")
+        pngToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable-xhdpi/special_heart.png")
+
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isEqualTo(FileUtils.sha1(generatedPng))
+
+        // Check interactions with other qualifiers.
+        assertThatApk(apk).containsResource("drawable-fr-hdpi-v21/french_heart.xml")
+        assertThatApk(apk).containsResource("drawable-fr-xhdpi-v21/french_heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/french_heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-fr/french_heart.xml")
+        assertThatApk(apk).containsResource("drawable-fr-hdpi-v4/french_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi/french_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/french_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-fr/french_heart.png")
+
+        assertThatApk(apk).containsResource("drawable-hdpi-v21/modern_heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-v16/modern_heart.xml")
+        assertThatApk(apk).containsResource("drawable-hdpi-v16/modern_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-v16/modern_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/modern_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi/modern_heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/modern_heart.png")
     }
 
     @Test
-    public void "incremental build: add file"() throws Exception {
+    public void "incremental build: add xml"() throws Exception {
         project.execute("assembleDebug")
 
         File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
-        File heartXmlCopy = new File(project.testDir, "src/main/res/drawable/heart2.xml")
+        File heartXmlCopy = new File(project.testDir, "src/main/res/drawable/heart_copy.xml")
         Files.copy(heartXml, heartXmlCopy)
 
         project.execute("assembleDebug")
@@ -58,14 +117,15 @@
 
         File apk = project.getApk("debug")
         assertThatApk(apk).containsResource("drawable/icon.png")
-        assertThatApk(apk).doesNotContainResource("drawable/heart2.xml")
-        assertThatApk(apk).containsResource("drawable-v21/heart2.xml")
-        assertThatApk(apk).containsResource("drawable-hdpi-v4/heart2.png")
-        assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart2.png")
+        assertThatApk(apk).doesNotContainResource("drawable/heart_copy.xml")
+        assertThatApk(apk).containsResource("drawable-hdpi-v21/heart_copy.xml")
+        assertThatApk(apk).containsResource("drawable-xhdpi-v21/heart_copy.xml")
+        assertThatApk(apk).containsResource("drawable-hdpi-v4/heart_copy.png")
+        assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart_copy.png")
     }
 
     @Test
-    public void "incremental build: delete file"() throws Exception {
+    public void "incremental build: delete xml"() throws Exception {
         project.execute("assembleDebug")
 
         File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
@@ -76,39 +136,181 @@
 
         File apk = project.getApk("debug")
         assertThatApk(apk).containsResource("drawable/icon.png")
-        assertThatApk(apk).doesNotContainResource("drawable/heart2.xml")
-        assertThatApk(apk).doesNotContainResource("drawable-v21/heart2.xml")
-        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart2.png")
-        assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart2.png")
+        assertThatApk(apk).doesNotContainResource("drawable/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-xhdpi/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart.png")
     }
 
     @Test
-    public void "incremental build: modify file"() throws Exception {
+    public void "incremental build: delete png"() throws Exception {
         project.execute("assembleDebug")
 
-        File preprocessedHeartXml = new File(project.testDir, "build/intermediates/res/preprocessed/debug/drawable-hdpi/heart.png")
-        File preprocessedIconPng = new File(project.testDir, "build/intermediates/res/preprocessed/debug/drawable/icon.png")
-        long heartXmlModified = preprocessedHeartXml.lastModified()
-        long iconModified = preprocessedIconPng.lastModified()
+        File generatedPng = new File(
+                project.testDir,
+                "build/generated/res/pngs/debug/drawable-hdpi/special_heart.png")
+        File originalPng = new File(
+                project.testDir,
+                "src/main/res/drawable-hdpi/special_heart.png")
+        File pngToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable-hdpi/special_heart.png")
 
-        File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
-        String content = Files.toString(heartXml, Charsets.UTF_8)
-        // Change the heart to blue.
-        Files.write(content.replace("ff0000", "0000ff"), heartXml, Charsets.UTF_8)
+        assertThat(generatedPng).doesNotExist()
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isEqualTo(FileUtils.sha1(originalPng))
+
+        originalPng.delete()
 
         project.execute("assembleDebug")
         checkIncrementalBuild()
 
-        assertThat(preprocessedIconPng.lastModified()).isEqualTo(iconModified)
-        assertThat(preprocessedHeartXml.lastModified()).isNotEqualTo(heartXmlModified)
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isEqualTo(FileUtils.sha1(generatedPng))
+    }
+
+    @Test
+    public void "incremental build: add png"() throws Exception {
+        project.execute("assembleDebug")
+
+        File generatedPng = new File(
+                project.testDir,
+                "build/generated/res/pngs/debug/drawable-xhdpi/special_heart.png")
+        File pngToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable-xhdpi/special_heart.png")
+
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isEqualTo(FileUtils.sha1(generatedPng))
+
+        // Create a PNG file for XHDPI. It should be used instead of the generated one.
+        File hdpiPng = new File(project.testDir, "src/main/res/drawable-hdpi/special_heart.png")
+        File xhdpiPng = new File(project.testDir, "src/main/res/drawable-xhdpi/special_heart.png")
+        Files.createParentDirs(xhdpiPng)
+        Files.copy(hdpiPng, xhdpiPng)
+
+        project.execute("assembleDebug")
+        checkIncrementalBuild()
+
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isNotEqualTo(FileUtils.sha1(generatedPng))
+
+        assertWithMessage("Wrong file used.")
+                .that(FileUtils.sha1(pngToUse))
+                .isEqualTo(FileUtils.sha1(xhdpiPng))
+    }
+
+    @Test
+    public void "incremental build: modify xml"() throws Exception {
+        project.execute("assembleDebug")
+
+        File heartPngToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable-hdpi/heart.png")
+        File iconPngToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable/icon.png")
+
+        String oldHashCode = FileUtils.sha1(heartPngToUse)
+        long heartPngModified = heartPngToUse.lastModified()
+        long iconPngModified = iconPngToUse.lastModified()
+
+        File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
+        String content = Files.toString(heartXml, UTF_8)
+        // Change the heart to blue.
+        Files.write(content.replace("ff0000", "0000ff"), heartXml, UTF_8)
+
+        project.execute("assembleDebug")
+        checkIncrementalBuild()
+
+        assertThat(iconPngToUse.lastModified()).isEqualTo(iconPngModified)
+        assertThat(heartPngToUse.lastModified()).isNotEqualTo(heartPngModified)
+        assertWithMessage("XML file change not reflected in PNG.")
+                .that(FileUtils.sha1(heartPngToUse))
+                .isNotEqualTo(oldHashCode)
+    }
+
+    @Test
+    public void "incremental build: replace vector drawable with bitmap alias"() throws Exception {
+        project.execute("assembleDebug")
+
+        File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
+        Files.write(
+                "<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\" " +
+                        "android:src=\"@drawable/icon\" />",
+                heartXml,
+                UTF_8)
+
+        project.execute("assembleDebug")
+        checkIncrementalBuild()
+
+        File apk = project.getApk("debug")
+        assertThatApk(apk).containsResource("drawable/icon.png")
+        assertThatApk(apk).containsResource("drawable/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-xhdpi/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart.png")
+
+        File heartXmlToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable/heart.xml")
+
+        // They won't be equal, because of the source marker added in the XML.
+        assertThat(Files.toString(heartXmlToUse, UTF_8)).contains(Files.toString(heartXml, UTF_8))
+    }
+
+    @Test
+    public void "incremental build: replace bitmap alias with vector drawable"() throws Exception {
+        File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
+
+        String vectorDrawable = Files.toString(heartXml, UTF_8)
+
+        Files.write(
+                "<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\" " +
+                "android:src=\"@drawable/icon\" />",
+                heartXml,
+                UTF_8)
+
+        project.execute("clean", "assembleDebug")
+
+        File apk = project.getApk("debug")
+        assertThatApk(apk).containsResource("drawable/icon.png")
+        assertThatApk(apk).containsResource("drawable/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-xhdpi/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart.png")
+        assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart.png")
+
+        File heartXmlToUse = new File(
+                project.testDir,
+                "build/intermediates/res/merged/debug/drawable/heart.xml")
+
+        // They won't be equal, because of the source marker added in the XML.
+        assertThat(Files.toString(heartXmlToUse, UTF_8)).contains(Files.toString(heartXml, UTF_8))
+
+        Files.write(vectorDrawable, heartXml, UTF_8)
+        project.execute("assembleDebug")
+        checkIncrementalBuild()
+
+        assertThatApk(apk).doesNotContainResource("drawable/heart.xml")
+        assertThatApk(apk).doesNotContainResource("drawable-v21/heart.xml")
+        assertThatApk(apk).containsResource("drawable-hdpi-v21/heart.xml")
+        assertThatApk(apk).containsResource("drawable-hdpi-v4/heart.png")
+        assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart.png")
+        assertThatApk(apk).containsResource("drawable-xhdpi-v21/heart.xml")
     }
 
     private void checkIncrementalBuild() {
-        File incrementalFolder = new File(
-                project.testDir,
-                "build/intermediates/incremental/preprocessResourcesTask/debug")
-        // state.json is always left behind, this is to make sure incrementalFolder is correct.
-        assertThat(new File(incrementalFolder, "state.json").exists()).isTrue()
-        assertThat(new File(incrementalFolder, "build_was_incremental").exists()).isTrue()
+        // Do nothing for now, the incremental marker was removed.
+        // TODO: remove the method or re-enable incremental markers.
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy
index dbdb73f..02d1d7d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy
@@ -18,18 +18,20 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.google.common.base.Throwables
+import groovy.transform.CompileStatic
 import org.gradle.tooling.BuildException
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 
-import static junit.framework.Assert.fail
+import static org.junit.Assert.fail
 
 /**
  * Debug builds with a wearApp with applicationId that does not match that of the main application
  * should fail.
  */
+@CompileStatic
 class WearWithCustomApplicationIdTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/category/SmokeTests.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/category/SmokeTests.java
new file mode 100644
index 0000000..ce2e120
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/category/SmokeTests.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 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.integration.common.category;
+
+/**
+ * JUnit category used to label good candidates for smoke testing.
+ */
+public interface SmokeTests {
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java
index f7e4f8c..00b4842 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java
@@ -17,13 +17,9 @@
 package com.android.build.gradle.integration.common.fixture;
 
 import com.android.builder.model.AndroidProject;
-import com.google.common.collect.Maps;
 
 import org.gradle.tooling.BuildAction;
 import org.gradle.tooling.BuildController;
-import org.gradle.tooling.model.DomainObjectSet;
-import org.gradle.tooling.model.gradle.BasicGradleProject;
-import org.gradle.tooling.model.gradle.GradleBuild;
 
 import java.util.Collections;
 import java.util.Map;
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java
index 912bdbd..d957ce3 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java
@@ -29,15 +29,18 @@
 import com.android.build.gradle.integration.common.utils.FileHelper;
 import com.android.build.gradle.integration.common.utils.JacocoAgent;
 import com.android.build.gradle.integration.common.utils.SdkHelper;
+import com.android.builder.core.BuilderConstants;
 import com.android.builder.model.AndroidProject;
 import com.android.builder.model.SyncIssue;
-import com.android.builder.Version;
+import com.android.builder.model.Version;
 import com.android.io.StreamException;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
+import com.android.sdklib.repository.FullRevision;
 import com.google.common.base.Charsets;
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.io.Files;
@@ -45,11 +48,15 @@
 import org.gradle.tooling.BuildAction;
 import org.gradle.tooling.BuildActionExecuter;
 import org.gradle.tooling.BuildLauncher;
+import org.gradle.tooling.GradleConnectionException;
 import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.LongRunningOperation;
 import org.gradle.tooling.ProjectConnection;
+import org.gradle.tooling.ResultHandler;
 import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
 import org.gradle.tooling.model.GradleProject;
 import org.gradle.tooling.model.GradleTask;
+import org.junit.Assume;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -59,11 +66,14 @@
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.nio.charset.Charset;
 import java.security.CodeSource;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -80,15 +90,29 @@
  */
 public class GradleTestProject implements TestRule {
 
+    public static final File TEST_RES_DIR = new File("src/test/resources");
+
     public static final int DEFAULT_COMPILE_SDK_VERSION = 21;
     public static final String DEFAULT_BUILD_TOOL_VERSION;
+    public static final String REMOTE_TEST_PROVIDER = System.getenv().get("REMOTE_TEST_PROVIDER");
+
+    public static final String DEVICE_PROVIDER_NAME = REMOTE_TEST_PROVIDER != null ?
+            REMOTE_TEST_PROVIDER : BuilderConstants.CONNECTED;
 
     public static final String GRADLE_TEST_VERSION = "2.2.1";
-    public static final String GRADLE_EXP_TEST_VERSION = "2.4-20150322230018+0000";
+    public static final String GRADLE_EXP_TEST_VERSION = "2.5";
 
     public static final String ANDROID_GRADLE_PLUGIN_VERSION;
 
     public static final String CUSTOM_JACK;
+    public static final boolean USE_JACK;
+
+    private static final String RECORD_BENCHMARK_NAME = "com.android.benchmark.name";
+    private static final String RECORD_BENCHMARK_MODE = "com.android.benchmark.mode";
+
+    public enum BenchmarkMode {
+        EVALUATION, SYNC, BUILD_FULL, BUILD_INC_JAVA, BUILD_INC_RES_EDIT, BUILD_INC_RES_ADD
+    }
 
     static {
         String envBuildToolVersion = System.getenv("CUSTOM_BUILDTOOLS");
@@ -99,6 +123,7 @@
                 : Version.ANDROID_GRADLE_PLUGIN_VERSION;
         String envJack = System.getenv().get("CUSTOM_JACK");
         CUSTOM_JACK = !Strings.isNullOrEmpty(envJack) ? envJack : "false";
+        USE_JACK = Boolean.parseBoolean(CUSTOM_JACK);
     }
 
     private static final String COMMON_HEADER = "commonHeader.gradle";
@@ -118,24 +143,43 @@
         @Nullable
         private TestProject testProject = null;
 
+        @Nullable
+        File sdkDir = SdkHelper.findSdkDir();
+        @Nullable
+        File ndkDir = findNdkDir();
         boolean captureStdOut = false;
         boolean captureStdErr = false;
         boolean experimentalMode = false;
+        @Nullable
+        private String targetGradleVersion;
 
+        boolean useJack = false;
+        boolean useMinify = false;
+        @NonNull
+        private List<String> gradleProperties = Lists.newArrayList();
         @Nullable
         private String heapSize;
 
         /**
          * Create a GradleTestProject.
          */
-        public GradleTestProject create()  {
+        public GradleTestProject create() {
+            if (targetGradleVersion == null) {
+                targetGradleVersion =
+                        experimentalMode ? GRADLE_EXP_TEST_VERSION : GRADLE_TEST_VERSION;
+            }
             return new GradleTestProject(
                     name,
                     testProject,
                     experimentalMode,
-                    experimentalMode ? GRADLE_EXP_TEST_VERSION : GRADLE_TEST_VERSION,
+                    useMinify,
+                    useJack,
+                    targetGradleVersion,
                     captureStdOut,
                     captureStdErr,
+                    sdkDir,
+                    ndkDir,
+                    gradleProperties,
                     heapSize);
         }
 
@@ -159,12 +203,42 @@
             return this;
         }
 
+        /**
+         * Use experimental plugin for the test project.
+         */
         public Builder forExpermimentalPlugin(boolean mode) {
             this.experimentalMode = mode;
             return this;
         }
 
         /**
+         * Use the gradle version for experimental plugin, but the test project do not necessarily
+         * have to use experimental plugin.
+         */
+        public Builder useExperimentalGradleVersion(boolean mode) {
+            if (mode) {
+                targetGradleVersion = GRADLE_EXP_TEST_VERSION;
+            }
+            return this;
+        }
+
+        /**
+         * Use the gradle version specified, e.g. "2.4".
+         */
+        public Builder useGradleVersion(String targetGradleVersion) {
+            this.targetGradleVersion = targetGradleVersion;
+            return this;
+        }
+
+        /**
+         * Create a project without setting ndk.dir in local.properties.
+         */
+        public Builder withoutNdk() {
+            this.ndkDir = null;
+            return this;
+        }
+
+        /**
          * Create GradleTestProject from a TestProject.
          */
         public Builder fromTestApp(@NonNull TestProject testProject) {
@@ -184,6 +258,29 @@
         }
 
         /**
+         * Create GradleTestProject from an existing test project.
+         */
+        public Builder fromExternalProject(@NonNull String project) throws IOException {
+            AndroidTestApp app = new EmptyTestApp();
+            name = project;
+            // compute the root folder of the checkout, based on test-projects.
+            File parentDir = TEST_PROJECT_DIR.getCanonicalFile().getParentFile().getParentFile()
+                    .getParentFile().getParentFile().getParentFile();
+            parentDir = new File(parentDir, "external");
+            File projectDir = new File(parentDir, project);
+            addAllFiles(app, projectDir);
+            return fromTestApp(app);
+        }
+
+        /**
+         * Add gradle properties.
+         */
+        public Builder addGradleProperties(@NonNull String property) {
+            gradleProperties.add(property);
+            return this;
+        }
+
+        /**
          * Sets the test heap size requirement. Example values : 1024m, 2048m...
          *
          * @param heapSize the heap size in a format understood by the -Xmx JVM parameter
@@ -194,6 +291,16 @@
             return this;
         }
 
+        public Builder withJack(boolean useJack) {
+            this.useJack = useJack;
+            return this;
+        }
+
+        public Builder withMinify(boolean useMinify) {
+            this.useMinify = useMinify;
+            return this;
+        }
+
         private static class EmptyTestApp extends AbstractAndroidTestApp {
             @Override
             public boolean containsFullBuildScript() {
@@ -202,28 +309,27 @@
         }
     }
 
-    private String name;
-
-    private File outDir;
-
+    private final String name;
+    private final File outDir;
     private File testDir;
-
     private File sourceDir;
-
     private File buildFile;
-
-    private File ndkDir;
-
-    private File sdkDir;
+    private final File ndkDir;
+    private final File sdkDir;
 
     private final ByteArrayOutputStream stdout;
     private final ByteArrayOutputStream stderr;
 
-    @Nullable
-    private TestProject testProject;
+    private final Collection<String> gradleProperties;
 
-    private boolean experimentalMode;
-    private String targetGradleVersion;
+    @Nullable
+    private final TestProject testProject;
+
+    private final boolean experimentalMode;
+    private final String targetGradleVersion;
+
+    private final boolean useJack;
+    private final boolean minifyEnabled;
 
     @Nullable
     private String heapSize;
@@ -232,21 +338,31 @@
             @Nullable String name,
             @Nullable TestProject testProject,
             boolean experimentalMode,
+            boolean minifyEnabled,
+            boolean useJack,
             String targetGradleVersion,
             boolean captureStdOut,
             boolean captureStdErr,
+            @Nullable File sdkDir,
+            @Nullable File ndkDir,
+            @NonNull Collection<String> gradleProperties,
             @Nullable String heapSize) {
-        sdkDir = SdkHelper.findSdkDir();
-        ndkDir = findNdkDir();
         String buildDir = System.getenv("PROJECT_BUILD_DIR");
         outDir = (buildDir == null) ? new File("build/tests") : new File(buildDir, "tests");
+        testDir = null;
+        buildFile = sourceDir = null;
         this.name = (name == null) ? DEFAULT_TEST_PROJECT_NAME : name;
         this.experimentalMode = experimentalMode;
+        this.minifyEnabled = minifyEnabled;
+        this.useJack = useJack;
         this.targetGradleVersion = targetGradleVersion;
         this.testProject = testProject;
         stdout = captureStdOut ? new ByteArrayOutputStream() : null;
         stderr = captureStdErr ? new ByteArrayOutputStream() : null;
+        this.sdkDir = sdkDir;
+        this.ndkDir = ndkDir;
         this.heapSize = heapSize;
+        this.gradleProperties = gradleProperties;
     }
 
     /**
@@ -269,7 +385,20 @@
         sdkDir = rootProject.sdkDir;
         stdout = rootProject.stdout;
         stderr = rootProject.stdout;
+        gradleProperties = ImmutableList.of();
         testProject = null;
+        experimentalMode = rootProject.isExperimentalMode();
+        targetGradleVersion = rootProject.getTargetGradleVersion();
+        minifyEnabled = false;
+        useJack = false;
+    }
+
+    String getTargetGradleVersion() {
+        return targetGradleVersion;
+    }
+
+    boolean isExperimentalMode() {
+        return experimentalMode;
     }
 
     public static Builder builder() {
@@ -366,6 +495,7 @@
                 }
 
                 createLocalProp(testDir, sdkDir, ndkDir);
+                createGradleProp();
                 base.evaluate();
             }
         };
@@ -523,11 +653,43 @@
      * @param tasks Variadic list of tasks to execute.
      */
     public void execute(String ... tasks) {
-        execute(Collections.<String>emptyList(), false, false, tasks);
+        execute(Collections.<String>emptyList(), false, false, ExpectedBuildResult.SUCCESS, tasks);
     }
 
     public void execute(@NonNull List<String> arguments, String ... tasks) {
-        execute(arguments, false, false, tasks);
+        execute(arguments, false, false, ExpectedBuildResult.SUCCESS, tasks);
+    }
+
+    public void executeWithBenchmark(
+            @NonNull String benchmarkName,
+            @NonNull BenchmarkMode benchmarkMode,
+            String ... tasks) {
+        List<String> arguments = ImmutableList.of(
+                "-P" + RECORD_BENCHMARK_NAME + "=" + benchmarkName,
+                "-P" + RECORD_BENCHMARK_MODE + "=" + benchmarkMode.name().toLowerCase(Locale.US)
+        );
+        execute(arguments, false, false, ExpectedBuildResult.SUCCESS, tasks);
+    }
+
+    public void executeExpectingFailure(String... tasks) {
+        executeExpectingFailure(Collections.<String>emptyList(), tasks);
+    }
+
+    public void executeExpectingFailure(@NonNull List<String> arguments, String... tasks) {
+        execute(
+                arguments,
+                false /*returnModel*/,
+                false /*emulateStudio_1_0*/,
+                ExpectedBuildResult.FAILURE,
+                tasks);
+    }
+
+    public void executeConnectedCheck() {
+        executeConnectedCheck(Collections.<String>emptyList());
+    }
+
+    public void executeConnectedCheck(List<String> arguments) {
+        execute(arguments, REMOTE_TEST_PROVIDER == null ? "connectedCheck" : "deviceCheck");
     }
 
     /**
@@ -553,7 +715,8 @@
     @NonNull
     public AndroidProject executeAndReturnModel(boolean emulateStudio_1_0, String ... tasks) {
         //noinspection ConstantConditions
-        return execute(Collections.<String>emptyList(), true, emulateStudio_1_0, tasks);
+        return execute(Collections.<String>emptyList(), true, emulateStudio_1_0,
+                ExpectedBuildResult.SUCCESS, tasks);
     }
 
     /**
@@ -582,9 +745,10 @@
     public Map<String, AndroidProject> executeAndReturnMultiModel(boolean emulateStudio_1_0, String ... tasks) {
         ProjectConnection connection = getProjectConnection();
         try {
-            executeBuild(Collections.<String>emptyList(), connection, tasks);
+            executeBuild(Collections.<String>emptyList(), connection, tasks,
+                    ExpectedBuildResult.SUCCESS);
 
-            return buildModel(connection, new GetAndroidModelAction(), emulateStudio_1_0);
+            return buildModel(connection, new GetAndroidModelAction(), emulateStudio_1_0, null, null);
 
         } finally {
             connection.close();
@@ -649,7 +813,9 @@
             Map<String, AndroidProject> modelMap = buildModel(
                     connection,
                     new GetAndroidModelAction(),
-                    emulateStudio_1_0);
+                    emulateStudio_1_0,
+                    null,
+                    null);
 
             // ensure there was only one project
             assertEquals("Quering GradleTestProject.getModel() with multi-project settings",
@@ -671,13 +837,28 @@
      */
     @NonNull
     public Map<String, AndroidProject> getAllModels() {
-        Map<String, AndroidProject> allModels = getAllModels(new GetAndroidModelAction(), false);
+        return getAllModelsWithBenchmark(null, null);
+    }
+
+    /**
+     * Returns a project model for each sub-project without building generating a
+     * @param benchmarkName optional benchmark name to pass to Gradle
+     * @param benchmarkMode optional benchmark mode to pass to gradle.
+
+     * {@link AssertionError} if any sync issue is raised during the model loading.
+     */
+    @NonNull
+    public Map<String, AndroidProject> getAllModelsWithBenchmark(
+            @Nullable String benchmarkName,
+            @Nullable BenchmarkMode benchmarkMode) {
+        Map<String, AndroidProject> allModels = getAllModels(new GetAndroidModelAction(), false, benchmarkName, benchmarkMode);
         for (AndroidProject project : allModels.values()) {
             assertNoSyncIssues(project);
         }
         return allModels;
     }
 
+
     private static void assertNoSyncIssues(AndroidProject project) {
         if (!project.getSyncIssues().isEmpty()) {
             StringBuilder msg = new StringBuilder();
@@ -699,7 +880,7 @@
      */
     @NonNull
     public Map<String, AndroidProject> getAllModelsIgnoringSyncIssues() {
-        return getAllModels(new GetAndroidModelAction(), false);
+        return getAllModels(new GetAndroidModelAction(), false, null, null);
     }
 
     /**
@@ -709,7 +890,7 @@
      */
     @NonNull
     public <K, V> Map<K, V> getAllModels(@NonNull BuildAction<Map<K, V>> action) {
-        return getAllModels(action, false);
+        return getAllModels(action, false, null, null);
     }
 
     /**
@@ -717,30 +898,32 @@
      *
      * @param action the build action to gather the model
      * @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
+     * @param benchmarkName optional benchmark name to pass to Gradle
+     * @param benchmarkMode optional benchmark mode to pass to gradle.
      */
     @NonNull
     public <K, V> Map<K, V> getAllModels(
             @NonNull BuildAction<Map<K, V>> action,
-            boolean emulateStudio_1_0) {
+            boolean emulateStudio_1_0,
+            @Nullable String benchmarkName,
+            @Nullable BenchmarkMode benchmarkMode) {
         ProjectConnection connection = getProjectConnection();
         try {
-            return buildModel(connection, action, emulateStudio_1_0);
+            return buildModel(connection, action, emulateStudio_1_0, benchmarkName, benchmarkMode);
 
         } finally {
             connection.close();
         }
     }
 
-    public void evaluate() {
-        getAllModels(new GetEmptyModelAction(), false);
-    }
-
     /**
      * Runs gradle on the project.  Throws exception on failure.
      *
      * @param arguments List of arguments for the gradle command.
      * @param returnModel whether the model should be queried and returned.
      * @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
+     * @param expectedBuildResult the expected result. If the build status does not match the
+     *                            expected failure, then an exception will be thrown.
      * @param tasks Variadic list of tasks to execute.
      *
      * @return the model, if <var>returnModel</var> was true, null otherwise
@@ -750,16 +933,19 @@
             @NonNull List<String> arguments,
             boolean returnModel,
             boolean emulateStudio_1_0,
+            ExpectedBuildResult expectedBuildResult,
             @NonNull String ... tasks) {
         ProjectConnection connection = getProjectConnection();
         try {
-            executeBuild(arguments, connection, tasks);
+            executeBuild(arguments, connection, tasks, expectedBuildResult);
 
             if (returnModel) {
                 Map<String, AndroidProject> modelMap = buildModel(
                         connection,
                         new GetAndroidModelAction(),
-                        emulateStudio_1_0);
+                        emulateStudio_1_0,
+                        null,
+                        null);
 
                 // ensure there was only one project
                 assertEquals("Quering GradleTestProject.getModel() with multi-project settings",
@@ -774,25 +960,28 @@
         return null;
     }
 
-    private void executeBuild(List<String> arguments, ProjectConnection connection,
-            String[] tasks) {
-        List<String> args = Lists.newArrayListWithCapacity(3 + arguments.size());
+    private enum ExpectedBuildResult {
+        SUCCESS,
+        FAILURE
+    }
+
+    private void executeBuild(final List<String> arguments, ProjectConnection connection,
+            final String[] tasks, ExpectedBuildResult expectedBuildResult) {
+        List<String> args = Lists.newArrayListWithCapacity(5 + arguments.size());
         args.add("-i");
         args.add("-u");
+        args.add("-Pcom.android.build.gradle.integratonTest.useJack=" + Boolean.toString(useJack));
+        args.add("-Pcom.android.build.gradle.integratonTest.minifyEnabled=" +
+                Boolean.toString(minifyEnabled));
+        args.add("-Pcom.android.build.gradle.integratonTest.useComponentModel=" +
+                Boolean.toString(experimentalMode));
         args.addAll(arguments);
 
+        BuildLauncher launcher = connection.newBuild()
+                .forTasks(tasks)
+                .withArguments(Iterables.toArray(args, String.class));
 
-        List<String> jvmArguments = getJvmArguments();
-
-        if (JacocoAgent.isJacocoEnabled()) {
-            jvmArguments.add(JacocoAgent.getJvmArg());
-        }
-        if (!jvmArguments.isEmpty()) {
-            args.add("-Dorg.gradle.jvmargs=" + Joiner.on(' ').join(jvmArguments));
-        }
-
-        BuildLauncher launcher = connection.newBuild().forTasks(tasks)
-                .withArguments(args.toArray(new String[args.size()]));
+        setJvmArguments(launcher);
 
         if (stdout != null) {
             launcher.setStandardOutput(stdout);
@@ -804,55 +993,89 @@
         } else {
             launcher.setStandardError(System.err);
         }
-        launcher.run();
+        if (expectedBuildResult == ExpectedBuildResult.SUCCESS) {
+            launcher.run();
+        } else {
+            launcher.run(new ResultHandler<Void>() {
+                @Override
+                public void onComplete(Void aVoid) {
+                    throw new AssertionError(
+                            String.format(
+                                    "Expecting build to fail:\n" +
+                                            "    Tasks:     %s\n" +
+                                            "    Arguments: %s",
+                                    Joiner.on(' ').join(tasks),
+                                    Joiner.on(' ').join(arguments)));
+                }
+
+                @Override
+                public void onFailure(GradleConnectionException e) {
+                    // Ignore, the test expects this build to fail.
+                }
+            });
+        }
     }
 
-    private List<String> getJvmArguments() {
+    private void setJvmArguments(LongRunningOperation launcher) {
         List<String> jvmArguments = new ArrayList<String>();
+
         if (!Strings.isNullOrEmpty(heapSize)) {
             jvmArguments.add("-Xmx" + heapSize);
         }
+
         jvmArguments.add("-XX:MaxPermSize=1024m");
+
         String debugIntegrationTest = System.getenv("DEBUG_INNER_TEST");
         if (!Strings.isNullOrEmpty(debugIntegrationTest)) {
-            jvmArguments.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005");
+            jvmArguments.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006");
         }
-        return jvmArguments;
+
+        if (JacocoAgent.isJacocoEnabled()) {
+            jvmArguments.add(JacocoAgent.getJvmArg());
+        }
+
+        launcher.setJvmArguments(Iterables.toArray(jvmArguments, String.class));
     }
 
     /**
      * Returns a project model for each sub-project without building.
-     *
      * @param connection the opened ProjectConnection
      * @param action the build action to gather the model
      * @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
+     * @param benchmarkName optional benchmark name to pass to Gradle
+     * @param benchmarkMode optional benchmark mode to pass to gradle.
      */
     @NonNull
     private <K,V> Map<K, V> buildModel(
             @NonNull ProjectConnection connection,
             @NonNull BuildAction<Map<K, V>> action,
-            boolean emulateStudio_1_0) {
+            boolean emulateStudio_1_0,
+            @Nullable String benchmarkName,
+            @Nullable BenchmarkMode benchmarkMode) {
 
-        BuildActionExecuter<Map<K, V>> executer = connection.action(action);
+        BuildActionExecuter<Map<K, V>> executor = connection.action(action);
 
-        List<String> arguments = Lists.newArrayListWithCapacity(emulateStudio_1_0 ? 2 : 3);
+        List<String> arguments = Lists.newArrayListWithCapacity(5);
         arguments.add("-P" + AndroidProject.PROPERTY_BUILD_MODEL_ONLY + "=true");
         arguments.add("-P" + AndroidProject.PROPERTY_INVOKED_FROM_IDE + "=true");
         if (!emulateStudio_1_0) {
             arguments.add("-P" + AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED + "=true");
         }
-
-        List<String> debugJvmArguments = getJvmArguments();
-        if (!debugJvmArguments.isEmpty()) {
-            arguments.add("-Dorg.gradle.jvmargs=" + Joiner.on(' ').join(debugJvmArguments));
+        if (benchmarkName != null) {
+            arguments.add("-P" + RECORD_BENCHMARK_NAME + "=" + benchmarkName);
+            if (benchmarkMode != null) {
+                arguments.add("-P" + RECORD_BENCHMARK_MODE + "=" + benchmarkMode.name().toLowerCase(Locale.US));
+            }
         }
 
-        executer.withArguments(Iterables.toArray(arguments, String.class));
+        setJvmArguments(executor);
 
-        executer.setStandardOutput(System.out);
-        executer.setStandardError(System.err);
+        executor.withArguments(Iterables.toArray(arguments, String.class));
 
-        return executer.run();
+        executor.setStandardOutput(System.out);
+        executor.setStandardError(System.err);
+
+        return executor.run();
     }
 
     /**
@@ -930,4 +1153,54 @@
 
         return (File) localProp.getFile();
     }
+
+    private void createGradleProp() throws IOException {
+        if (gradleProperties.isEmpty()) {
+            return;
+        }
+        File propertyFile = file("gradle.properties");
+        Files.write(Joiner.on('\n').join(gradleProperties), propertyFile, Charset.defaultCharset());
+    }
+
+
+    public static void assumeLocalDevice() {
+        Assume.assumeTrue(
+                "Install task not run against device provider",
+                GradleTestProject.REMOTE_TEST_PROVIDER == null);
+    }
+
+    public static void assumeBuildToolsAtLeast(int major) {
+        assumeBuildToolsAtLeast(
+                major, FullRevision.IMPLICIT_MINOR_REV, FullRevision.IMPLICIT_MICRO_REV);
+    }
+
+    public static void assumeBuildToolsAtLeast(int major, int minor, int micro) {
+        FullRevision currentVersion = FullRevision.parseRevision(DEFAULT_BUILD_TOOL_VERSION);
+        Assume.assumeTrue("Test is only applicable to build tools > " + major,
+                new FullRevision(major, minor, micro).compareTo(currentVersion) < 0);
+    }
+
+    /**
+     * Replace a line from a file with another line.
+     * @param relativePath the relative path of the file from the root of the project
+     * @param lineNumber the line number, starting at 1
+     * @param line the line to replace with.
+     * @throws IOException
+     */
+    public void replaceLine(
+            String relativePath,
+            int lineNumber,
+            String line) throws IOException {
+        File file = new File(testDir, relativePath.replace("/", File.separator));
+
+        List<String> lines = Files.readLines(file, Charsets.UTF_8);
+
+        lines.add(lineNumber, line);
+        lines.remove(lineNumber - 1);
+
+        Files.write(
+                Joiner.on(System.getProperty("line.separator")).join(lines),
+                file,
+                Charsets.UTF_8);
+    }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/TemporaryProjectModification.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/TemporaryProjectModification.java
new file mode 100644
index 0000000..316361e
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/TemporaryProjectModification.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.integration.common.fixture;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.annotations.NonNull;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.io.Files;
+
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.MultipleFailureException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import groovy.lang.Closure;
+
+/**
+ * Allows project files to be modified, but stores their original content, so it can be restored for
+ * the next test.
+ */
+public class TemporaryProjectModification {
+
+    private final GradleTestProject mTestProject;
+
+    private final Map<String, String> mFileContentToRestore = new HashMap<String, String>();
+
+    private TemporaryProjectModification(GradleTestProject testProject) {
+        mTestProject = testProject;
+    }
+
+    /**
+     * Runs a test that mutates the project in a reversible way, and returns the project to its
+     * original state after the callback has been run.
+     *
+     * @param project The project to modify.
+     * @param test    The test to run.
+     * @throws InitializationError if the project modification infrastructure fails.
+     * @throws Exception passed through if the test throws an exception.
+     */
+    public static void doTest(GradleTestProject project, ModifiedProjectTest test) throws
+            Exception {
+        TemporaryProjectModification modifiedProject = new TemporaryProjectModification(project);
+        try {
+            test.runTest(modifiedProject);
+        } finally {
+            modifiedProject.close();
+        }
+    }
+
+    public interface ModifiedProjectTest {
+        void runTest(TemporaryProjectModification modifiedProject) throws Exception;
+    }
+
+    public void replaceFile(
+            @NonNull String relativePath,
+            @NonNull final String content) throws InitializationError {
+        modifyFile(relativePath, new Function<String, String>() {
+            @Override
+            public String apply(String input) {
+                return content;
+            }
+        });
+    }
+
+    public void replaceInFile(
+            @NonNull String relativePath,
+            @NonNull final String search,
+            @NonNull final String replace) throws InitializationError {
+        modifyFile(relativePath, new Function<String, String>() {
+            @Override
+            public String apply(String input) {
+                return input.replaceAll(search, replace);
+            }
+        });
+
+    }
+
+    public void modifyFile(
+            @NonNull String relativePath,
+            @NonNull Function<String, String> modification) throws InitializationError {
+        File file = mTestProject.file(relativePath);
+
+        String currentContent = null;
+        try {
+            currentContent = Files.toString(file, Charsets.UTF_8);
+        } catch (IOException e) {
+            throw new InitializationError(e);
+        }
+
+        // We can modify multiple times, but we only want to store the original.
+        if (!mFileContentToRestore.containsKey(relativePath)) {
+            mFileContentToRestore.put(relativePath, currentContent);
+        }
+
+        String newContent = modification.apply(currentContent);
+
+        if (newContent == null) {
+            assertTrue("File should have been deleted", file.delete());
+        } else {
+            try {
+                Files.write(newContent, file, Charsets.UTF_8);
+            } catch (IOException e) {
+                throw new InitializationError(e);
+            }
+        }
+    }
+
+    /**
+     * Returns the project back to its original state.
+     */
+    public void close() throws InitializationError {
+        try {
+            for (Map.Entry<String, String> entry : mFileContentToRestore.entrySet()) {
+                Files.write(entry.getValue(), mTestProject.file(entry.getKey()), Charsets.UTF_8);
+            }
+        } catch (IOException e) {
+            throw new InitializationError(e);
+        }
+        mFileContentToRestore.clear();
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java
index e723a64..4fe46b5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java
@@ -46,11 +46,11 @@
         return "apply plugin: 'com.android.model.library'\n" +
                 "\n" +
                 "model {\n" +
-                "    android.config {\n" +
-                "        compileSdkVersion " + DEFAULT_COMPILE_SDK_VERSION + "\n" +
-                "        buildToolsVersion '" + DEFAULT_BUILD_TOOL_VERSION + "'\n" +
-                "        defaultConfig {\n" +
-                "            useJack " + CUSTOM_JACK + "\n" +
+                "    android {\n" +
+                "        compileSdkVersion = " + DEFAULT_COMPILE_SDK_VERSION + "\n" +
+                "        buildToolsVersion = '" + DEFAULT_BUILD_TOOL_VERSION + "'\n" +
+                "        defaultConfig.with {\n" +
+                "//            useJack " + CUSTOM_JACK + "\n" +
                 "        }\n" +
                 "    }\n" +
                 "}\n";
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy
index 71448db..1454877 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy
@@ -19,5 +19,25 @@
 /**
  * An empty app.
  */
-class EmptyAndroidTestApp extends AbstractAndroidTestApp {
+class EmptyAndroidTestApp extends AbstractAndroidTestApp implements AndroidTestApp {
+
+    public EmptyAndroidTestApp() {
+
+    }
+
+    public EmptyAndroidTestApp(String packageName) {
+        TestSourceFile manifest =
+                new TestSourceFile("src/main", "AndroidManifest.xml",
+                        """<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="$packageName"
+      android:versionCode="1"
+      android:versionName="1.0">
+
+    <application/>
+</manifest>
+""");
+
+        addFiles(manifest);
+    }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java
index 5b65c8d..d9160ef 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java
@@ -19,7 +19,6 @@
 import com.android.annotations.NonNull;
 
 import java.io.File;
-import java.lang.reflect.Constructor;
 import java.util.List;
 
 /**
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java
index 7cf61e6..a4fb890 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java
@@ -1,7 +1,11 @@
 package com.android.build.gradle.integration.common.fixture.app;
 
+import com.google.common.collect.Maps;
+
 import java.util.Map;
 
+import groovy.lang.Closure;
+
 /**
  * Generator to create build.gradle with arbitrary number of variants.
  *
@@ -25,6 +29,8 @@
 
     private final Map<String, Integer> variantCounts;
 
+    private final Map<String, Closure<String>> variantPostProcessors = Maps.newHashMap();
+
     /**
      * Create a VariantBuildScriptGenerator
      *
@@ -33,27 +39,42 @@
      * @param template a template for the build script.  Strings in the format "${key}" will be
      *                 replaced if the key exists in variantCounts.
      */
-    public VariantBuildScriptGenerator(Map<String, Integer> variantCounts, String template) {
+    public VariantBuildScriptGenerator(
+            Map<String, Integer> variantCounts,
+            String template) {
         this.template = template;
         this.variantCounts = variantCounts;
     }
 
     /**
+     * Add a post processor to customize the output format of a specified variant.
+     *
+     * @param variant Name of the variant type.
+     * @param postProcessor A Closure that accept the variant type name as String and return the
+     *                      formatted String.
+     */
+    public void addPostProcessor(String variant, Closure<String> postProcessor) {
+        variantPostProcessors.put(variant, postProcessor);
+    }
+
+    /**
      * Generate the string for a build.gradle script.
      */
     public String createBuildScript() {
         String buildScript = template;
-        System.out.println(template);
         for (Map.Entry<String, Integer> variantCount : variantCounts.entrySet()) {
             String variantName = variantCount.getKey();
             StringBuilder variants = new StringBuilder();
             for (int i = 0; i < variantCount.getValue(); i++) {
-                variants.append(variantName);
-                variants.append(i);
+                Closure<String> postProcessor = variantPostProcessors.get(variantName);
+                if (postProcessor == null) {
+                    variants.append(variantName);
+                    variants.append(i);
+                } else {
+                    variants.append(postProcessor.call(variantName + i));
+                }
                 variants.append("\n");
             }
-            System.out.println(variants.toString());
-
             buildScript = buildScript.replace("${" + variantName + "}", variants.toString());
         }
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java
index ec75b68..8554fae 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java
@@ -17,64 +17,59 @@
 package com.android.build.gradle.integration.common.truth;
 
 import com.android.annotations.NonNull;
-import com.android.annotations.VisibleForTesting;
-import com.android.build.gradle.integration.common.utils.ApkHelper;
-import com.android.build.gradle.integration.common.utils.SdkHelper;
-import com.android.builder.core.ApkInfoParser;
-import com.android.ide.common.process.DefaultProcessExecutor;
 import com.android.ide.common.process.ProcessException;
-import com.android.ide.common.process.ProcessExecutor;
-import com.android.ide.common.process.ProcessInfoBuilder;
-import com.android.utils.StdLogger;
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
 import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.ListSubject;
-import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
-
-import junit.framework.Assert;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.SubjectFactory;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.io.InputStreamReader;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
 /**
  * Truth support for aar files.
  */
-public class AarSubject extends AbstractZipSubject<AarSubject> {
+public class AarSubject extends AbstractAndroidSubject<AarSubject> {
 
-    public AarSubject(FailureStrategy failureStrategy, File subject) {
+    static class Factory extends SubjectFactory<AarSubject, File> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public AarSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull File subject) {
+            return new AarSubject(failureStrategy, subject);
+        }
+    }
+
+    public AarSubject(@NonNull FailureStrategy failureStrategy, @NonNull File subject) {
         super(failureStrategy, subject);
     }
 
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void containsClass(String className) throws IOException, ProcessException {
-        if (!checkForClass(className)) {
-            failWithRawMessage("'%s' does not contain '%s'", getDisplaySubject(), className);
-        }
-    }
+    @NonNull
+    public StringSubject textSymbolFile() throws IOException {
+        InputStream stream = getInputStream("R.txt");
 
-    public void doesNotContainClass(String className) throws IOException, ProcessException {
-        if (checkForClass(className)) {
-            failWithRawMessage("'%s' unexpectedly contains '%s'", getDisplaySubject(), className);
-        }
-    }
-
-    @Override
-    protected String getDisplaySubject() {
-        String name = (internalCustomName() == null) ? "" : "\"" + internalCustomName() + "\" ";
-        return name + "<" + getSubject().getName() + ">";
+        return new StringSubject(failureStrategy,
+                CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)));
     }
 
     /**
      * Returns true if the provided class is present in the file.
      * @param expectedClassName the class name in the format Lpkg1/pk2/Name;
      */
-    private boolean checkForClass(
+    @Override
+    protected boolean checkForClass(
             @NonNull String expectedClassName)
             throws ProcessException, IOException {
         InputStream stream = getInputStream("classes.jar");
@@ -94,4 +89,5 @@
             zis.close();
         }
     }
+
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubjectFactory.java
deleted file mode 100644
index cde9e1f..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubjectFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 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.integration.common.truth;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-import java.io.File;
-
-/**
- * Factory to add truth support for aar files.
- */
-class AarSubjectFactory extends SubjectFactory<AarSubject, File> {
-    public static AarSubjectFactory factory() {
-        return new AarSubjectFactory();
-    }
-
-    private AarSubjectFactory() {}
-
-    @Override
-    public AarSubject getSubject(FailureStrategy failureStrategy, File subject) {
-        return new AarSubject(failureStrategy, subject);
-    }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractAndroidSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractAndroidSubject.java
new file mode 100644
index 0000000..8c83940
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractAndroidSubject.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 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.integration.common.truth;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.ProcessException;
+import com.google.common.truth.FailureStrategy;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.ZipFile;
+
+/**
+ * Base Truth support for android archives (aar and apk)
+ */
+public abstract class AbstractAndroidSubject<T extends AbstractZipSubject<T>> extends AbstractZipSubject<T> {
+
+    public AbstractAndroidSubject(@NonNull FailureStrategy failureStrategy, @NonNull File subject) {
+        super(failureStrategy, subject);
+    }
+
+    /**
+     * Returns true if the provided class is present in the file.
+     * @param expectedClassName the class name in the format Lpkg1/pk2/Name;
+     */
+    protected abstract boolean checkForClass(
+            @NonNull String expectedClassName)
+            throws ProcessException, IOException;
+
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public void containsClass(String className) throws IOException, ProcessException {
+        if (!checkForClass(className)) {
+            failWithRawMessage("'%s' does not contain '%s'", getDisplaySubject(), className);
+        }
+    }
+
+    public void doesNotContainClass(String className) throws IOException, ProcessException {
+        if (checkForClass(className)) {
+            failWithRawMessage("'%s' unexpectedly contains '%s'", getDisplaySubject(), className);
+        }
+    }
+
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public void containsResource(String name) throws IOException, ProcessException {
+        if (!checkForResource(name)) {
+            failWithRawMessage("'%s' does not contain resource '%s'", getDisplaySubject(), name);
+        }
+    }
+
+    public void doesNotContainResource(String name) throws IOException, ProcessException {
+        if (checkForResource(name)) {
+            failWithRawMessage("'%s' unexpectedly contains resource '%s'", getDisplaySubject(), name);
+        }
+    }
+
+    @Override
+    protected String getDisplaySubject() {
+        String name = (internalCustomName() == null) ? "" : "\"" + internalCustomName() + "\" ";
+        return name + "<" + getSubject().getName() + ">";
+    }
+
+    private boolean checkForResource(String name) throws IOException {
+        ZipFile zipFile = null;
+        try {
+            zipFile = new ZipFile(getSubject());
+            return zipFile.getEntry("res/" + name) != null;
+        } finally {
+            if (zipFile != null) {
+                zipFile.close();
+            }
+        }
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java
index fea69b4..3566744 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java
@@ -16,11 +16,13 @@
 
 package com.android.build.gradle.integration.common.truth;
 
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
 
+import com.android.annotations.NonNull;
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
+import com.google.common.primitives.Bytes;
 import com.google.common.truth.FailureStrategy;
 import com.google.common.truth.IterableSubject;
 import com.google.common.truth.Subject;
@@ -40,12 +42,13 @@
 public abstract class AbstractZipSubject<T extends Subject<T, File>> extends Subject<T, File> {
     private ZipFile zip;
 
-    public AbstractZipSubject(FailureStrategy failureStrategy, File subject) {
+    public AbstractZipSubject(@NonNull FailureStrategy failureStrategy, @NonNull File subject) {
         super(failureStrategy, subject);
+        new FileSubject(failureStrategy, subject).exists();
         try {
             zip = new ZipFile(subject);
         } catch (IOException e) {
-            failWithRawMessage("IOException thrown when creating ZipFile: %s.", e.toString());
+            failWithRawMessage("IOException thrown when creating ZipFile: '%s'.", e.toString());
         }
     }
 
@@ -53,7 +56,7 @@
      * Asserts the zip file contains a file with the specified path.
      */
     @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void contains(String path) {
+    public void contains(@NonNull String path) {
         if (zip.getEntry(path) == null) {
             failWithRawMessage("'%s' does not contain '%s'", zip.getName(), path);
         }
@@ -62,7 +65,7 @@
     /**
      * Asserts the zip file does not contains a file with the specified path.
      */
-    public void doesNotContain(String path) {
+    public void doesNotContain(@NonNull String path) {
         if (zip.getEntry(path) != null) {
             failWithRawMessage("'%s' unexpectedly contains '%s'", zip.getName(), path);
         }
@@ -77,7 +80,7 @@
      * @throws IOException of the zip file cannot be opened.
      */
     public IterableSubject<? extends IterableSubject<?, String, List<String>>, String, List<String>> entries(
-            String conformingTo) throws IOException {
+            @NonNull String conformingTo) throws IOException {
 
         ImmutableList.Builder<String> entries = ImmutableList.builder();
         Pattern pattern = Pattern.compile(conformingTo);
@@ -102,19 +105,32 @@
      * Content is trimmed when compared.
      */
     @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void containsFileWithContent(String path, String content) {
-        assertThat(extractContentAsString(path).trim()).named(path).comparesEqualTo(content.trim());
+    public void containsFileWithContent(@NonNull String path, @NonNull String content) {
+        assertThat(extractContentAsString(path).trim()).named(path).isEqualTo(content.trim());
     }
 
     /**
      * Asserts the zip file contains a file with the specified byte array content.
      */
     @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void containsFileWithContent(String path, byte[] content) {
+    public void containsFileWithContent(@NonNull String path, @NonNull byte[] content) {
         assertThat(extractContentAsByte(path)).named(path).isEqualTo(content);
     }
 
-    protected String extractContentAsString(String path) {
+    /**
+     * Asserts the zip file contains a file <b>without</b> the specified byte sequence
+     * <b>anywhere</b> in the file
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public void containsFileWithoutContent(@NonNull String path, @NonNull byte[] sub) {
+        byte[] contents = extractContentAsByte(path);
+        int index = Bytes.indexOf(contents, sub);
+        if (index != -1) {
+            failWithRawMessage("Found byte sequence at " + index + " in class file " + path);
+        }
+    }
+
+    protected String extractContentAsString(@NonNull String path) {
         InputStream stream = getInputStream(path);
         try {
             return new String(ByteStreams.toByteArray(stream), Charsets.UTF_8).trim();
@@ -124,7 +140,7 @@
         }
     }
 
-    protected byte[] extractContentAsByte(String path) {
+    protected byte[] extractContentAsByte(@NonNull String path) {
         InputStream stream = getInputStream(path);
         try {
             return ByteStreams.toByteArray(stream);
@@ -134,7 +150,7 @@
         }
     }
 
-    protected InputStream getInputStream(String path) {
+    protected InputStream getInputStream(@NonNull String path) {
         ZipEntry entry = zip.getEntry(path);
         if (entry == null) {
             failWithRawMessage("'%s' does not contain '%s'", zip.getName(), path);
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java
index 91f47f8..291ff13 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java
@@ -27,23 +27,22 @@
 import com.android.ide.common.process.ProcessInfoBuilder;
 import com.android.utils.StdLogger;
 import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.ListSubject;
-import com.google.common.truth.Subject;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.SubjectFactory;
 import com.google.common.truth.Truth;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.zip.ZipFile;
 
 /**
  * Truth support for apk files.
  */
-public class ApkSubject extends Subject<ApkSubject, File> {
+public class ApkSubject extends AbstractAndroidSubject<ApkSubject> {
 
     private static final Pattern PATTERN_CLASS_DESC = Pattern.compile(
             "^Class descriptor\\W*:\\W*'(L.+;)'$");
@@ -51,12 +50,31 @@
     private static final Pattern PATTERN_MAX_SDK_VERSION = Pattern.compile(
             "^maxSdkVersion\\W*:\\W*'(.+)'$");
 
-    public ApkSubject(FailureStrategy failureStrategy, File subject) {
+    static class Factory extends SubjectFactory<ApkSubject, File> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public ApkSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull File subject) {
+            return new ApkSubject(failureStrategy, subject);
+        }
+    }
+
+
+    public ApkSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull File subject) {
         super(failureStrategy, subject);
     }
 
     @NonNull
-    public ListSubject locales() throws ProcessException {
+    public IterableSubject<? extends IterableSubject<?, String, List<String>>, String, List<String>> locales() throws ProcessException {
         File apk = getSubject();
         List<String> locales = ApkHelper.getLocales(apk);
 
@@ -68,32 +86,6 @@
     }
 
     @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void containsClass(String className) throws IOException, ProcessException {
-        if (!checkForClass(className)) {
-            failWithRawMessage("'%s' does not contain '%s'", getDisplaySubject(), className);
-        }
-    }
-
-    public void doesNotContainClass(String className) throws IOException, ProcessException {
-        if (checkForClass(className)) {
-            failWithRawMessage("'%s' unexpectedly contains '%s'", getDisplaySubject(), className);
-        }
-    }
-
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void containsResource(String name) throws IOException, ProcessException {
-        if (!checkForResource(name)) {
-            failWithRawMessage("'%s' does not contain resource '%s'", getDisplaySubject(), name);
-        }
-    }
-
-    public void doesNotContainResource(String name) throws IOException, ProcessException {
-        if (checkForResource(name)) {
-            failWithRawMessage("'%s' unexpectedly contains resource '%s'", getDisplaySubject(), name);
-        }
-    }
-
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
     public void hasVersionCode(int versionCode) throws ProcessException {
         File apk = getSubject();
 
@@ -110,7 +102,7 @@
     }
 
     @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void hasVersionName(String versionName) throws ProcessException {
+    public void hasVersionName(@NonNull String versionName) throws ProcessException {
         File apk = getSubject();
 
         ApkInfoParser.ApkInfo apkInfo = getApkInfo(apk);
@@ -143,7 +135,8 @@
      * Returns true if the provided class is present in the file.
      * @param expectedClassName the class name in the format Lpkg1/pk2/Name;
      */
-    private boolean checkForClass(
+    @Override
+    protected boolean checkForClass(
             @NonNull String expectedClassName)
             throws ProcessException, IOException {
         // get the dexdump exec
@@ -170,20 +163,8 @@
         return false;
     }
 
-    private boolean checkForResource(String name) throws IOException {
-        ZipFile zipFile = null;
-        try {
-            zipFile = new ZipFile(getSubject());
-            return zipFile.getEntry("res/" + name) != null;
-        } finally {
-            if (zipFile != null) {
-                zipFile.close();
-            }
-        }
-    }
-
     @NonNull
-    private static ApkInfoParser.ApkInfo getApkInfo(File apk) throws ProcessException {
+    private static ApkInfoParser.ApkInfo getApkInfo(@NonNull File apk) throws ProcessException {
         ProcessExecutor processExecutor = new DefaultProcessExecutor(
                 new StdLogger(StdLogger.Level.ERROR));
         ApkInfoParser parser = new ApkInfoParser(SdkHelper.getAapt(), processExecutor);
@@ -191,7 +172,7 @@
     }
 
     @VisibleForTesting
-    void checkMaxSdkVersion(List<String> output, int maxSdkVersion) {
+    void checkMaxSdkVersion(@NonNull List<String> output, int maxSdkVersion) {
         for (String line : output) {
             Matcher m = PATTERN_MAX_SDK_VERSION.matcher(line.trim());
             if (m.matches()) {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectFactory.java
deleted file mode 100644
index 7664e6f..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 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.integration.common.truth;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-import java.io.File;
-
-/**
- * Factory to add truth support for apk files.
- */
-class ApkSubjectFactory extends SubjectFactory<ApkSubject, File> {
-    public static ApkSubjectFactory factory() {
-        return new ApkSubjectFactory();
-    }
-
-    private ApkSubjectFactory() {}
-
-    @Override
-    public ApkSubject getSubject(FailureStrategy failureStrategy, File subject) {
-        return new ApkSubject(failureStrategy, subject);
-    }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java
index 18d6e0c..f1bdca3 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java
@@ -35,10 +35,12 @@
         FakeFailureStrategy failure = new FakeFailureStrategy();
         File file = new File("foo");
         ApkSubject subject = new ApkSubject(failure, file);
+        // apk file doesn't exist so the failure gets filled with error. Ignore and reset.
+        failure.reset();
 
         subject.checkMaxSdkVersion(strings, 1);
 
-        assertThat(failure.message).is("maxSdkVersion not found in badging output for <foo>");
+        assertThat(failure.message).isEqualTo("maxSdkVersion not found in badging output for <foo>");
     }
 
     @Test
@@ -51,6 +53,8 @@
         FakeFailureStrategy failure = new FakeFailureStrategy();
         File file = new File("foo");
         ApkSubject subject = new ApkSubject(failure, file);
+        // apk file doesn't exist so the failure gets filled with error. Ignore and reset.
+        failure.reset();
 
         subject.checkMaxSdkVersion(strings, 14);
 
@@ -67,9 +71,11 @@
         FakeFailureStrategy failure = new FakeFailureStrategy();
         File file = new File("foo");
         ApkSubject subject = new ApkSubject(failure, file);
+        // apk file doesn't exist so the failure gets filled with error. Ignore and reset.
+        failure.reset();
 
         subject.checkMaxSdkVersion(strings, 14);
 
-        assertThat(failure.message).is("Not true that <foo> has maxSdkVersion <14>. It is <20>");
+        assertThat(failure.message).isEqualTo("Not true that <foo> has maxSdkVersion <14>. It is <20>");
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ArtifactSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ArtifactSubject.java
new file mode 100644
index 0000000..d2068c1
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ArtifactSubject.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.integration.common.truth;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.AndroidArtifact;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+public class ArtifactSubject extends Subject<ArtifactSubject, AndroidArtifact> {
+
+    static class Factory extends SubjectFactory<ArtifactSubject, AndroidArtifact> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public ArtifactSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull AndroidArtifact subject) {
+            return new ArtifactSubject(failureStrategy, subject);
+        }
+    }
+
+    public ArtifactSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull AndroidArtifact subject) {
+        super(failureStrategy, subject);
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/CollectionIssueSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/CollectionIssueSubject.java
deleted file mode 100644
index 329bade..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/CollectionIssueSubject.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2015 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.integration.common.truth;
-
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
-
-import com.android.builder.model.SyncIssue;
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.Subject;
-
-import java.util.Collection;
-
-public class CollectionIssueSubject extends Subject<CollectionIssueSubject, Collection<SyncIssue>> {
-
-    public CollectionIssueSubject(FailureStrategy failureStrategy, Collection<SyncIssue> subject) {
-        super(failureStrategy, subject);
-    }
-
-    /**
-     * Asserts that the issue collection has only a single element with the given properties.
-     * Not specified properties are not tested and could have any value.
-     *
-     * @param severity the expected severity
-     * @param type the expected type
-     * @return the found issue for further testing.
-     */
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public SyncIssue hasSingleIssue(int severity, int type) {
-        Collection<SyncIssue> subject = getSubject();
-
-        assertThat(subject).hasSize(1);
-
-        SyncIssue issue = subject.iterator().next();
-        assertThat(issue).isNotNull();
-        assertThat(issue).hasSeverity(severity);
-        assertThat(issue).hasType(type);
-
-        return issue;
-    }
-
-    /**
-     * Asserts that the issue collection has only a single element with the given properties.
-     * Not specified properties are not tested and could have any value.
-     *
-     * @param severity the expected severity
-     * @param type the expected type
-     * @param data the expected data
-     * @return the found issue for further testing.
-     */
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public SyncIssue hasSingleIssue(int severity, int type, String data) {
-        Collection<SyncIssue> subject = getSubject();
-
-        assertThat(subject).hasSize(1);
-
-        SyncIssue issue = subject.iterator().next();
-        assertThat(issue).isNotNull();
-        assertThat(issue).hasSeverity(severity);
-        assertThat(issue).hasType(type);
-        assertThat(issue).hasData(data);
-
-        return issue;
-    }
-
-    /**
-     * Asserts that the issue collection has only a single element with the given properties.
-     *
-     * @param severity the expected severity
-     * @param type the expected type
-     * @param data the expected data
-     * @param message the expected message
-     */
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public void hasSingleIssue(int severity, int type, String data, String message) {
-        Collection<SyncIssue> subject = getSubject();
-
-        assertThat(subject).hasSize(1);
-
-        SyncIssue issue = subject.iterator().next();
-        assertThat(issue).isNotNull();
-        assertThat(issue).hasSeverity(severity);
-        assertThat(issue).hasType(type);
-        assertThat(issue).hasData(data);
-        assertThat(issue).hasMessage(message);
-    }
-
-    /**
-     * Asserts that the issue collection has only an element with the given properties.
-     * Not specified properties are not tested and could have any value.
-     *
-     * @param severity the expected severity
-     * @param type the expected type
-     * @return the found issue for further testing.
-     */
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public SyncIssue has(int severity, int type) {
-        Collection<SyncIssue> subject = getSubject();
-
-        for (SyncIssue issue : subject) {
-            if (severity == issue.getSeverity() &&
-                    type == issue.getType()) {
-                return issue;
-            }
-        }
-
-        failWithRawMessage("'%s' does not contain <%s / %s>", getDisplaySubject(),
-                severity, type);
-        // won't reach
-        return null;
-    }
-
-    /**
-     * Asserts that the issue collection has only an element with the given properties.
-     * Not specified properties are not tested and could have any value.
-     *
-     * @param severity the expected severity
-     * @param type the expected type
-     * @param data the expected data
-     * @return the found issue for further testing.
-     */
-    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
-    public SyncIssue has(int severity, int type, String data) {
-        Collection<SyncIssue> subject = getSubject();
-
-        for (SyncIssue issue : subject) {
-            if (severity == issue.getSeverity() &&
-                    type == issue.getType() &&
-                    data.equals(issue.getData())) {
-                return issue;
-            }
-        }
-
-        failWithRawMessage("'%s' does not contain <%s / %s / %s>", getDisplaySubject(),
-                severity, type, data);
-        // won't reach
-        return null;
-    }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/DependenciesSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/DependenciesSubject.java
new file mode 100644
index 0000000..b5c04b9
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/DependenciesSubject.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.integration.common.truth;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.Dependencies;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.Collection;
+
+
+public class DependenciesSubject extends Subject<DependenciesSubject, Dependencies> {
+
+    static class Factory extends
+            SubjectFactory<DependenciesSubject, Dependencies> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public DependenciesSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull Dependencies subject) {
+            return new DependenciesSubject(failureStrategy, subject);
+        }
+    }
+
+    public DependenciesSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull Dependencies subject) {
+        super(failureStrategy, subject);
+    }
+
+    /**
+     * Checks that the dependencies has a single library.
+     *
+     * @return the library.
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public AndroidLibrary hasOneLibrary() {
+        Collection<AndroidLibrary> libs = getSubject().getLibraries();
+
+        assertThat(libs).hasSize(1);
+
+        return libs.iterator().next();
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java
index ff27f07..73a3e1b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java
@@ -16,6 +16,7 @@
 
 package com.android.build.gradle.integration.common.truth;
 
+import com.google.common.base.Objects;
 import com.google.common.truth.FailureStrategy;
 
 /**
@@ -45,4 +46,21 @@
         this.expected = expected;
         this.actual = actual;
     }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this)
+                .add("message", message)
+                .add("throwable", throwable)
+                .add("expected", expected)
+                .add("actual", actual)
+                .toString();
+    }
+
+    public void reset() {
+        message = null;
+        throwable = null;
+        expected = null;
+        actual = null;
+    }
 }
\ No newline at end of file
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubject.java
new file mode 100644
index 0000000..f9a6975
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubject.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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.integration.common.truth;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+
+import java.io.File;
+
+/**
+ * Truth support for validating File.
+ */
+public class FileSubject extends Subject<FileSubject, File> {
+    public FileSubject(FailureStrategy failureStrategy, File subject) {
+        super(failureStrategy, subject);
+    }
+
+    public void exists() {
+        if (!getSubject().exists()) {
+            fail("exists");
+        }
+    }
+
+    public void doesNotExist() {
+        if (getSubject().exists()) {
+            fail("does not exist");
+        }
+    }
+
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public void isFile() {
+        if (!getSubject().exists()) {
+            fail("is a file");
+        }
+    }
+
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public void isDirectory() {
+        if (!getSubject().isDirectory()) {
+            fail("is a directory");
+        }
+    }
+
+    public FileSubject subFile(String path) {
+        if (!getSubject().isDirectory()) {
+            fail("is a directory");
+        }
+        return new FileSubject(failureStrategy, new File(getSubject(), path));
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubjectFactory.java
new file mode 100644
index 0000000..f9a9965
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubjectFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.integration.common.truth;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
+
+import java.io.File;
+
+/**
+ * Factory to add truth support for File.
+ */
+public class FileSubjectFactory extends SubjectFactory<FileSubject, File> {
+    public static FileSubjectFactory factory() {
+        return new FileSubjectFactory();
+    }
+
+    private FileSubjectFactory() {}
+
+    @Override
+    public FileSubject getSubject(FailureStrategy failureStrategy, File subject) {
+        return new FileSubject(failureStrategy, subject);
+    }
+
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java
index d9e9d11..70b0d76 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java
@@ -16,15 +16,33 @@
 
 package com.android.build.gradle.integration.common.truth;
 
+import com.android.annotations.NonNull;
 import com.android.builder.model.SyncIssue;
 import com.google.common.truth.FailureStrategy;
 import com.google.common.truth.Subject;
-
+import com.google.common.truth.SubjectFactory;
 
 public class IssueSubject extends Subject<IssueSubject, SyncIssue> {
 
-    public IssueSubject(FailureStrategy failureStrategy,
-            SyncIssue subject) {
+    static class Factory extends SubjectFactory<IssueSubject, SyncIssue> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public IssueSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull SyncIssue subject) {
+            return new IssueSubject(failureStrategy, subject);
+        }
+    }
+
+    public IssueSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull SyncIssue subject) {
         super(failureStrategy, subject);
     }
 
@@ -40,13 +58,13 @@
         }
     }
 
-    public void hasData(String data) {
+    public void hasData(@NonNull String data) {
         if (!data.equals(getSubject().getData())) {
             failWithBadResults("has data", data, "is", getSubject().getData());
         }
     }
 
-    public void hasMessage(String message) {
+    public void hasMessage(@NonNull String message) {
         if (!message.equals(getSubject().getMessage())) {
             failWithBadResults("has message", message, "is", getSubject().getMessage());
         }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectFactory.java
deleted file mode 100644
index 9ca376e..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 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.integration.common.truth;
-
-import com.android.builder.model.SyncIssue;
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-/**
- * Factory to add truth support for SyncIssue.
- */
-public class IssueSubjectFactory extends SubjectFactory<IssueSubject, SyncIssue> {
-    public static IssueSubjectFactory factory() {
-        return new IssueSubjectFactory();
-    }
-
-    private IssueSubjectFactory() {}
-
-    @Override
-    public IssueSubject getSubject(FailureStrategy failureStrategy, SyncIssue subject) {
-        return new IssueSubject(failureStrategy, subject);
-    }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java
index 80ac751..01031ae 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java
@@ -79,7 +79,7 @@
         IssueSubject subject = new IssueSubject(failure, issue);
         subject.hasSeverity(0);
 
-        assertThat(failure.message).is("Not true that <1|2|> has severity <0>. It is <1>");
+        assertThat(failure.message).isEqualTo("Not true that <1|2|> has severity <0>. It is <1>");
     }
 
     @Test
@@ -91,7 +91,7 @@
         IssueSubject subject = new IssueSubject(failure, issue);
         subject.hasType(0);
 
-        assertThat(failure.message).is("Not true that <1|2|> has type <0>. It is <2>");
+        assertThat(failure.message).isEqualTo("Not true that <1|2|> has type <0>. It is <2>");
     }
 
     @Test
@@ -103,7 +103,7 @@
         IssueSubject subject = new IssueSubject(failure, issue);
         subject.hasData("bar");
 
-        assertThat(failure.message).is("Not true that <1|2|foo> has data <bar>. It is <foo>");
+        assertThat(failure.message).isEqualTo("Not true that <1|2|foo> has data <bar>. It is <foo>");
     }
 
     @Test
@@ -115,7 +115,7 @@
         IssueSubject subject = new IssueSubject(failure, issue);
         subject.hasMessage("robert");
 
-        assertThat(failure.message).is("Not true that <1|2|foo> has message <robert>. It is <bob>");
+        assertThat(failure.message).isEqualTo("Not true that <1|2|foo> has message <robert>. It is <bob>");
     }
 
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java
index 99b9950..5736717 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java
@@ -16,26 +16,163 @@
 
 package com.android.build.gradle.integration.common.truth;
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
+
+import com.android.annotations.NonNull;
 import com.android.builder.model.AndroidProject;
-import com.google.common.truth.CollectionSubject;
+import com.android.builder.model.SyncIssue;
 import com.google.common.truth.FailureStrategy;
 import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.Collection;
 
 /**
  * Truth support for AndroidProject.
  */
 public class ModelSubject extends Subject<ModelSubject, AndroidProject> {
 
-    public ModelSubject(FailureStrategy failureStrategy, AndroidProject subject) {
+    static class Factory extends SubjectFactory<ModelSubject, AndroidProject> {
+
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public ModelSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull AndroidProject subject) {
+            return new ModelSubject(failureStrategy, subject);
+        }
+    }
+
+    public ModelSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull AndroidProject subject) {
         super(failureStrategy, subject);
     }
 
-    public CollectionIssueSubject issues() {
-        return new CollectionIssueSubject(failureStrategy, getSubject().getSyncIssues());
+
+    /**
+     * Asserts that the issue collection has only a single element with the given properties.
+     * Not specified properties are not tested and could have any value.
+     *
+     * @param severity the expected severity
+     * @param type the expected type
+     * @return the found issue for further testing.
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public SyncIssue hasSingleIssue(int severity, int type) {
+        Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+        assertThat(subject).hasSize(1);
+
+        SyncIssue issue = subject.iterator().next();
+        assertThat(issue).isNotNull();
+        assertThat(issue).hasSeverity(severity);
+        assertThat(issue).hasType(type);
+
+        return issue;
     }
 
-    public CollectionSubject issuesAsCollection() {
-        return Truth.assertThat(getSubject().getSyncIssues());
+    /**
+     * Asserts that the issue collection has only a single element with the given properties.
+     * Not specified properties are not tested and could have any value.
+     *
+     * @param severity the expected severity
+     * @param type the expected type
+     * @param data the expected data
+     * @return the found issue for further testing.
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public SyncIssue hasSingleIssue(int severity, int type, String data) {
+        Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+        assertThat(subject).hasSize(1);
+
+        SyncIssue issue = subject.iterator().next();
+        assertThat(issue).isNotNull();
+        assertThat(issue).hasSeverity(severity);
+        assertThat(issue).hasType(type);
+        assertThat(issue).hasData(data);
+
+        return issue;
+    }
+
+    /**
+     * Asserts that the issue collection has only a single element with the given properties.
+     *
+     * @param severity the expected severity
+     * @param type the expected type
+     * @param data the expected data
+     * @param message the expected message
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public void hasSingleIssue(int severity, int type, String data, String message) {
+        Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+        assertThat(subject).hasSize(1);
+
+        SyncIssue issue = subject.iterator().next();
+        assertThat(issue).isNotNull();
+        assertThat(issue).hasSeverity(severity);
+        assertThat(issue).hasType(type);
+        assertThat(issue).hasData(data);
+        assertThat(issue).hasMessage(message);
+    }
+
+    /**
+     * Asserts that the issue collection has only an element with the given properties.
+     * Not specified properties are not tested and could have any value.
+     *
+     * @param severity the expected severity
+     * @param type the expected type
+     * @return the found issue for further testing.
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public SyncIssue hasIssue(int severity, int type) {
+        Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+        for (SyncIssue issue : subject) {
+            if (severity == issue.getSeverity() &&
+                    type == issue.getType()) {
+                return issue;
+            }
+        }
+
+        failWithRawMessage("'%s' does not contain <%s / %s>", getDisplaySubject(),
+                severity, type);
+        // won't reach
+        return null;
+    }
+
+    /**
+     * Asserts that the issue collection has only an element with the given properties.
+     * Not specified properties are not tested and could have any value.
+     *
+     * @param severity the expected severity
+     * @param type the expected type
+     * @param data the expected data
+     * @return the found issue for further testing.
+     */
+    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+    public SyncIssue hasIssue(int severity, int type, String data) {
+        Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+        for (SyncIssue issue : subject) {
+            if (severity == issue.getSeverity() &&
+                    type == issue.getType() &&
+                    data.equals(issue.getData())) {
+                return issue;
+            }
+        }
+
+        failWithRawMessage("'%s' does not contain <%s / %s / %s>", getDisplaySubject(),
+                severity, type, data);
+        // won't reach
+        return null;
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubjectFactory.java
deleted file mode 100644
index df189e6..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubjectFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 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.integration.common.truth;
-
-import com.android.builder.model.AndroidProject;
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-/**
- * Factory to add truth support for AndroidProject.
- */
-class ModelSubjectFactory extends SubjectFactory<ModelSubject, AndroidProject> {
-    public static ModelSubjectFactory factory() {
-        return new ModelSubjectFactory();
-    }
-
-    private ModelSubjectFactory() {}
-
-    @Override
-    public ModelSubject getSubject(FailureStrategy failureStrategy, AndroidProject subject) {
-        return new ModelSubject(failureStrategy, subject);
-    }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java
index a3567b3..58d992e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java
@@ -19,19 +19,32 @@
 import static com.google.common.truth.Truth.assert_;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
 import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Dependencies;
 import com.android.builder.model.SyncIssue;
+import com.android.builder.model.Variant;
+import com.google.common.annotations.GwtIncompatible;
 import com.google.common.base.Optional;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Table;
+import com.google.common.truth.BigDecimalSubject;
 import com.google.common.truth.BooleanSubject;
 import com.google.common.truth.ClassSubject;
-import com.google.common.truth.CollectionSubject;
 import com.google.common.truth.ComparableSubject;
 import com.google.common.truth.DefaultSubject;
+import com.google.common.truth.DoubleSubject;
 import com.google.common.truth.IntegerSubject;
 import com.google.common.truth.IterableSubject;
-import com.google.common.truth.ListSubject;
+import com.google.common.truth.ListMultimapSubject;
 import com.google.common.truth.LongSubject;
 import com.google.common.truth.MapSubject;
+import com.google.common.truth.MultimapSubject;
+import com.google.common.truth.MultisetSubject;
 import com.google.common.truth.ObjectArraySubject;
 import com.google.common.truth.OptionalSubject;
 import com.google.common.truth.PrimitiveBooleanArraySubject;
@@ -41,130 +54,189 @@
 import com.google.common.truth.PrimitiveFloatArraySubject;
 import com.google.common.truth.PrimitiveIntArraySubject;
 import com.google.common.truth.PrimitiveLongArraySubject;
+import com.google.common.truth.SetMultimapSubject;
 import com.google.common.truth.StringSubject;
 import com.google.common.truth.Subject;
+import com.google.common.truth.TableSubject;
+import com.google.common.truth.TestVerb;
+import com.google.common.truth.ThrowableSubject;
 
 import java.io.File;
-import java.util.Collection;
-import java.util.List;
+import java.math.BigDecimal;
 import java.util.Map;
 
 /**
  * Helper for custom Truth factories.
  */
 public class TruthHelper {
+    @NonNull
+    public static FileSubject assertThat(@NonNull File file) {
+        return assert_().about(FileSubjectFactory.factory()).that(file);
+    }
 
     @NonNull
     public static ApkSubject assertThatApk(@NonNull File apk) {
-        return assert_().about(ApkSubjectFactory.factory()).that(apk);
+        return assert_().about(ApkSubject.Factory.get()).that(apk);
     }
 
     @NonNull
     public static AarSubject assertThatAar(@NonNull File aar) {
-        return assert_().about(AarSubjectFactory.factory()).that(aar);
+        return assert_().about(AarSubject.Factory.get()).that(aar);
     }
 
     @NonNull
     public static ZipFileSubject assertThatZip(@NonNull File file) {
-        return assert_().about(ZipFileSubjectFactory.factory()).that(file);
+        return assert_().about(ZipFileSubject.Factory.get()).that(file);
     }
 
     @NonNull
     public static ModelSubject assertThat(@NonNull AndroidProject androidProject) {
-        return assert_().about(ModelSubjectFactory.factory()).that(androidProject);
+        return assert_().about(ModelSubject.Factory.get()).that(androidProject);
     }
 
     @NonNull
     public static IssueSubject assertThat(@NonNull SyncIssue issue) {
-        return assert_().about(IssueSubjectFactory.factory()).that(issue);
+        return assert_().about(IssueSubject.Factory.get()).that(issue);
     }
 
+    @NonNull
+    public static VariantSubject assertThat(@NonNull Variant variant) {
+        return assert_().about(VariantSubject.Factory.get()).that(variant);
+    }
+
+    @NonNull
+    public static ArtifactSubject assertThat(@NonNull AndroidArtifact artifact) {
+        return assert_().about(ArtifactSubject.Factory.get()).that(artifact);
+    }
+
+    @NonNull
+    public static DependenciesSubject assertThat(@NonNull Dependencies dependencies) {
+        return assert_().about(DependenciesSubject.Factory.get()).that(
+                dependencies);
+    }
 
     // ---- helper method from com.google.common.truth.Truth
     // this to allow a single static import of assertThat
 
-    public static <T extends Comparable<?>> ComparableSubject<?, T> assertThat(T target) {
+    /**
+     * Returns a {@link TestVerb} that will prepend the given message to the failure message in
+     * the event of a test failure.
+     */
+    public static TestVerb assertWithMessage(String messageToPrepend) {
+        return assert_().withFailureMessage(messageToPrepend);
+    }
+
+    public static <T extends Comparable<?>> ComparableSubject<?, T> assertThat(@Nullable T target) {
         return assert_().that(target);
     }
 
-    public static Subject<DefaultSubject, Object> assertThat(Object target) {
+    public static BigDecimalSubject assertThat(@Nullable BigDecimal target) {
         return assert_().that(target);
     }
 
-    public static ClassSubject assertThat(Class<?> target) {
+    public static Subject<DefaultSubject, Object> assertThat(@Nullable Object target) {
         return assert_().that(target);
     }
 
-    public static LongSubject assertThat(Long target) {
+    @GwtIncompatible("ClassSubject.java")
+    public static ClassSubject assertThat(@Nullable Class<?> target) {
         return assert_().that(target);
     }
 
-    public static IntegerSubject assertThat(Integer target) {
+    public static ThrowableSubject assertThat(@Nullable Throwable target) {
         return assert_().that(target);
     }
 
-    public static BooleanSubject assertThat(Boolean target) {
+    public static LongSubject assertThat(@Nullable Long target) {
         return assert_().that(target);
     }
 
-    public static StringSubject assertThat(String target) {
+    public static DoubleSubject assertThat(@Nullable Double target) {
+        return assert_().that(target);
+    }
+
+    public static IntegerSubject assertThat(@Nullable Integer target) {
+        return assert_().that(target);
+    }
+
+    public static BooleanSubject assertThat(@Nullable Boolean target) {
+        return assert_().that(target);
+    }
+
+    public static StringSubject assertThat(@Nullable String target) {
         return assert_().that(target);
     }
 
     public static <T, C extends Iterable<T>> IterableSubject<? extends IterableSubject<?, T, C>, T, C>
-    assertThat(Iterable<T> target) {
+    assertThat(@Nullable Iterable<T> target) {
         return assert_().that(target);
     }
 
-    public static <T, C extends Collection<T>>
-    CollectionSubject<? extends CollectionSubject<?, T, C>, T, C>
-    assertThat(Collection<T> target) {
+    public static <T> ObjectArraySubject<T> assertThat(@Nullable T[] target) {
         return assert_().that(target);
     }
 
-    public static <T, C extends List<T>> ListSubject<? extends ListSubject<?, T, C>, T, C>
-    assertThat(List<T> target) {
+    public static PrimitiveBooleanArraySubject assertThat(@Nullable boolean[] target) {
         return assert_().that(target);
     }
 
-    public static <T> ObjectArraySubject<T> assertThat(T[] target) {
+    public static PrimitiveIntArraySubject assertThat(@Nullable int[] target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveBooleanArraySubject assertThat(boolean[] target) {
+    public static PrimitiveLongArraySubject assertThat(@Nullable long[] target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveIntArraySubject assertThat(int[] target) {
+    public static PrimitiveByteArraySubject assertThat(@Nullable byte[] target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveLongArraySubject assertThat(long[] target) {
+    public static PrimitiveCharArraySubject assertThat(@Nullable char[] target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveByteArraySubject assertThat(byte[] target) {
+    public static PrimitiveFloatArraySubject assertThat(@Nullable float[] target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveCharArraySubject assertThat(char[] target) {
+    public static PrimitiveDoubleArraySubject assertThat(@Nullable double[] target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveFloatArraySubject assertThat(float[] target) {
+    public static <T> OptionalSubject<T> assertThat(@Nullable Optional<T> target) {
         return assert_().that(target);
     }
 
-    public static PrimitiveDoubleArraySubject assertThat(double[] target) {
+    public static MapSubject assertThat(@Nullable Map<?, ?> target) {
         return assert_().that(target);
     }
 
-    public static <T> OptionalSubject<T> assertThat(Optional<T> target) {
+    public static <K, V, M extends Multimap<K, V>>
+    MultimapSubject<? extends MultimapSubject<?, K, V, M>, K, V, M> assertThat(
+            @Nullable Multimap<K, V> target) {
         return assert_().that(target);
     }
 
-    public static <K, V, M extends Map<K, V>> MapSubject<? extends MapSubject<?, K, V, M>, K, V, M>
-    assertThat(Map<K, V> target) {
+    public static <K, V, M extends ListMultimap<K, V>>
+    ListMultimapSubject<? extends ListMultimapSubject<?, K, V, M>, K, V, M> assertThat(
+            @Nullable ListMultimap<K, V> target) {
+        return assert_().that(target);
+    }
+
+    public static <K, V, M extends SetMultimap<K, V>>
+    SetMultimapSubject<? extends SetMultimapSubject<?, K, V, M>, K, V, M> assertThat(
+            @Nullable SetMultimap<K, V> target) {
+        return assert_().that(target);
+    }
+
+    public static <E, M extends Multiset<E>>
+    MultisetSubject<? extends MultisetSubject<?, E, M>, E, M> assertThat(
+            @Nullable Multiset<E> target) {
+        return assert_().that(target);
+    }
+
+    public static TableSubject assertThat(@Nullable Table<?, ?, ?> target) {
         return assert_().that(target);
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/VariantSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/VariantSubject.java
new file mode 100644
index 0000000..bf4586c
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/VariantSubject.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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.integration.common.truth;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.Variant;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+
+public class VariantSubject extends Subject<VariantSubject, Variant> {
+
+    static class Factory extends SubjectFactory<VariantSubject, Variant> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public VariantSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull Variant subject) {
+            return new VariantSubject(failureStrategy, subject);
+        }
+    }
+
+    public VariantSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull Variant subject) {
+        super(failureStrategy, subject);
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java
index ce3aa3f..c567dec 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java
@@ -16,7 +16,9 @@
 
 package com.android.build.gradle.integration.common.truth;
 
+import com.android.annotations.NonNull;
 import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
 
 import java.io.File;
 
@@ -25,7 +27,25 @@
  */
 public class ZipFileSubject extends AbstractZipSubject<ZipFileSubject> {
 
-    public ZipFileSubject(FailureStrategy failureStrategy, File subject) {
+    static class Factory extends SubjectFactory<ZipFileSubject, File> {
+        @NonNull
+        public static Factory get() {
+            return new Factory();
+        }
+
+        private Factory() {}
+
+        @Override
+        public ZipFileSubject getSubject(
+                @NonNull FailureStrategy failureStrategy,
+                @NonNull File subject) {
+            return new ZipFileSubject(failureStrategy, subject);
+        }
+    }
+
+    public ZipFileSubject(
+            @NonNull FailureStrategy failureStrategy,
+            @NonNull File subject) {
         super(failureStrategy, subject);
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubjectFactory.java
deleted file mode 100644
index fe262d2..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubjectFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 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.integration.common.truth;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-import java.io.File;
-
-/**
- * Factory to add truth support for zip files.
- */
-public class ZipFileSubjectFactory extends SubjectFactory<ZipFileSubject, File> {
-    public static ZipFileSubjectFactory factory() {
-        return new ZipFileSubjectFactory();
-    }
-
-    private ZipFileSubjectFactory() {}
-
-    @Override
-    public ZipFileSubject getSubject(FailureStrategy failureStrategy, File subject) {
-        return new ZipFileSubject(failureStrategy, subject);
-    }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java
index bc1322e..2967398 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java
@@ -21,12 +21,10 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.build.gradle.integration.common.fixture.app.TestSourceFile;
 import com.google.common.base.Charsets;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Lists;
 import com.google.common.io.Files;
-import com.google.common.truth.Truth;
 
 import java.io.File;
 import java.io.IOException;
@@ -49,7 +47,8 @@
                 new Predicate<File>() {
                     @Override
                     public boolean apply(@Nullable File file) {
-                        return file != null && !file.isDirectory();
+                        // we want to skip directories and symlinks, so isFile is the best check.
+                        return file != null && file.isFile();
                     }
                 })) {
             assertThat(file.toString()).startsWith(base.toString());
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy
index b54178e..fb15666 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.component
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Test AndroidComponentModelPlugin.
  */
+@CompileStatic
 class AndroidComponentPluginTest {
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
@@ -40,11 +42,11 @@
 
 model {
     android.buildTypes {
-        custom
+        create("custom")
     }
     android.productFlavors {
-        flavor1
-        flavor2
+        create("flavor1")
+        create("flavor2")
     }
 }
 """
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy
index f3c73ba..0556cdf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy
@@ -17,8 +17,11 @@
 package com.android.build.gradle.integration.component
 
 import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.category.SmokeTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
+import com.android.builder.model.AndroidProject
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -29,12 +32,15 @@
 /**
  * Basic integration test for AppComponentModelPlugin.
  */
+@Category(SmokeTests.class)
+@CompileStatic
 class AppComponentPluginTest {
 
     @Rule
     public GradleTestProject project = GradleTestProject.builder()
             .fromTestApp(new HelloWorldApp())
             .forExpermimentalPlugin(true)
+            .withoutNdk()
             .create();
 
     @Before
@@ -43,9 +49,9 @@
 apply plugin: "com.android.model.application"
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
 }
 """
@@ -53,7 +59,12 @@
 
     @Test
     public void basicAssemble() {
-        project.execute("assemble");
+        AndroidProject model = project.executeAndReturnModel("assemble");
+        assertThat(model).isNotNull();
+        assertThat(model.getName()).isEqualTo(project.name)
+        assertThat(model.getBuildTypes()).hasSize(2)
+        assertThat(model.getProductFlavors()).hasSize(0)
+        assertThat(model.getVariants()).hasSize(2)
     }
 
     @Test
@@ -61,11 +72,11 @@
         project.buildFile << """
 model {
     android.buildTypes {
-        b1
+        create("b1")
     }
     android.productFlavors {
-        f1
-        f2
+        create("f1")
+        create("f2")
     }
 }
 """
@@ -88,11 +99,17 @@
                 "assembleF1DebugAndroidTest",
                 "assembleF2DebugAndroidTest");
 
+        AndroidProject model = project.executeAndReturnModel("assemble");
+        assertThat(model).isNotNull();
+        assertThat(model.getName()).isEqualTo(project.name)
+        assertThat(model.getBuildTypes()).hasSize(3)
+        assertThat(model.getProductFlavors()).hasSize(2)
+        assertThat(model.getVariants()).hasSize(6)
     }
 
     @Test
     @Category(DeviceTests.class)
     public void connnectedAndroidTest() {
-        project.execute("connectedAndroidTest");
+        project.executeConnectedCheck();
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy
index 154a7bb..3f74612 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy
@@ -17,28 +17,28 @@
 package com.android.build.gradle.integration.component
 
 import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.category.SmokeTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
 
-import java.util.zip.ZipFile
-
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
-import static org.junit.Assert.assertNotNull
 
 /**
  * Basic integration test for native plugin.
  */
+@Category(SmokeTests.class)
+@CompileStatic
 class BasicNdkComponentTest {
 
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
-            .fromTestApp(new HelloWorldJniApp(jniDir: "cpp", useCppSource: true))
+            .fromTestApp(new HelloWorldJniApp(useCppSource: true))
             .forExpermimentalPlugin(true)
             .withHeap("20148m")
             .create();
@@ -49,17 +49,12 @@
 apply plugin: 'com.android.model.application'
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
     android.ndk {
-        moduleName "hello-jni"
-    }
-    android.buildTypes {
-        debug {
-            jniDebuggable true
-        }
+        moduleName = "hello-jni"
     }
 }
 """
@@ -72,44 +67,44 @@
 
     @Test
     public void assemble() {
-        project.execute("assemble");
+        project.execute("assemble")
     }
 
     @Test
     public void assembleRelease() {
-        project.execute("assembleRelease");
+        project.execute("assembleRelease")
 
         // Verify .so are built for all platform.
-        File apk = project.getApk("release", "unsigned");
-        assertThatZip(apk).contains("lib/x86/libhello-jni.so");
-        assertThatZip(apk).contains("lib/mips/libhello-jni.so");
-        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so");
-        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so");
+        File apk = project.getApk("release", "unsigned")
+        assertThatZip(apk).contains("lib/x86/libhello-jni.so")
+        assertThatZip(apk).contains("lib/mips/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
     }
 
     @Test
     public void assembleDebug() {
-        project.execute("assembleDebug");
+        project.execute("assembleDebug")
 
         // Verify .so are built for all platform.
-        File apk = project.getApk("debug");
-        assertThatZip(apk).contains("lib/x86/libhello-jni.so");
-        assertThatZip(apk).contains("lib/x86/gdbserver");
-        assertThatZip(apk).contains("lib/x86/gdb.setup");
-        assertThatZip(apk).contains("lib/mips/libhello-jni.so");
-        assertThatZip(apk).contains("lib/mips/gdbserver");
-        assertThatZip(apk).contains("lib/mips/gdb.setup");
-        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so");
-        assertThatZip(apk).contains("lib/armeabi/gdbserver");
-        assertThatZip(apk).contains("lib/armeabi/gdb.setup");
-        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so");
-        assertThatZip(apk).contains("lib/armeabi-v7a/gdbserver");
-        assertThatZip(apk).contains("lib/armeabi-v7a/gdb.setup");
+        File apk = project.getApk("debug")
+        assertThatZip(apk).contains("lib/x86/libhello-jni.so")
+        assertThatZip(apk).contains("lib/x86/gdbserver")
+        assertThatZip(apk).contains("lib/x86/gdb.setup")
+        assertThatZip(apk).contains("lib/mips/libhello-jni.so")
+        assertThatZip(apk).contains("lib/mips/gdbserver")
+        assertThatZip(apk).contains("lib/mips/gdb.setup")
+        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi/gdbserver")
+        assertThatZip(apk).contains("lib/armeabi/gdb.setup")
+        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi-v7a/gdbserver")
+        assertThatZip(apk).contains("lib/armeabi-v7a/gdb.setup")
     }
 
     @Test
     @Category(DeviceTests.class)
     public void connnectedAndroidTest() {
-        project.execute("connectedAndroidTest");
+        project.executeConnectedCheck();
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentDslTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentDslTest.groovy
new file mode 100644
index 0000000..f58e310
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentDslTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 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.integration.component
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.truth.TruthHelper
+import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+/**
+ * Test various options can be set without necessarily using it.
+ */
+@CompileStatic
+public class ComponentDslTest {
+
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldJniApp())
+            .forExpermimentalPlugin(true)
+            .create();
+
+    @Before
+    public void setUp() {
+        project.file("proguard.txt").createNewFile()
+        project.buildFile << """
+apply plugin: "com.android.model.application"
+
+model {
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+        defaultConfig.with {
+            minSdkVersion.apiLevel = 7
+        }
+    }
+    android.ndk {
+        moduleName = "hello-jni"
+    }
+    android.productFlavors {
+        create("f1") {
+            proguardFiles += file("proguard.txt")
+            buildConfigFields.create {
+                type = "String"
+                name = "foo"
+                value = "\\"bar\\""
+            }
+        }
+        create("f2")
+    }
+}
+
+dependencies {
+    compile 'com.android.support:appcompat-v7:22.2.0'
+}
+"""
+    }
+
+    @Test
+    public void assemble() {
+        AndroidProject model = project.executeAndReturnModel("assemble");
+        assertThat(model).isNotNull();
+        assertThat(model.getName()).isEqualTo(project.name)
+        assertThat(model.getBuildTypes()).hasSize(2)
+        assertThat(model.getProductFlavors()).hasSize(2)
+        assertThat(model.getVariants()).hasSize(4)
+        assertThat(project.getApk("f1", "debug")).exists()
+    }
+
+    @Test
+    @Category(DeviceTests.class)
+    public void connnectedAndroidTest() {
+        project.executeConnectedCheck();
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy
index 7346128..0158f17 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy
@@ -20,6 +20,7 @@
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
 import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -30,20 +31,25 @@
 /**
  * Integration tests for different configuration of source sets.
  */
+@CompileStatic
 class ComponentSourceSetTest {
 
     public static AndroidTestApp app = new HelloWorldJniApp()
 
     static {
+        TestSourceFile manifest = app.getFile("AndroidManifest.xml")
+        app.removeFile(manifest)
+        app.addFile(new TestSourceFile("src", manifest.name, manifest.content));
+
         // Remove the main hello-jni.c and place it in different directories for different flavors.
         // Note that *not* all variant can be built.
         TestSourceFile cSource = app.getFile("hello-jni.c");
         app.removeFile(cSource);
         app.addFile(
-                new TestSourceFile("src/release/c", cSource.name, cSource.content))
+                new TestSourceFile("src/release/jni", cSource.name, cSource.content))
         app.addFile(
-                new TestSourceFile("src/flavor1/c/hello-jni.c", cSource.name, cSource.content))
-        app.addFile(new TestSourceFile("src/flavor2Debug/c/hello-jni.c", cSource.name,
+                new TestSourceFile("src/flavor1/jni/hello-jni.c", cSource.name, cSource.content))
+        app.addFile(new TestSourceFile("src/flavor2Debug/jni/hello-jni.c", cSource.name,
                 cSource.content))
     }
 
@@ -60,29 +66,45 @@
 apply plugin: "com.android.model.application"
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
     android.ndk {
-        moduleName "hello-jni"
+        moduleName = "hello-jni"
     }
     android.productFlavors {
-        flavor1
-        flavor2
-        flavor3
+        create("flavor1")
+        create("flavor2")
+        create("flavor3")
     }
     android.sources {
-        flavor3 {
-            c {
+        main {
+            manifest {
                 source {
-                    srcDir 'src/flavor1/c'
+                    srcDir 'src'
+                }
+            }
+            jni {
+                source {
+                    exclude "**/fail.c"
+                }
+            }
+        }
+        flavor3 {
+            jni {
+                source {
+                    srcDir 'src/flavor1/jni'
                 }
             }
         }
     }
 }
 """
+        project.file("src/main/jni").mkdirs()
+        project.file("src/main/jni/fail.c") << """
+Un-compilable file to test exclude source works.
+"""
     }
 
     @AfterClass
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy
index 6bb948c..f34e196 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy
@@ -16,20 +16,27 @@
 
 package com.android.build.gradle.integration.component
 
+import com.android.build.gradle.integration.common.category.SmokeTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldLibraryApp
 import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatAar
 
 /**
  * Basic integration test for LibraryComponentModelPlugin.
  */
+@Category(SmokeTests.class)
+@CompileStatic
 class LibraryComponentPluginTest {
-    private static testApp = new HelloWorldLibraryApp()
+    private static HelloWorldLibraryApp testApp = new HelloWorldLibraryApp()
 
     static {
         AndroidTestApp app = (AndroidTestApp) testApp.getSubproject(":app")
@@ -46,9 +53,9 @@
 }
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
 }
 """))
@@ -58,10 +65,15 @@
 """
 apply plugin: "com.android.model.library"
 
+dependencies {
+    /* Depend on annotations to trigger the creation of the ExtractAnnotations task */
+    compile 'com.android.support:support-annotations:22.2.0'
+}
+
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
 }
 """))
@@ -73,6 +85,11 @@
             .forExpermimentalPlugin(true)
             .create();
 
+    @BeforeClass
+    public static void assemble() {
+        project.execute("assemble")
+    }
+
     @AfterClass
     static void cleanUp() {
         project = null
@@ -80,7 +97,8 @@
     }
 
     @Test
-    void assemble() {
-        project.execute("assemble")
+    void "check build config file is included"() {
+        File releaseAar = project.getSubproject("lib").getAar("release");
+        assertThatAar(releaseAar).containsClass("com/example/helloworld/BuildConfig.class");
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentModelTest.groovy
new file mode 100644
index 0000000..c8d83c7
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentModelTest.groovy
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2015 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.integration.component
+
+import com.android.SdkConstants
+import com.android.build.gradle.integration.common.category.SmokeTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
+import com.android.builder.model.Variant
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+/**
+ * Test the return model of the NDK.
+ */
+@Category(SmokeTests.class)
+class NdkComponentModelTest {
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldJniApp())
+            .forExpermimentalPlugin(true)
+            .create()
+
+    @Before
+    void setUp() {
+        project.buildFile <<
+"""
+apply plugin: 'com.android.model.application'
+
+model {
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    }
+    android.ndk {
+        moduleName = "hello-jni"
+        CFlags += "-DTEST_C_FLAG"
+        cppFlags += "-DTEST_CPP_FLAG"
+        toolchain = "clang"
+    }
+}
+"""
+    }
+
+    @Test
+    void "check native libraries in model"() {
+        checkModel(
+                debug : [
+                    SdkConstants.ABI_ARMEABI,
+                    SdkConstants.ABI_ARMEABI_V7A,
+                    SdkConstants.ABI_ARM64_V8A,
+                    SdkConstants.ABI_INTEL_ATOM,
+                    SdkConstants.ABI_INTEL_ATOM64,
+                    SdkConstants.ABI_MIPS,
+                    SdkConstants.ABI_MIPS64
+                ]);
+    }
+
+    @Test
+    void "check native libraries with splits"() {
+        project.buildFile <<
+"""
+model {
+    android {
+        splits.with {
+            abi {
+                enable true
+                reset()
+                include 'x86', 'armeabi-v7a', 'mips'
+            }
+        }
+    }
+}
+"""
+        checkModel(
+                debug: [SdkConstants.ABI_ARMEABI_V7A, SdkConstants.ABI_INTEL_ATOM, SdkConstants.ABI_MIPS]);
+    }
+
+    @Test
+    void "check native libraries with splits and universalApk"() {
+        project.buildFile <<
+                """
+model {
+    android {
+        splits.with {
+            abi {
+                enable true
+                reset()
+                include 'x86', 'armeabi-v7a', 'mips'
+                universalApk true
+            }
+        }
+    }
+}
+"""
+        checkModel(
+                debug : [
+                        SdkConstants.ABI_ARMEABI,
+                        SdkConstants.ABI_ARMEABI_V7A,
+                        SdkConstants.ABI_ARM64_V8A,
+                        SdkConstants.ABI_INTEL_ATOM,
+                        SdkConstants.ABI_INTEL_ATOM64,
+                        SdkConstants.ABI_MIPS,
+                        SdkConstants.ABI_MIPS64
+                ]);
+    }
+
+    @Test
+    void "check native libraries with abiFilters"() {
+        project.buildFile <<
+                """
+model {
+    android.productFlavors {
+        create("x86") {
+            ndk.abiFilters += "x86"
+        }
+        create("arm") {
+            ndk.abiFilters += "armeabi-v7a"
+        }
+        create("mips") {
+            ndk.abiFilters +="mips"
+        }
+    }
+}
+"""
+        checkModel(
+                x86Debug : [SdkConstants.ABI_INTEL_ATOM],
+                armDebug : [SdkConstants.ABI_ARMEABI_V7A],
+                mipsDebug : [SdkConstants.ABI_MIPS]);
+    }
+
+    @Test
+    void "check variant specific flags"() {
+        project.buildFile <<
+                """
+model {
+    android.buildTypes {
+        debug {
+            ndk.CFlags += "-DTEST_FLAG_DEBUG"
+        }
+        release {
+            ndk.CFlags += "-DTEST_FLAG_RELEASE"
+        }
+    }
+    android.productFlavors {
+        create("f1") {
+            ndk.CFlags += "-DTEST_FLAG_F1"
+        }
+        create("f2") {
+            ndk.CFlags += "-DTEST_FLAG_F2"
+        }
+    }
+}
+"""
+        AndroidProject model = project.executeAndReturnModel("assembleDebug")
+        NativeLibrary f1Debug = ModelHelper.getVariant(model.getVariants(), "f1Debug").getMainArtifact()
+                .getNativeLibraries().first()
+        assertThat(f1Debug.getCCompilerFlags()).contains("-DTEST_FLAG_DEBUG")
+        assertThat(f1Debug.getCCompilerFlags()).contains("-DTEST_FLAG_F1")
+        NativeLibrary f2Release = ModelHelper.getVariant(model.getVariants(), "f2Release").getMainArtifact()
+                .getNativeLibraries().first()
+        assertThat(f2Release.getCCompilerFlags()).contains("-DTEST_FLAG_RELEASE")
+        assertThat(f2Release.getCCompilerFlags()).contains("-DTEST_FLAG_F2")
+    }
+
+    @Test
+    void "check using add on string for compileSdkVersion"() {
+        project.buildFile <<
+"""
+model {
+    android {
+        compileSdkVersion = "Google Inc.:Google APIs:$GradleTestProject.DEFAULT_COMPILE_SDK_VERSION"
+    }
+}
+"""
+        AndroidProject model = project.executeAndReturnModel("assembleDebug")
+        NativeLibrary lib = ModelHelper.getVariant(model.getVariants(), "debug").getMainArtifact()
+                .getNativeLibraries().first()
+        for (String flag : lib.getCCompilerFlags()) {
+            if (flag.contains("sysroot")) {
+                assertThat(flag).contains("android-${GradleTestProject.DEFAULT_COMPILE_SDK_VERSION}")
+            }
+        }
+    }
+
+    /**
+     * Verify resulting model is as expected.
+     *
+     * @param variantToolchains map of variant name to array of expected toolchains.
+     */
+    private void checkModel(Map variantToolchains) {
+
+        AndroidProject model = project.executeAndReturnModel("assembleDebug")
+
+        Collection<Variant> variants = model.getVariants()
+        for (Map.Entry entry : variantToolchains) {
+            Variant variant = ModelHelper.getVariant(variants, (String) entry.getKey())
+            AndroidArtifact mainArtifact = variant.getMainArtifact()
+
+            assertThat(mainArtifact.getNativeLibraries()).hasSize(((Collection)entry.getValue()).size())
+            for (NativeLibrary nativeLibrary : mainArtifact.getNativeLibraries()) {
+                assertThat(nativeLibrary.getName()).isEqualTo("hello-jni")
+                assertThat(nativeLibrary.getCCompilerFlags()).contains("-DTEST_C_FLAG");
+                assertThat(nativeLibrary.getCCompilerFlags()).contains("-gcc-toolchain");  // check clang specific flags
+                assertThat(nativeLibrary.getCppCompilerFlags()).contains("-DTEST_CPP_FLAG");
+                assertThat(nativeLibrary.getCppCompilerFlags()).contains("-gcc-toolchain");  // check clang specific flags
+                assertThat(nativeLibrary.getCSystemIncludeDirs()).isEmpty();
+                assertThat(nativeLibrary.getCppSystemIncludeDirs()).isNotEmpty();
+                File solibSearchPath = nativeLibrary.getDebuggableLibraryFolders().first()
+                assertThat(new File(solibSearchPath, "libhello-jni.so")).exists()
+            }
+
+            Collection<String> expectedToolchainNames = entry.getValue().collect { "clang-" + it }
+            Collection<String> toolchainNames = model.getNativeToolchains().collect { it.getName() }
+            assertThat(toolchainNames).containsAllIn(expectedToolchainNames)
+            Collection<String> nativeLibToolchains = mainArtifact.getNativeLibraries().
+                    collect { it.getToolchainName() }
+            assertThat(nativeLibToolchains).containsExactlyElementsIn(expectedToolchainNames)
+        }
+
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy
index 0d81b31..7cf288d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy
@@ -16,19 +16,24 @@
 
 package com.android.build.gradle.integration.component
 
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+
 /**
  * Tests for NdkComponentModelPlugin
  */
-import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
 /**
  * Basic integration test for ndk component plugin.
  */
+@CompileStatic
 class NdkComponentPluginTest {
 
     @ClassRule
@@ -44,9 +49,11 @@
 apply plugin: NdkComponentModelPlugin
 
 model {
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    }
     android.ndk {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        moduleName "hello-jni"
+        moduleName = "hello-jni"
     }
 }
 """
@@ -60,5 +67,8 @@
     @Test
     public void assemble() {
         project.execute("assemble");
+        assertThat(project.file("build/intermediates/binaries/debug/obj/x86/libhello-jni.so")).exists()
+        assertThat(project.file("build/intermediates/binaries/debug/lib/x86/libhello-jni.so")).exists()
+        assertThat(project.file("build/intermediates/binaries/release/lib/x86/libhello-jni.so")).exists()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy
index 5d9e347..bc3b282 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy
@@ -15,49 +15,50 @@
  */
 
 package com.android.build.gradle.integration.component
-
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
 
-import java.util.zip.ZipFile
-
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
-import static org.junit.Assert.assertNotNull
-import static org.junit.Assert.assertNull
 
 /**
  * Integration test of the native plugin with multiple variants.
  */
+@CompileStatic
 class NdkComponentSplitTest {
 
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
-            .fromTestApp(new HelloWorldJniApp(jniDir: "c"))
+            .fromTestApp(new HelloWorldJniApp())
             .forExpermimentalPlugin(true)
             .create()
 
     @BeforeClass
     public static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.getBuildFile() << """
 apply plugin: 'com.android.model.application'
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
-        generatePureSplits true
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+        generatePureSplits = true
 
-        defaultConfig {
-            minSdkVersion 21
+        defaultConfig.with {
+            minSdkVersion.with {
+                 apiLevel = 21
+            }
         }
 
-        splits {
+        splits.with {
             abi {
                 enable true
                 reset()
@@ -66,12 +67,7 @@
         }
     }
     android.ndk {
-        moduleName "hello-jni"
-    }
-    android.buildTypes {
-        debug {
-            jniDebuggable true
-        }
+        moduleName = "hello-jni"
     }
 }
 """
@@ -84,6 +80,11 @@
 
     @Test
     public void assembleDebug() {
+        // Ensure compileDebugSource creates the shared object.
+        project.execute("compileDebugSources");
+        assertThat(project.file("build/intermediates/binaries/debug/lib/x86/libhello-jni.so"))
+                .exists();
+
         project.execute("assembleDebug");
 
         // Verify .so are built for all platform.
@@ -111,6 +112,6 @@
     @Test
     @Category(DeviceTests.class)
     public void connectedAndroidTest() {
-        project.execute("connectedAndroidTest");
+        project.executeConnectedCheck();
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy
index c1267c1..779d25c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy
@@ -19,28 +19,28 @@
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.builder.core.BuilderConstants
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
 
-import java.util.zip.ZipFile
-
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
 import static com.google.common.truth.Truth.assertThat
-import static org.junit.Assert.assertNotNull
-import static org.junit.Assert.assertNull
 
 /**
  * Integration test of the native plugin with multiple variants.
  */
+@CompileStatic
 class NdkComponentVariantTest {
 
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
-            .fromTestApp(new HelloWorldJniApp(jniDir: "c"))
+            .fromTestApp(new HelloWorldJniApp())
             .forExpermimentalPlugin(true)
-            .create();
+            .create()
 
     @BeforeClass
     public static void setUp() {
@@ -49,33 +49,28 @@
 apply plugin: 'com.android.model.application'
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
     android.ndk {
-        moduleName "hello-jni"
+        moduleName = "hello-jni"
     }
     android.buildTypes {
-        debug {
-            jniDebuggable true
+        create("jniDebug") {
+            ndk.debuggable = true;
         }
     }
     android.productFlavors {
-        x86 {
-            ndk {
-                abiFilter "x86"
-            }
+        create("x86") {
+            ndk.abiFilters += "x86"
         }
-        arm {
-            ndk {
-                abiFilters "armeabi-v7a", "armeabi"
-            }
+        create("arm") {
+            ndk.abiFilters += "armeabi-v7a"
+            ndk.abiFilters += "armeabi"
         }
-        mips {
-            ndk {
-                abiFilter "mips"
-            }
+        create("mips") {
+            ndk.abiFilters += "mips"
         }
     }
 }
@@ -101,43 +96,73 @@
 
     @Test
     public void assembleX86Debug() {
-        project.execute("assembleX86Debug");
+        project.execute("assembleX86Debug")
 
         // Verify .so are built for all platform.
-        ZipFile apk = new ZipFile(project.getApk("x86", "debug"));
-        assertNotNull(apk.getEntry("lib/x86/libhello-jni.so"));
-        assertNull(apk.getEntry("lib/mips/libhello-jni.so"));
-        assertNull(apk.getEntry("lib/armeabi/libhello-jni.so"));
-        assertNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so"));
+        File apk = project.getApk("x86", "debug")
+        assertThatZip(apk).contains("lib/x86/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/mips/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/armeabi-v7a/libhello-jni.so")
     }
 
     @Test
     public void assembleArmDebug() {
-        project.execute("assembleArmDebug");
+        project.execute("assembleArmDebug")
 
         // Verify .so are built for all platform.
-        ZipFile apk = new ZipFile(project.getApk("arm", "debug"));
-        assertNull(apk.getEntry("lib/x86/libhello-jni.so"));
-        assertNull(apk.getEntry("lib/mips/libhello-jni.so"));
-        assertNotNull(apk.getEntry("lib/armeabi/libhello-jni.so"));
-        assertNotNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so"));
+        File apk = project.getApk("arm", "debug")
+        assertThatZip(apk).doesNotContain("lib/x86/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/mips/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
     }
 
     @Test
     public void assembleMipsDebug() {
-        project.execute("assembleMipsDebug");
+        project.execute("assembleMipsDebug")
 
         // Verify .so are built for all platform.
-        ZipFile apk = new ZipFile(project.getApk("mips", "debug"));
-        assertNull(apk.getEntry("lib/x86/libhello-jni.so"));
-        assertNotNull(apk.getEntry("lib/mips/libhello-jni.so"));
-        assertNull(apk.getEntry("lib/armeabi/libhello-jni.so"));
-        assertNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so"));
+        File apk = project.getApk("mips", "debug")
+        assertThatZip(apk).doesNotContain("lib/x86/libhello-jni.so")
+        assertThatZip(apk).contains("lib/mips/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/armeabi-v7a/libhello-jni.so")
+    }
+
+    @Test
+    public void "check setting isDebuggable generates gdbserver and gdb.setup"() {
+        project.execute("assembleArmJniDebug")
+
+        File apk = project.getApk("arm", "jniDebug", "unsigned")
+        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi/gdbserver")
+        assertThatZip(apk).contains("lib/armeabi/gdb.setup")
+        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
+        assertThatZip(apk).contains("lib/armeabi-v7a/gdbserver")
+        assertThatZip(apk).contains("lib/armeabi-v7a/gdb.setup")
+    }
+
+    @Test
+    public void "check release build does not contain gdbserver and gdb.setup"() {
+        project.execute("assembleArmRelease")
+
+        File apk = project.getApk("arm", "release", "unsigned")
+        assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/armeabi/gdbserver")
+        assertThatZip(apk).doesNotContain("lib/armeabi/gdb.setup")
+        assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
+        assertThatZip(apk).doesNotContain("lib/armeabi-v7a/gdbserver")
+        assertThatZip(apk).doesNotContain("lib/armeabi-v7a/gdb.setup")
     }
 
     @Test
     @Category(DeviceTests.class)
     public void connectedAndroidTest() {
-        project.execute("connectedAndroidTestArmDebug");
+        if (GradleTestProject.DEVICE_PROVIDER_NAME.equals(BuilderConstants.CONNECTED)) {
+            project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "ArmDebugAndroidTest")
+        } else {
+            project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "X86DebugAndroidTest")
+        }
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkFlagsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkFlagsTest.groovy
new file mode 100644
index 0000000..d21d8cd
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkFlagsTest.groovy
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 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.integration.component
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+
+/**
+ * Tests C/C++/ld flags in an NDK project.
+ */
+@CompileStatic
+class NdkFlagsTest {
+
+    static AndroidTestApp cApp = new HelloWorldJniApp()
+    static {
+        TestSourceFile orig = cApp.getFile("hello-jni.c")
+        cApp.removeFile(orig)
+        cApp.addFile(new TestSourceFile(orig.path, orig.name,
+                """
+#include <string.h>
+#include <jni.h>
+
+// This is a trivial JNI example where we use a native method
+// to return a new VM String.
+jstring
+Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
+{
+    return (*env)->NewStringUTF(env, HELLO_WORLD EXCLAMATION_MARK);
+}
+"""
+        ))
+    }
+
+
+    @ClassRule
+    public static GradleTestProject cProject = GradleTestProject.builder()
+            .withName("c_project")
+            .fromTestApp(cApp)
+            .forExpermimentalPlugin(true)
+            .create();
+
+    static AndroidTestApp cppApp = new HelloWorldJniApp(useCppSource: true)
+    static {
+        TestSourceFile orig = cppApp.getFile("hello-jni.cpp")
+        cppApp.removeFile(orig)
+        cppApp.addFile(new TestSourceFile(orig.path, orig.name,
+                """
+#include <string.h>
+#include <jni.h>
+
+// This is a trivial JNI example where we use a native method
+// to return a new VM String.
+extern "C"
+jstring
+Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
+{
+    // HELLO_WORLD and EXCLAMATION_MARK must be defined as follows during compilation.
+    // #define HELLO_WORLD "hello world"
+    // #define EXCLAMATION_MARK "!"
+    return env->NewStringUTF(HELLO_WORLD EXCLAMATION_MARK);
+}
+"""
+        ))
+
+    }
+
+    @ClassRule
+    public static GradleTestProject cppProject = GradleTestProject.builder()
+            .withName("cpp_project")
+            .fromTestApp(cppApp)
+            .forExpermimentalPlugin(true)
+            .create();
+
+    static AndroidTestApp ldApp = new HelloWorldJniApp()
+    static {
+        ldApp.addFile(new TestSourceFile("src/main/jni", "log.c",
+                """
+#include <android/log.h>
+
+// Simple function that uses function from an external library.  Should fail unless -llog is set
+// when linking.
+void log() {
+    __android_log_print(ANDROID_LOG_INFO, "hello-world", "Hello World!");
+}
+"""))
+    }
+
+    @ClassRule
+    public static GradleTestProject ldProject = GradleTestProject.builder()
+            .withName("ld_project")
+            .fromTestApp(ldApp)
+            .forExpermimentalPlugin(true)
+            .create();
+
+
+    @BeforeClass
+    public static void setUp() {
+        cProject.getBuildFile() << """
+apply plugin: 'com.android.model.application'
+
+model {
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    }
+    android.ndk {
+        moduleName = "hello-jni"
+        CFlags += ['-DHELLO_WORLD="hello world"', '-DEXCLAMATION_MARK="!"']
+        CFlags += ' -DFLAG_WITH_LEADING_SPACE'
+    }
+}
+"""
+
+        cppProject.getBuildFile() << """
+apply plugin: 'com.android.model.application'
+
+model {
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    }
+    android.ndk {
+        moduleName = "hello-jni"
+        cppFlags = ['-DHELLO_WORLD="hello world"', '-DEXCLAMATION_MARK="!"']
+    }
+}
+"""
+
+        ldProject.getBuildFile() << """
+apply plugin: 'com.android.model.application'
+
+model {
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    }
+    android.ndk {
+        moduleName = "hello-jni"
+        ldFlags += "-llog"
+    }
+}
+"""
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        cProject = null
+    }
+
+    @Test
+    public void "assemble C project"() {
+        cProject.execute("assembleDebug")
+        assertThatZip(cProject.getApk("debug")).contains("lib/x86/libhello-jni.so")
+    }
+
+    @Test
+    public void "assemble C++ project"() {
+        cppProject.execute("assembleDebug")
+        assertThatZip(cppProject.getApk("debug")).contains("lib/x86/libhello-jni.so")
+    }
+
+    @Test
+    public void "assemble ld project"() {
+        ldProject.execute("assembleDebug")
+        assertThatZip(ldProject.getApk("debug")).contains("lib/x86/libhello-jni.so")
+    }
+
+    @Test
+    @Category(DeviceTests.class)
+    public void "connectedCheck C project"() {
+        cProject.executeConnectedCheck();
+    }
+
+    @Test
+    @Category(DeviceTests.class)
+    public void "connectedCheck C++ project"() {
+        cppProject.executeConnectedCheck();
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy
index 1e2ac01..caeef48 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
  * Assemble tests for ndkJniLib2.
  */
 @Ignore("Test is disabled until new dependency model is ready.")
+@CompileStatic
 class NdkJniLib2Test {
 
     @ClassRule
@@ -54,6 +56,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck");
+        project.executeConnectedCheck();
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy
index cf40e68..feca3d1 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy
@@ -16,33 +16,46 @@
 
 package com.android.build.gradle.integration.component
 
+import com.android.SdkConstants
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
 /**
  * Assemble tests for ndkSanAngeles2.
  */
+@CompileStatic
 class NdkSanAngeles2Test {
 
     @ClassRule
-    static public GradleTestProject project = GradleTestProject.builder()
+    public static GradleTestProject project = GradleTestProject.builder()
             .forExpermimentalPlugin(true)
             .fromTestProject("ndkSanAngeles2")
             .create()
 
+    private static AndroidProject model;
+
     @BeforeClass
     static void setUp() {
-        project.execute("clean", "assembleDebug");
+        model = project.executeAndReturnModel("clean", "assembleDebug")
     }
 
     @AfterClass
     static void cleanUp() {
         project = null
+        model = null
     }
 
     @Test
@@ -51,8 +64,35 @@
     }
 
     @Test
+    void "check model"() {
+        Collection<Variant> variants = model.getVariants()
+        assertThat(variants).hasSize(8)
+
+        Variant debugVariant = ModelHelper.getVariant(variants, "x86Debug")
+        AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+        assertThat(debugMainArtifact.getNativeLibraries()).hasSize(1)
+
+        NativeLibrary nativeLibrary = debugMainArtifact.getNativeLibraries().first()
+        assertThat(nativeLibrary.getName()).isEqualTo("sanangeles")
+        assertThat(nativeLibrary.getToolchainName()).isEqualTo("clang-x86")
+        assertThat(nativeLibrary.getCCompilerFlags()).contains("-DDISABLE_IMPORTGL");
+        assertThat(nativeLibrary.getCSystemIncludeDirs()).isEmpty();
+        assertThat(nativeLibrary.getCppSystemIncludeDirs()).isNotEmpty()
+        File solibSearchPath = nativeLibrary.getDebuggableLibraryFolders().first()
+        assertThat(new File(solibSearchPath, "libsanangeles.so")).exists()
+
+        Collection<String> toolchainNames = model.getNativeToolchains().collect { it.getName() }
+        Collection<String> expectedToolchains = [
+                SdkConstants.ABI_INTEL_ATOM,
+                SdkConstants.ABI_ARMEABI_V7A,
+                SdkConstants.ABI_ARMEABI,
+                SdkConstants.ABI_MIPS].collect { "clang-" + it }
+        assertThat(toolchainNames).containsAllIn(expectedToolchains)
+    }
+
+    @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck");
+        project.executeConnectedCheck();
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy
index 84cf84c..ff8938a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -29,6 +30,7 @@
  * Assemble tests for ndkStandaloneSo.
  */
 @Ignore("Test is disabled until new dependency model is ready.")
+@CompileStatic
 class NdkStandaloneSoTest {
 
     @ClassRule
@@ -54,6 +56,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck");
+        project.executeConnectedCheck();
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy
index 67386bb..dfded44 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
 import org.gradle.tooling.BuildException
 import org.junit.Before
 import org.junit.Rule
@@ -36,6 +37,7 @@
  * This unit test is parameterized and will be executed for various values of STL.
  */
 @RunWith(Parameterized.class)
+@CompileStatic
 public class NdkStlTest {
 
     @Parameterized.Parameters
@@ -62,7 +64,7 @@
 
     @Rule
     public GradleTestProject project = GradleTestProject.builder()
-            .fromTestApp(new HelloWorldJniApp(jniDir: "cpp", useCppSource: true))
+            .fromTestApp(new HelloWorldJniApp(useCppSource: true))
             .forExpermimentalPlugin(true)
             .create()
 
@@ -72,12 +74,12 @@
 apply plugin: 'com.android.model.application'
 
 model {
-    android.config {
-        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    android {
+        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
     }
     android.ndk {
-        moduleName "hello-jni"
+        moduleName = "hello-jni"
     }
 }
 """
@@ -88,7 +90,7 @@
         project.getBuildFile() << """
 model {
     android.ndk {
-        stl "$stl"
+        stl = "$stl"
     }
 }
 """
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy
index 5e90883..fe48955 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy
@@ -16,15 +16,19 @@
 
 package com.android.build.gradle.integration.component
 
+import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
+import org.junit.experimental.categories.Category
 
 /**
  * Assemble tests for ndkVariants.
  */
+@CompileStatic
 class NdkVariantsTest {
 
     @ClassRule
@@ -47,4 +51,10 @@
     void lint() {
         project.execute("lint")
     }
+
+    @Test
+    @Category(DeviceTests.class)
+    public void connnectedAndroidTest() {
+        project.executeConnectedCheck();
+    }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy
index 027fe64..f4d74de 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy
@@ -63,7 +63,7 @@
 
     @Test
     void "check model failed to load"() {
-        SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+        SyncIssue issue = assertThat(model).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_NON_JAR_LOCAL_DEP)
         assertThat(new File(issue.getData()).getName()).is('baseLib-1.0.aar')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy
index 9600a2b..18cb14e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy
@@ -65,7 +65,7 @@
 
     @Test
     void "check model failed to load"() {
-        assertThat(models.get(':app')).issues().hasSingleIssue(
+        assertThat(models.get(':app')).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_JAR_DEPEND_ON_AAR,
                 'projectWithModules:jar:jar:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy
index 2d047fd..7ddca09 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy
@@ -15,20 +15,17 @@
  */
 
 package com.android.build.gradle.integration.dependencies
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.SyncIssue
-import com.google.common.collect.Iterables
 import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 
-import static org.junit.Assert.assertEquals
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
 import static org.junit.Assert.assertTrue
-
 /**
  * test for flavored dependency on a different package.
  */
@@ -88,10 +85,9 @@
 
     @Test
     void "check we received a sync issue"() {
-        assertEquals(1, models.get(":app").getSyncIssues().size());
-        SyncIssue syncIssue = Iterables.getOnlyElement(models.get(":app").getSyncIssues());
-        assertEquals(SyncIssue.SEVERITY_ERROR, syncIssue.getSeverity());
-        assertEquals(SyncIssue.TYPE_UNRESOLVED_DEPENDENCY, syncIssue.getType());
-        assertTrue(syncIssue.message.contains("org.jdeferred:jdeferred-android-aar:-1.-1.-1"));
+        SyncIssue issue = assertThat(models.get(":app")).hasSingleIssue(
+                SyncIssue.SEVERITY_ERROR,
+                SyncIssue.TYPE_UNRESOLVED_DEPENDENCY)
+        assertTrue(issue.message.contains("org.jdeferred:jdeferred-android-aar:-1.-1.-1"));
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy
index e6ad2f2..89eff2e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy
@@ -56,7 +56,7 @@
 
     @Test
     void "check model failed to load"() {
-        assertThat(models.get(':app')).issues().hasSingleIssue(
+        assertThat(models.get(':app')).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_NON_JAR_PACKAGE_DEP,
                 'projectWithModules:library:aar:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy
index bacd296..c7a7d18 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy
@@ -63,7 +63,7 @@
 
     @Test
     void "check model failed to load"() {
-        SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+        SyncIssue issue = assertThat(model).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_NON_JAR_LOCAL_DEP)
         assertThat(new File(issue.getData()).getName()).is('baseLib-1.0.aar')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy
index ff961ac..14ac3c6 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy
@@ -56,7 +56,7 @@
 
     @Test
     void "check model failed to load"() {
-        assertThat(models.get(':app')).issues().hasSingleIssue(
+        assertThat(models.get(':app')).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_NON_JAR_PROVIDED_DEP,
                 'projectWithModules:library:aar:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy
index b378f64..ccf23b5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy
@@ -63,7 +63,7 @@
 
     @Test
     void "check model failed to load"() {
-        SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+        SyncIssue issue = assertThat(model).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_NON_JAR_LOCAL_DEP)
         assertThat(new File(issue.getData()).getName()).is('baseLib-1.0.aar')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/LocalJarInAarInModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/LocalJarInAarInModelTest.groovy
new file mode 100644
index 0000000..efa71de
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/LocalJarInAarInModelTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 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.integration.dependencies
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.truth.TruthHelper
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidLibrary
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Dependencies
+import com.android.builder.model.Variant
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+/**
+ * test for the path of the local jars in aars before and after exploding them.
+ */
+class LocalJarInAarInModelTest {
+
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldApp())
+            .create()
+
+    @Before
+    void setUp() {
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+  compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+  buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+  defaultConfig {
+    minSdkVersion 4
+  }
+}
+
+dependencies {
+  compile 'com.android.support:support-v4:22.1.1'
+}
+"""
+    }
+
+    @After
+    void cleanUp() {
+        project = null
+    }
+
+    @Test
+    void checkModelBeforeBuild() {
+        //clean the project and get the model. The aar won't be exploded for this sync event.
+        AndroidProject model = project.executeAndReturnModel("clean")
+
+        Variant variant = ModelHelper.getVariant(model.getVariants(), "debug")
+        Dependencies dependencies = variant.getMainArtifact().getDependencies()
+        Collection<AndroidLibrary> libraries = dependencies.getLibraries();
+
+        TruthHelper.assertThat(libraries).hasSize(1);
+
+        // now build the project.
+        project.execute("prepareDebugDependencies")
+
+        // now check the model validity
+        AndroidLibrary lib = libraries.iterator().next()
+
+        File jarFile = lib.getJarFile()
+        assertTrue("File doesn't exist: " + jarFile, jarFile.exists());
+        for (File localJar : lib.getLocalJars()) {
+            assertTrue("File doesn't exist: " + localJar, localJar.exists());
+        }
+    }
+
+    @Test
+    void checkModelAfterBuild() {
+        //build the project and get the model. The aar is exploded for this sync event.
+        AndroidProject model = project.executeAndReturnModel("clean", "prepareDebugDependencies")
+
+        Variant variant = ModelHelper.getVariant(model.getVariants(), "debug")
+        Dependencies dependencies = variant.getMainArtifact().getDependencies()
+        Collection<AndroidLibrary> libraries = dependencies.getLibraries();
+
+        TruthHelper.assertThat(libraries).hasSize(1);
+
+        // now check the model validity
+        AndroidLibrary lib = libraries.iterator().next()
+
+        File jarFile = lib.getJarFile()
+        assertTrue("File doesn't exist: " + jarFile, jarFile.exists());
+        for (File localJar : lib.getLocalJars()) {
+            assertTrue("File doesn't exist: " + localJar, localJar.exists());
+        }
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/OptionalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/OptionalAarTest.groovy
new file mode 100644
index 0000000..7faf203
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/OptionalAarTest.groovy
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 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.integration.dependencies
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidLibrary
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Dependencies
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatAar
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+/**
+ * test for optional aar (using the provided scope)
+ */
+@CompileStatic
+class OptionalAarTest {
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("projectWithModules")
+            .create()
+    static Map<String, AndroidProject> models
+
+    @BeforeClass
+    static void setUp() {
+        project.getSubproject('app').getBuildFile() << """
+
+dependencies {
+    compile project(':library')
+}
+"""
+        project.getSubproject('library').getBuildFile() << """
+
+dependencies {
+    provided project(':library2')
+}
+"""
+        models = project.executeAndReturnMultiModel("clean", ":app:assembleDebug")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+        models = null
+    }
+
+    @Test
+    void "check app doesn't contain provided lib's layout"() {
+        File apk = project.getSubproject('app').getApk("debug")
+
+        assertThatApk(apk).doesNotContainResource("layout/lib2layout.xml")
+    }
+
+    @Test
+    void "check app doesn't contain provided lib's code"() {
+        File apk = project.getSubproject('app').getApk("debug")
+
+        assertThatApk(apk).doesNotContainClass("Lcom/example/android/multiproject/library2/PersonView2;")
+    }
+
+    @Test
+    void "check lib doesn't contain provided lib's layout"() {
+        File aar = project.getSubproject('library').getAar("release")
+
+        assertThatAar(aar).doesNotContainResource("layout/lib2layout.xml")
+        assertThatAar(aar).textSymbolFile().contains("int layout liblayout")
+        assertThatAar(aar).textSymbolFile().doesNotContain("int layout lib2layout")
+    }
+
+    @Test
+    void "check app model doesn't include optional library"() {
+        Collection<Variant> variants = models.get(":app").getVariants()
+
+        // get the main artifact of the debug artifact and its dependencies
+        Variant variant = ModelHelper.getVariant(variants, "debug")
+        AndroidArtifact artifact = variant.getMainArtifact()
+        Dependencies dependencies = artifact.getDependencies()
+        Collection<AndroidLibrary> libs = dependencies.getLibraries();
+
+        assertThat(libs).hasSize(1);
+
+        AndroidLibrary library = libs.first()
+        assertThat(library.getProject()).isEqualTo(":library")
+        assertThat(library.isOptional()).isFalse()
+    }
+
+    @Test
+    void "check library model includes optional library"() {
+        Collection<Variant> variants = models.get(":library").getVariants()
+
+        // get the main artifact of the debug artifact and its dependencies
+        Variant variant = ModelHelper.getVariant(variants, "debug")
+        AndroidArtifact artifact = variant.getMainArtifact()
+        Dependencies dependencies = artifact.getDependencies()
+        Collection<AndroidLibrary> libs = dependencies.getLibraries();
+
+        assertThat(libs).hasSize(1);
+
+        AndroidLibrary library = libs.first()
+        assertThat(library.getProject()).isEqualTo(":library2")
+        assertThat(library.isOptional()).isTrue()
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy
index b19566a..c32bfe8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy
@@ -18,6 +18,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.SyncIssue
+import groovy.transform.CompileStatic
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -26,10 +27,10 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertTrue
 import static org.junit.Assert.fail
-
 /**
  * Tests the handling of test dependencies.
  */
+@CompileStatic
 class TestWithMismatchDep {
 
     @Rule
@@ -55,7 +56,7 @@
         // Query the model to get the mismatch dep sync error.
         AndroidProject model = project.getSingleModelIgnoringSyncIssues()
 
-        assertThat(model).issues().hasSingleIssue(
+        assertThat(model).hasSingleIssue(
                 SyncIssue.SEVERITY_ERROR,
                 SyncIssue.TYPE_MISMATCH_DEP,
                 'com.google.guava:guava',
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy
index 60d5b1f..435b370 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.dependencies
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Tests the handling of test dependency.
  */
+@CompileStatic
 class TestWithSameDepAsApp {
 
     @ClassRule
@@ -59,6 +61,6 @@
     @Test
     @Category(DeviceTests.class)
     void "run tests on devices"() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy
index 7eddff6..eaea1ad 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy
@@ -18,6 +18,7 @@
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -26,6 +27,7 @@
 /**
  * Tests the handling of test dependency.
  */
+@CompileStatic
 class TestWithSameDepAsAppWithProguard {
 
     private static AndroidTestApp testApp = new HelloWorldApp()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy
index d827983..36b2e20 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy
@@ -33,6 +33,7 @@
 import com.android.ide.common.process.ProcessExecutor
 import com.android.utils.StdLogger
 import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -44,6 +45,7 @@
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
 
+@CompileStatic
 class VariantDependencyTest {
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy
index d1fddd8..24f84bc 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.dsl
-
 import com.android.build.gradle.integration.application.BuildConfigTest
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
@@ -26,7 +25,6 @@
 
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
-
 /**
  * General DSL tests
  */
@@ -135,7 +133,7 @@
   public static final String BUILD_TYPE = "debug";
   public static final String FLAVOR = "";
   public static final int VERSION_CODE = 1;
-  public static final String VERSION_NAME = "";
+  public static final String VERSION_NAME = "1.0";
   // Fields from default config.
   public static final String test2 = "Ä…";
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy
index a2880ec..25e0250 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -25,6 +26,7 @@
 /**
  * Test that the test variant returns what it should.
  */
+@CompileStatic
 class TestedVariantTest {
 
     @Rule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/BasicTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/BasicTest.groovy
new file mode 100644
index 0000000..ae60782
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/BasicTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 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.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Basic test with gcm + ga
+ */
+@CompileStatic
+class BasicTest {
+
+    public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.free")
+
+    private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "basic")
+    static {
+        File source = new File(resDataFolder, "example.json")
+        helloWorldApp.addFile(new TestSourceFile(
+                "",
+                TestHelper.JSON_FILE_NAME,
+                Files.toString(source, Charsets.UTF_8)))
+    }
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+        .fromTestApp(helloWorldApp)
+        .create()
+
+    public static AndroidProject model
+    private static File generatedResFolder
+
+    @BeforeClass
+    public static void setUp() {
+
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+        model = project.executeAndReturnModel("clean", "assembleDebug")
+
+        generatedResFolder = new File(project.getTestDir(),
+                Joiner.on(File.separator).join("build", "generated", "res", "google-services", "debug"))
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        project = null
+        model = null
+    }
+
+    @Test
+    public void "test values res file is generated"() {
+        File valuesFolder = new File(generatedResFolder, "values")
+        File values = new File(valuesFolder, "values.xml")
+        Assert.assertTrue(values.isFile())
+
+        File goldenFile = new File(resDataFolder, "values.xml")
+        Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+                .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+    }
+
+    @Test
+    public void "test ga res file is generated"() {
+        File xmlFolder = new File(generatedResFolder, "xml")
+        File global_tracker = new File(xmlFolder, "global_tracker.xml")
+        Assert.assertTrue(global_tracker.isFile())
+
+        File goldenFile = new File(resDataFolder, "global_tracker.xml")
+        Truth.assert_().that(Files.toString(global_tracker, Charsets.UTF_8).trim())
+                .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+    }
+
+    @Test
+    public void "test generated res folder is in model"() {
+        Variant debugVariant = ModelHelper.getVariant(model.getVariants(), "debug");
+        Collection<File> generatedResFolders = debugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+        Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/DisabledServiceTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/DisabledServiceTest.groovy
new file mode 100644
index 0000000..055690e
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/DisabledServiceTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Test for mostly empty json file.
+ */
+@CompileStatic
+class DisabledServiceTest {
+
+    public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.free")
+
+    private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "disabledservice")
+    static {
+        File source = new File(resDataFolder, "example.json")
+        helloWorldApp.addFile(new TestSourceFile(
+                "",
+                TestHelper.JSON_FILE_NAME,
+                Files.toString(source, Charsets.UTF_8)))
+    }
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+        .fromTestApp(helloWorldApp)
+        .create()
+
+    public static AndroidProject model
+    private static File generatedResFolder
+
+    @BeforeClass
+    public static void setUp() {
+
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+        model = project.executeAndReturnModel("clean", "assembleDebug")
+        generatedResFolder = new File(project.getTestDir(),
+                Joiner.on(File.separator).join("build", "generated", "res", "google-services", "debug"))
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        project = null
+
+    }
+    @Test
+    public void "test values res file is generated"() {
+        File valuesFolder = new File(generatedResFolder, "values")
+        File values = new File(valuesFolder, "values.xml")
+        Assert.assertTrue(values.isFile())
+
+        File goldenFile = new File(resDataFolder, "values.xml")
+        Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+                .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+    }
+
+    @Test
+    public void "test generated res folder is in model"() {
+        Variant debugVariant = ModelHelper.getVariant(model.getVariants(), "debug");
+        Collection<File> generatedResFolders = debugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+        Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/FlavorTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/FlavorTest.groovy
new file mode 100644
index 0000000..4e727b0
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/FlavorTest.groovy
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Basic test with gcm + ga
+ */
+@CompileStatic
+class FlavorTest {
+
+    public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app")
+
+    private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "flavor")
+    static {
+        File source = new File(resDataFolder, "example.json")
+        helloWorldApp.addFile(new TestSourceFile(
+                "",
+                TestHelper.JSON_FILE_NAME,
+                Files.toString(source, Charsets.UTF_8)))
+    }
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+        .fromTestApp(helloWorldApp)
+        .create()
+
+    public static AndroidProject model
+    private static File generatedFreeResFolder
+    private static File generatedPaidResFolder
+
+    @BeforeClass
+    public static void setUp() {
+
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+    productFlavors {
+        free {
+            applicationId 'com.example.app.free'
+        }
+        paid {
+            applicationId 'com.example.app.paid'
+        }
+    }
+
+}
+"""
+        model = project.executeAndReturnModel("clean", "assembleDebug")
+
+        generatedFreeResFolder = new File(project.getTestDir(),
+                Joiner.on(File.separator).join("build", "generated", "res", "google-services", "free", "debug"))
+        generatedPaidResFolder = new File(project.getTestDir(),
+                Joiner.on(File.separator).join("build", "generated", "res", "google-services", "paid", "debug"))
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        project = null
+        model = null
+    }
+
+    @Test
+    public void "test values res file is generated"() {
+        checkResValuesFile(generatedFreeResFolder, "free.values.xml")
+        checkResValuesFile(generatedPaidResFolder, "paid.values.xml")
+    }
+
+    private static void checkResValuesFile(File generatedResFolder, String goldenFileName) {
+        File valuesFolder = new File(generatedResFolder, "values")
+        File values = new File(valuesFolder, "values.xml")
+        Assert.assertTrue(values.isFile())
+
+        File goldenFile = new File(resDataFolder, goldenFileName)
+        Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+                .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+    }
+
+    @Test
+    public void "test ga res file is generated"() {
+        checkGlobalTracker(generatedFreeResFolder, "free.global_tracker.xml")
+        checkGlobalTracker(generatedPaidResFolder, "paid.global_tracker.xml")
+    }
+
+    private static void checkGlobalTracker(File generatedResFolder, String goldenFileName) {
+        File xmlFolder = new File(generatedResFolder, "xml")
+        File global_tracker = new File(xmlFolder, "global_tracker.xml")
+        Assert.assertTrue(global_tracker.isFile())
+
+        File goldenFile = new File(resDataFolder, goldenFileName)
+        Truth.assert_().that(Files.toString(global_tracker, Charsets.UTF_8).trim())
+                .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+    }
+
+    @Test
+    public void "test generated res folder is in model"() {
+        checkModel("freeDebug", generatedFreeResFolder)
+        checkModel("paidDebug", generatedPaidResFolder)
+    }
+
+    private static void checkModel(String variantName, File generatedResFolder) {
+        Variant freeDebugVariant = ModelHelper.getVariant(model.getVariants(), variantName);
+        Collection<File> generatedResFolders = freeDebugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+        Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoClientTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoClientTest.groovy
new file mode 100644
index 0000000..7419b44
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoClientTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 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.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Test with a mismatch json file vs the app package name.
+ */
+@CompileStatic
+class NoClientTest {
+
+    public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.typo")
+
+    static {
+        File source = new File(new File(GradleTestProject.TEST_RES_DIR, "basic"), "example.json")
+        helloWorldApp.addFile(new TestSourceFile(
+                "",
+                TestHelper.JSON_FILE_NAME,
+                Files.toString(source, Charsets.UTF_8)))
+    }
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+        .fromTestApp(helloWorldApp)
+        .captureStdOut(true)
+        .create()
+
+    @BeforeClass
+    public static void setUp() {
+
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+        project.execute("clean", "assembleDebug")
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        project = null
+    }
+
+    @Test
+    public void "test warning is output"() {
+        ByteArrayOutputStream stream = project.getStdout()
+
+        Truth.assert_().that(stream.toString("UTF-8")).contains(
+                "No matching client found for package name 'com.example.app.typo'")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoServiceTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoServiceTest.groovy
new file mode 100644
index 0000000..5772f19
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoServiceTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 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.integration.googleservices
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+/**
+ * Test for mostly empty json file.
+ */
+@CompileStatic
+class NoServiceTest {
+
+    public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.free")
+
+    private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "noservice")
+    static {
+        File source = new File(resDataFolder, "no_services.json")
+        helloWorldApp.addFile(new TestSourceFile(
+                "",
+                TestHelper.JSON_FILE_NAME,
+                Files.toString(source, Charsets.UTF_8)))
+    }
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+        .fromTestApp(helloWorldApp)
+        .create()
+
+    public static AndroidProject model
+    private static File generatedResFolder
+
+    @BeforeClass
+    public static void setUp() {
+
+        project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+        model = project.executeAndReturnModel("clean", "assembleDebug")
+        generatedResFolder = new File(project.getTestDir(),
+                Joiner.on(File.separator).join("build", "generated", "res", "google-services", "debug"))
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        project = null
+
+    }
+    @Test
+    public void "test values res file is generated"() {
+        File valuesFolder = new File(generatedResFolder, "values")
+        File values = new File(valuesFolder, "values.xml")
+        Assert.assertTrue(values.isFile())
+
+        File goldenFile = new File(resDataFolder, "values.xml")
+        Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+                .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+    }
+
+    @Test
+    public void "test generated res folder is in model"() {
+        Variant debugVariant = ModelHelper.getVariant(model.getVariants(), "debug");
+        Collection<File> generatedResFolders = debugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+        Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/TestHelper.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/TestHelper.java
new file mode 100644
index 0000000..0ec42b8
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/TestHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.integration.googleservices;
+
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.List;
+
+/**
+ */
+public class TestHelper {
+
+    public static final String JSON_FILE_NAME = "google-services.json";
+
+    public static File getResourceFolder2(String... pathSegments) {
+        CodeSource source = TestHelper.class.getProtectionDomain().getCodeSource();
+        if (source != null) {
+            URL location = source.getLocation();
+            try {
+                File dir = new File(location.toURI());
+                Assert.assertTrue(dir.getPath(), dir.exists());
+
+                dir = dir.getParentFile().getParentFile().getParentFile();
+
+                List<String> segments = Lists.newArrayList("src", "test", "resources");
+                if (pathSegments != null) {
+                    segments.addAll(Lists.newArrayList(pathSegments));
+                }
+
+                return new File(
+                        dir,
+                        Joiner.on(File.separator).join(segments));
+            } catch (URISyntaxException e) {
+                fail(e.getLocalizedMessage());
+            }
+        }
+
+        fail("Fail to get the test resource folder");
+
+        return null;
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy
index 288c688..9ccd2d6 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.library
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for aidl.
  */
+@CompileStatic
 class AidlTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy
index 1f9c389..fc61bb2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy
@@ -18,15 +18,20 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
 /**
  * Assemble tests for api.
  */
+@CompileStatic
 class ApiTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +56,16 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
+    }
+
+    @Test
+    public void backwardsCompatible() throws Exception {
+        // ATTENTION Author and Reviewers - please make sure required changes to the build file
+        // are backwards compatible before updating this test.
+        assertThat(FileUtils.sha1(project.file("app/build.gradle")))
+                .isEqualTo("e20b70879b449c222ce9c0f9f17cb808d6899b06")
+        assertThat(FileUtils.sha1(project.file("lib/build.gradle")))
+                .isEqualTo("fbdd1f6d0d0e190412db885a1c281efee4ab0cac")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy
index 54c09db..dcc0f24 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for applibtest.
  */
+@CompileStatic
 class ApplibtestTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy
index 38b4a8d..fae6db3 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for assets.
  */
+@CompileStatic
 class AssetsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy
index 0c981ac..f4445eb 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for attrOrder.
  */
+@CompileStatic
 class AttrOrderTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy
index 6f0a676..5e9802c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.library
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
 import org.gradle.tooling.BuildException
 import org.junit.Before
 import org.junit.Rule
@@ -25,6 +26,7 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.fail
 
+@CompileStatic
 class DslTest {
 
     @Rule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy
index ccf8ff3..55a81f5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy
@@ -23,6 +23,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.ProductFlavorContainer
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -36,6 +37,7 @@
 /**
  * Assemble tests for flavoredlib.
  */
+@CompileStatic
 class FlavoredlibTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -111,6 +113,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy
index 7acb0a2..b12a565 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy
@@ -24,6 +24,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.ProductFlavorContainer
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.Assert
 import org.junit.BeforeClass
@@ -39,6 +40,7 @@
 /**
  * Assemble tests for flavorlib.
  */
+@CompileStatic
 class FlavorlibTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -118,6 +120,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy
index fd569fd..cd37449 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy
@@ -16,6 +16,7 @@
 
 package com.android.build.gradle.integration.library
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -23,6 +24,7 @@
 
 import static org.junit.Assert.assertFalse
 
+@CompileStatic
 class GenerateAnnotationsClassPathTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy
index 1f9e364..e072ed0 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for libMinifyJarDep.
  */
+@CompileStatic
 class LibMinifyJarDepTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy
index 89d4356..06f97fa 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for libMinifyLibDep.
  */
+@CompileStatic
 class LibMinifyLibDepTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy
index b29df0e..f2d552d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.FileHelper
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -26,6 +27,7 @@
 /**
  * Assemble tests for libMinify.
  */
+@CompileStatic
 class LibMinifyTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy
index 5db0cac..7c85dc8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.FileHelper
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -28,6 +29,7 @@
 /**
  * Assemble tests for libProguarConsumerFiles.
  */
+@CompileStatic
 class LibProguardConsumerFilesTest {
 
     @ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy
index 9464e16..591fe2c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.library
-
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.utils.ModelHelper
@@ -24,6 +23,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.JavaLibrary
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -34,12 +34,11 @@
 import static com.android.builder.model.AndroidProject.ARTIFACT_ANDROID_TEST
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
-import static org.junit.Assert.assertThat
 import static org.junit.Assert.assertTrue
-
 /**
  * Assemble tests for libTestDep.
  */
+@CompileStatic
 class LibTestDepTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -91,6 +90,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy
index 9f64770..8372aac 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for libsTest.
  */
+@CompileStatic
 class LibsTestTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy
index 3a1b343..6d19d2f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.library
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for localAarTest.
  */
+@CompileStatic
 class LocalAarTestTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy
index 8627580..9133eef 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy
@@ -21,6 +21,7 @@
 import com.android.builder.model.Dependencies
 import com.android.builder.model.JavaLibrary
 import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -31,6 +32,7 @@
 /**
  * Assemble tests for localJars.
  */
+@CompileStatic
 class LocalJarsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy
index 27ce57e..8f10a52 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for minifyLib.
  */
+@CompileStatic
 class MinifyLibTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy
index acf86f6..3ff336f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for multiDexWithLib.
  */
+@CompileStatic
 class MultiDexWithLibTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -35,6 +37,7 @@
 
     @BeforeClass
     static void setUp() {
+        GradleTestProject.assumeBuildToolsAtLeast(21)
         project.execute("clean", "assembleDebug")
     }
 
@@ -51,6 +54,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy
index d8a604b..662043e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for multiproject.
  */
+@CompileStatic
 class MultiprojectTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,9 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheckAndReport() {
-        project.execute("connectedCheck", "mergeAndroidReports")
+        project.executeConnectedCheck()
+        // android-reporting plugin currently executes connected tasks.
+        GradleTestProject.assumeLocalDevice();
+        project.execute("mergeAndroidReports")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy
index 8420836..68a8600 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy
@@ -1,5 +1,4 @@
 package com.android.build.gradle.integration.library
-
 import com.android.SdkConstants
 import com.android.annotations.NonNull
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
@@ -22,7 +21,6 @@
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertNull
 import static org.junit.Assert.assertTrue
-
 /**
   * Integration test to check that libraries included directly as jar files are correctly handled
   * when using proguard.
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy
index b39d8a1..ac08c8e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy
@@ -17,6 +17,7 @@
 package com.android.build.gradle.integration.library
 
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -25,6 +26,7 @@
 /**
  * Assemble tests for renderscriptInLib.
  */
+@CompileStatic
 class RenderscriptInLibTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy
index 6340ef7..ab4fccf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,6 +28,7 @@
 /**
  * Assemble tests for sameNamedLibs.
  */
+@CompileStatic
 class SameNamedLibsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkConnectedCheckTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkConnectedCheckTest.groovy
new file mode 100644
index 0000000..264d5aa
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkConnectedCheckTest.groovy
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 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.integration.ndk
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.TestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+
+/**
+ * Test AndroidTest with NDK.
+ */
+class NdkConnectedCheckTest {
+
+    private static AndroidTestApp app = new HelloWorldJniApp();
+    static {
+        app.addFile(new TestSourceFile("src/androidTest/jni", "hello-jni-test.c",
+"""
+#include <string.h>
+#include <jni.h>
+
+jstring
+Java_com_example_hellojni_HelloJniTest_expectedString(JNIEnv* env, jobject thiz)
+{
+    return (*env)->NewStringUTF(env, "hello world!");
+}
+"""));
+        app.addFile(new TestSourceFile("src/androidTest/java/com/example/hellojni", "HelloJniTest.java",
+"""
+package com.example.hellojni;
+
+import android.test.ActivityInstrumentationTestCase;
+
+public class HelloJniTest extends ActivityInstrumentationTestCase<HelloJni> {
+
+    public HelloJniTest() {
+        super("com.example.hellojni", HelloJni.class);
+    }
+
+    // Get expected string from JNI.
+    public native String expectedString();
+
+    static {
+        System.loadLibrary("hello-jni_test");
+    }
+
+    public void testJniName() {
+        final HelloJni a = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(a);
+
+        assertTrue(expectedString().equals(a.stringFromJNI()));
+    }
+}
+"""));
+    }
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(app)
+            .addGradleProperties("android.useDeprecatedNdk=true")
+            .create()
+
+
+    @BeforeClass
+    static void setUp() {
+        project.getBuildFile() <<
+"""
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    defaultConfig {
+        ndk {
+            moduleName "hello-jni"
+        }
+    }
+}
+"""
+        project.execute("clean", "assembleAndroidTest");
+    }
+
+
+    @Test
+    void "check test lib is packaged"() {
+        File apk = project.getApk("debug", "androidTest", "unaligned")
+        assertThatZip(apk).contains("lib/x86/libhello-jni_test.so")
+    }
+
+    @Test
+    @Category(DeviceTests.class)
+    void connectedCheck() {
+        project.executeConnectedCheck()
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy
index 06f2019..8561323 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy
@@ -33,6 +33,7 @@
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("ndkJniLib")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
@@ -67,6 +68,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy
index 6aa62a6..ca42727 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy
@@ -18,6 +18,7 @@
 
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -27,10 +28,12 @@
 /**
  * Assemble tests for ndkLibPrebuilts.
  */
+@CompileStatic
 class NdkLibPrebuiltsTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("ndkLibPrebuilts")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
@@ -51,6 +54,6 @@
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkModelTest.groovy
new file mode 100644
index 0000000..b261142
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkModelTest.groovy
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 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.integration.ndk
+
+import com.android.SdkConstants
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
+import com.android.builder.model.Variant
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+/**
+ * Test the return model of the NDK.
+ */
+class NdkModelTest {
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromTestApp(new HelloWorldJniApp())
+            .addGradleProperties("android.useDeprecatedNdk=true")
+            .create()
+
+    @Before
+    void setUp() {
+        project.buildFile <<
+"""
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    defaultConfig {
+        ndk {
+            moduleName "hello-jni"
+            cFlags = "-DTEST_FLAG"
+        }
+    }
+}
+"""
+    }
+
+    @Test
+    void "check native libraries in model"() {
+        checkModel(
+                debug : [
+                    SdkConstants.ABI_ARMEABI,
+                    SdkConstants.ABI_ARMEABI_V7A,
+                    SdkConstants.ABI_ARM64_V8A,
+                    SdkConstants.ABI_INTEL_ATOM,
+                    SdkConstants.ABI_INTEL_ATOM64,
+                    SdkConstants.ABI_MIPS,
+                    SdkConstants.ABI_MIPS64
+                ]);
+    }
+
+    @Test
+    void "check native libraries with splits"() {
+        project.buildFile <<
+"""
+android {
+    splits {
+        abi {
+            enable true
+            reset()
+            include 'x86', 'armeabi-v7a', 'mips'
+        }
+    }
+}
+"""
+        checkModel(
+                debug: [SdkConstants.ABI_ARMEABI_V7A, SdkConstants.ABI_INTEL_ATOM, SdkConstants.ABI_MIPS]);
+    }
+
+    @Test
+    void "check native libraries with splits and universalApk"() {
+        project.buildFile <<
+                """
+android {
+    splits {
+        abi {
+            enable true
+            reset()
+            include 'x86', 'armeabi-v7a', 'mips'
+            universalApk true
+        }
+    }
+}
+"""
+        checkModel(
+                debug : [
+                        SdkConstants.ABI_ARMEABI,
+                        SdkConstants.ABI_ARMEABI_V7A,
+                        SdkConstants.ABI_ARM64_V8A,
+                        SdkConstants.ABI_INTEL_ATOM,
+                        SdkConstants.ABI_INTEL_ATOM64,
+                        SdkConstants.ABI_MIPS,
+                        SdkConstants.ABI_MIPS64
+                ]);
+    }
+
+    @Test
+    void "check native libraries with abiFilters"() {
+        project.buildFile <<
+                """
+android {
+    productFlavors {
+        x86 {
+            ndk {
+                abiFilter "x86"
+            }
+        }
+        arm {
+            ndk {
+                abiFilters "armeabi-v7a"
+            }
+        }
+        mips {
+            ndk {
+                abiFilter "mips"
+            }
+        }
+    }
+}
+"""
+        checkModel(
+                x86Debug : [SdkConstants.ABI_INTEL_ATOM],
+                armDebug : [SdkConstants.ABI_ARMEABI_V7A],
+                mipsDebug : [SdkConstants.ABI_MIPS]);
+    }
+
+    @Test
+    void "check using add on string for compileSdkVersion"() {
+        project.buildFile <<
+"""
+android {
+    compileSdkVersion "Google Inc.:Google APIs:$GradleTestProject.DEFAULT_COMPILE_SDK_VERSION"
+}
+"""
+        AndroidProject model = project.executeAndReturnModel("assembleDebug")
+        NativeLibrary lib = ModelHelper.getVariant(model.getVariants(), "debug").getMainArtifact()
+                .getNativeLibraries().first()
+        for (String flag : lib.getCCompilerFlags()) {
+            if (flag.contains("sysroot")) {
+                assertThat(flag).contains("android-${GradleTestProject.DEFAULT_COMPILE_SDK_VERSION}")
+            }
+        }
+    }
+
+    /**
+     * Verify resulting model is as expected.
+     *
+     * @param variantToolchains map of variant name to array of expected toolchains.
+     */
+    private void checkModel(Map variantToolchains) {
+
+        AndroidProject model = project.executeAndReturnModel("assembleDebug")
+
+        Collection<Variant> variants = model.getVariants()
+        for (Map.Entry entry : variantToolchains) {
+            Variant variant = ModelHelper.getVariant(variants, (String) entry.getKey())
+            AndroidArtifact mainArtifact = variant.getMainArtifact()
+
+            assertThat(mainArtifact.getNativeLibraries()).hasSize(((Collection)entry.getValue()).size())
+            for (NativeLibrary nativeLibrary : mainArtifact.getNativeLibraries()) {
+                assertThat(nativeLibrary.getName()).isEqualTo("hello-jni")
+                assertThat(nativeLibrary.getCCompilerFlags()).contains("-DTEST_FLAG");
+                assertThat(nativeLibrary.getCppCompilerFlags()).contains("-DTEST_FLAG");
+                assertThat(nativeLibrary.getCSystemIncludeDirs()).isEmpty();
+                assertThat(nativeLibrary.getCppSystemIncludeDirs()).isNotEmpty();
+                File solibSearchPath = nativeLibrary.getDebuggableLibraryFolders().first()
+                assertThat(new File(solibSearchPath, "libhello-jni.so")).exists()
+            }
+
+            Collection<String> expectedToolchainNames = entry.getValue().collect { "gcc-" + it }
+            Collection<String> toolchainNames = model.getNativeToolchains().collect { it.getName() }
+            assertThat(toolchainNames).containsAllIn(expectedToolchainNames)
+            Collection<String> nativeLibToolchains = mainArtifact.getNativeLibraries().
+                    collect { it.getToolchainName() }
+            assertThat(nativeLibToolchains).containsExactlyElementsIn(expectedToolchainNames)
+        }
+
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy
index 4dc9015..e8b283f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.ndk
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.builder.model.AndroidProject
 import com.android.builder.model.Variant
@@ -29,7 +28,6 @@
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
 import static org.junit.Assert.assertTrue
-
 /**
  * Assemble tests for ndkPrebuilts.
  */
@@ -37,6 +35,7 @@
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("ndkPrebuilts")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     static AndroidProject model
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy
index e9bba65..7c2952b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy
@@ -16,6 +16,7 @@
 
 package com.android.build.gradle.integration.ndk
 
+import com.android.SdkConstants
 import com.android.build.FilterData
 import com.android.build.OutputFile
 import com.android.build.gradle.integration.common.category.DeviceTests
@@ -24,14 +25,17 @@
 import com.android.builder.model.AndroidArtifact
 import com.android.builder.model.AndroidArtifactOutput
 import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
 import com.android.builder.model.Variant
 import com.google.common.collect.Maps
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 import org.junit.experimental.categories.Category
 
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
 import static com.android.builder.core.BuilderConstants.DEBUG
 import static org.junit.Assert.assertEquals
 import static org.junit.Assert.assertNotNull
@@ -40,10 +44,12 @@
 /**
  * Assemble tests for ndkSanAngeles.
  */
+@CompileStatic
 class NdkSanAngelesTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestProject("ndkSanAngeles")
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     static AndroidProject model
@@ -72,11 +78,11 @@
         // get the main artifact of the debug artifact
         Variant debugVariant = ModelHelper.getVariant(variants, DEBUG)
         assertNotNull("debug Variant null-check", debugVariant)
-        AndroidArtifact debugMainArficat = debugVariant.getMainArtifact()
-        assertNotNull("Debug main info null-check", debugMainArficat)
+        AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+        assertNotNull("Debug main info null-check", debugMainArtifact)
 
         // get the outputs.
-        Collection<AndroidArtifactOutput> debugOutputs = debugMainArficat.getOutputs()
+        Collection<AndroidArtifactOutput> debugOutputs = debugMainArtifact.getOutputs()
         assertNotNull(debugOutputs)
         assertEquals(3, debugOutputs.size())
 
@@ -107,10 +113,9 @@
         assertTrue(expected.isEmpty())
     }
 
-
     @Test
     @Category(DeviceTests.class)
     void connectedCheck() {
-        project.execute("connectedCheck")
+        project.executeConnectedCheck()
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy
index 37e2da0..85ec5be 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy
@@ -19,6 +19,8 @@
 import com.android.build.gradle.integration.common.category.DeviceTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.builder.core.BuilderConstants
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
@@ -33,11 +35,13 @@
 /**
  * Integration test of the native plugin with multiple variants without using splits.
  */
+@CompileStatic
 class NoSplitNdkVariantsTest {
 
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestApp(new HelloWorldJniApp())
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
@@ -124,6 +128,10 @@
     @Test
     @Category(DeviceTests.class)
     public void connectedAndroidTest() {
-        project.execute("connectedAndroidTestArmDebug")
+        if (GradleTestProject.DEVICE_PROVIDER_NAME.equals(BuilderConstants.CONNECTED)) {
+            project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "ArmDebugAndroidTest")
+        } else {
+            project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "X86DebugAndroidTest")
+        }
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy
index 9ff13a6..9a52db5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy
@@ -16,22 +16,28 @@
 
 package com.android.build.gradle.integration.ndk
 
+import com.android.build.gradle.integration.common.category.SmokeTests
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
+import org.junit.experimental.categories.Category
 
 import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
 
 /**
  * Test split DSL with API level < 21.
  */
+@Category(SmokeTests.class)
+@CompileStatic
 class Pre21SplitTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestApp(new HelloWorldJniApp())
+            .addGradleProperties("android.useDeprecatedNdk=true")
             .create()
 
     @BeforeClass
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleCodeChangeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleCodeChangeTest.groovy
new file mode 100644
index 0000000..203d742
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleCodeChangeTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.integration.performance
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_INC_JAVA
+/**
+ * Performance test for full and incremental build on ioschedule 2014
+ */
+@RunWith(Parameterized.class)
+@CompileStatic
+class IOScheduleCodeChangeTest {
+
+    @Parameterized.Parameters(name="minify={0} jack={1}")
+    public static Collection<Object[]> data() {
+        // returns an array of boolean for all combinations of (proguard, jack).
+        // Right now, only return the (false, false) and (false, true) cases.
+        return [
+//                [true, false].toArray(),
+//                [true, true].toArray(),
+                [false, false].toArray(),
+                [false, true].toArray(),
+        ];
+    }
+
+    private final boolean proguard
+    private final boolean jack
+
+    IOScheduleCodeChangeTest(boolean proguard, boolean jack) {
+        this.proguard = proguard
+        this.jack = jack
+    }
+
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromExternalProject("iosched")
+            .withJack(jack)
+            .withMinify(proguard)
+            .create()
+
+    @Before
+    public void setUp() {
+        project.executeWithBenchmark("iosched2014", BUILD_FULL, "clean" , "assembleDebug")
+    }
+
+    @After
+    void cleanUp() {
+        project = null;
+    }
+
+    @Test
+    void "Incremental Build on Java Change"() {
+        project.replaceLine(
+                "android/src/main/java/com/google/samples/apps/iosched/model/ScheduleItem.java",
+                30,
+                "    public long startTime = 1;")
+        project.executeWithBenchmark("iosched2014", BUILD_INC_JAVA, "assembleDebug")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleResChangeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleResChangeTest.groovy
new file mode 100644
index 0000000..8f96bd4
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleResChangeTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.integration.performance
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_INC_RES_ADD
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_INC_RES_EDIT
+
+/**
+ * Performance test for full and incremental build on ioschedule 2014
+ */
+@CompileStatic
+class IOScheduleResChangeTest {
+
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+            .fromExternalProject("iosched")
+            .create()
+
+    @Before
+    public void setUp() {
+        project.executeWithBenchmark("iosched2014", BUILD_FULL, "clean" , "assembleDebug")
+    }
+
+    @After
+    void cleanUp() {
+        project = null;
+    }
+
+    @Test
+    void "Incremental Build on Resource Edit Change"() {
+        project.replaceLine(
+                "android/src/main/res/values/strings.xml",
+                97,
+                "    <string name=\"app_name\">Google I/O 2015</string>")
+
+        project.executeWithBenchmark("iosched2014", BUILD_INC_RES_EDIT, "assembleDebug")
+    }
+
+    @Test
+    void "Incremental Build on Resource Add Change"() {
+        project.replaceLine(
+                "android/src/main/res/values/strings.xml",
+                97,
+                "    <string name=\"app_name\">Google I/O 2015</string><string name=\"aaaa\">aaa</string>")
+
+        project.executeWithBenchmark("iosched2014", BUILD_INC_RES_ADD, "assembleDebug")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy
index 91b57b9..8330926 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.performance
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
 import com.android.build.gradle.integration.common.fixture.app.VariantBuildScriptGenerator
@@ -24,6 +23,8 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
 /**
  * Performance test on gradle experimantal plugin with a large number of variants
  */
@@ -34,18 +35,19 @@
             .forExpermimentalPlugin(true)
             .create()
 
+
     @BeforeClass
     static void setUp() {
-        project.buildFile << new VariantBuildScriptGenerator(
+        VariantBuildScriptGenerator generator = new VariantBuildScriptGenerator(
                 buildTypes: VariantBuildScriptGenerator.LARGE_NUMBER,
                 productFlavors: VariantBuildScriptGenerator.LARGE_NUMBER,
                 """
                 apply plugin: "com.android.model.application"
 
                 model {
-                    android.config {
-                        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-                        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+                    android {
+                        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+                        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
                     }
 
                     android.buildTypes {
@@ -56,7 +58,11 @@
                         \${productFlavors}
                     }
                 }
-                """.stripIndent()).createBuildScript()
+                """.stripIndent())
+        generator.addPostProcessor("buildTypes") { return (String) "create(\"$it\")" }
+        generator.addPostProcessor("productFlavors") { return (String) "create(\"$it\")" }
+
+        project.buildFile << generator.createBuildScript()
 
         // Execute before performance test to warm up the cache.
         project.execute("help");
@@ -69,6 +75,6 @@
 
     @Test
     void performanceTest() {
-        project.execute("help")
+        project.executeWithBenchmark("LargeVariantAndroid", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy
index b617d14..df629a5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.performance
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
 import com.android.build.gradle.integration.common.fixture.app.VariantBuildScriptGenerator
@@ -24,6 +23,8 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
 /**
  * Performance test on gradle plugin with a large number of variants
  */
@@ -31,6 +32,7 @@
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
             .fromTestApp(new HelloWorldApp())
+            .useExperimentalGradleVersion(true)
             .create()
 
     @BeforeClass
@@ -66,6 +68,6 @@
 
     @Test
     void performanceTest() {
-        project.execute("help")
+        project.executeWithBenchmark("LargeVariantAndroid", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy
index 7e4c34c..f6a1ffc 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy
@@ -23,6 +23,7 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
 
@@ -50,6 +51,6 @@
 
     @Test
     void "'projects' task run on 120 projects"() {
-        project.execute("projects")
+        project.executeWithBenchmark("MediumAndroid", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy
index 61e13b4..f2095b2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy
@@ -23,8 +23,10 @@
 import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.ClassRule
+import org.junit.Ignore
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
 
@@ -50,7 +52,8 @@
     }
 
     @Test
+    @Ignore
     void "model query for 120 projects"() {
-        Map<String, AndroidProject> models = project.getAllModels()
+        Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("MediumAndroid", SYNC)
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy
index abbfa42..e24c2e8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy
@@ -24,6 +24,7 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
 
@@ -50,6 +51,6 @@
 
     @Test
     void "'projects' task run on 120 projects"() {
-        project.execute("projects")
+        project.executeWithBenchmark("MediumAndroid", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy
index 077c44e..910d44a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy
@@ -25,6 +25,7 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
 
@@ -51,6 +52,6 @@
 
     @Test
     void "model query for 120 projects"() {
-        Map<String, AndroidProject> models = project.getAllModels()
+        Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("MediumAndroid", SYNC)
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy
index 1cad88f..e6164c1 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy
@@ -24,6 +24,7 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
 
@@ -49,6 +50,6 @@
 
     @Test
     void "'projects' task run on 120 projects"() {
-        project.execute("projects")
+        project.executeWithBenchmark("MediumJava", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy
index 2698c8f..d995e2b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.performance
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.TestProject
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
@@ -24,26 +23,29 @@
 import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
 import com.android.build.gradle.integration.common.fixture.app.VariantBuildScriptGenerator
 import org.junit.AfterClass
+import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
 /**
- * Performance test on gradle experimantal plugin with multiple subprojects and multiple variants.
+ * Performance test on gradle experimental plugin with multiple sub-projects and multiple variants.
  */
 class MultiProjectsAndroidComponentTest {
     public static AndroidTestApp app = new HelloWorldApp()
     static {
-        app.addFile(new TestSourceFile("", "build.gradle",
-                new VariantBuildScriptGenerator(
+        VariantBuildScriptGenerator generator = new VariantBuildScriptGenerator(
                 buildTypes: VariantBuildScriptGenerator.MEDIUM_NUMBER,
                 productFlavors: VariantBuildScriptGenerator.MEDIUM_NUMBER,
                 """
                 apply plugin: "com.android.model.application"
 
                 model {
-                    android.config {
-                        compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
-                        buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+                    android {
+                        compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+                        buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
                     }
 
                     android.buildTypes {
@@ -54,8 +56,11 @@
                         \${productFlavors}
                     }
                 }
-                """.stripIndent()).createBuildScript())
-        )
+                """.stripIndent())
+        generator.addPostProcessor("buildTypes") { return (String) "create(\"$it\")" }
+        generator.addPostProcessor("productFlavors") { return (String) "create(\"$it\")" }
+
+        app.addFile(new TestSourceFile("", "build.gradle", generator.createBuildScript()))
     }
 
     public static TestProject baseProject = new MultiModuleTestProject("app", app, 10)
@@ -66,6 +71,12 @@
             .forExpermimentalPlugin(true)
             .create()
 
+    @BeforeClass
+    static void setUp() {
+        // Execute before performance test to warm up the cache.
+        project.execute("help");
+    }
+
     @AfterClass
     static void cleanUp() {
         app = null;
@@ -74,7 +85,12 @@
     }
 
     @Test
-    void performanceTest() {
-        project.execute("help")
+    void "performance test - projects"() {
+        project.executeWithBenchmark("MultiProjectsAndroid", EVALUATION, "projects")
+    }
+
+    @Test
+    void "performance test - single variant"() {
+        project.executeWithBenchmark("MultiProjectsAndroid", BUILD_FULL, ":app0:assembleProductFlavor0BuildType0")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy
index 3fabb69..ede30b5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.performance
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.android.build.gradle.integration.common.fixture.TestProject
 import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
@@ -28,6 +27,9 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
 /**
  * Performance test on gradle plugin with multiple subprojects and multiple variants.
  */
@@ -62,8 +64,15 @@
     @ClassRule
     public static GradleTestProject project = GradleTestProject.builder()
             .fromTestApp(baseProject)
+            .useExperimentalGradleVersion(true)
             .create()
 
+    @BeforeClass
+    static void setUp() {
+        // Execute before performance test to warm up the cache.
+        project.execute("help");
+    }
+
     @AfterClass
     static void cleanUp() {
         app = null;
@@ -72,7 +81,12 @@
     }
 
     @Test
-    void performanceTest() {
-        project.execute("help")
+    void "performance test - projects"() {
+        project.executeWithBenchmark("MultiProjectsAndroid", EVALUATION, "projects")
+    }
+
+    @Test
+    void "performance test - single variant"() {
+        project.executeWithBenchmark("MultiProjectsAndroid", BUILD_FULL, ":app0:assembleProductFlavor0BuildType0")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy
index 53c855f..bd2a5c2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy
@@ -23,6 +23,7 @@
 import org.junit.ClassRule
 import org.junit.Test
 
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.SMALL_BREADTH
 import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.SMALL_DEPTH
 
@@ -49,6 +50,6 @@
 
     @Test
     void "'projects' task run on 30 projects"() {
-        project.execute("projects")
+        project.executeWithBenchmark("SmallAndroid", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy
index 59a6933..b29fcb8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy
@@ -22,7 +22,11 @@
 import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.ClassRule
+import org.junit.Ignore
 import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
+
 /**
  * test with ~30 projects that queries the IDE model
  */
@@ -45,7 +49,8 @@
     }
 
     @Test
+    @Ignore
     void "model query for 30 projects"() {
-        Map<String, AndroidProject> models = project.getAllModels()
+        Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("SmallAndroid", SYNC)
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy
index 1217529..d463454 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy
@@ -22,6 +22,9 @@
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
 /**
  * test with ~30 projects that evaluates the projects
  */
@@ -44,6 +47,6 @@
 
     @Test
     void "'projects' task run on 30 projects"() {
-        project.execute("projects")
+        project.executeWithBenchmark("SmallAndroid", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy
index 9feedc0..6b01591 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy
@@ -23,6 +23,9 @@
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
+
 /**
  * test with ~30 projects that queries the IDE model
  */
@@ -45,6 +48,6 @@
 
     @Test
     void "model query for 30 projects"() {
-        Map<String, AndroidProject> models = project.getAllModels()
+        Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("SmallAndroid", SYNC)
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy
index 1e20066..8b4e6a2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy
@@ -22,6 +22,9 @@
 import org.junit.AfterClass
 import org.junit.ClassRule
 import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
 /**
  * test with ~30 projects that evaluates the projects
  */
@@ -44,6 +47,6 @@
 
     @Test
     void "'projects' task run on 30 projects"() {
-        project.execute("projects")
+        project.executeWithBenchmark("MediumJava", EVALUATION, "projects")
     }
 }
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/DuplicateModuleNameImportTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/DuplicateModuleNameImportTest.groovy
new file mode 100644
index 0000000..dbbcbd2
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/DuplicateModuleNameImportTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.integration.test
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.gradle.api.tasks.StopExecutionException
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+import static org.junit.Assert.fail
+
+/**
+ * Regression test for a infinite loop error in DependencyManager when importing a project
+ * with the same local name as the requester.
+ */
+@CompileStatic
+class DuplicateModuleNameImportTest {
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("duplicateNameImport")
+            .create()
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+    }
+
+    @Test
+    void "check build"() throws Exception {
+        // building should generate an exception
+        try {
+            project.execute("clean", "assemble")
+            fail("build succeeded while it should have failed")
+        } catch (Exception expected) {
+            // look for the root exception which has been thrown by the gradle build process
+            Throwable rootException = expected;
+            while (rootException.getCause() != null) {
+                rootException = rootException.getCause();
+            }
+            assertTrue("Exception did not contain expected message",
+                rootException.getMessage().contains(
+"""Your project contains 2 or more modules with the same identification a.b.c:Project
+at ":A:Project" and ":B:Project".
+You must use different identification (either name or group) for each modules."""))
+        }
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy
index ecceacf..412d870 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy
@@ -15,26 +15,17 @@
  */
 
 package com.android.build.gradle.integration.test
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.android.build.gradle.integration.common.utils.ModelHelper
-import com.android.builder.model.AndroidArtifact
 import com.android.builder.model.AndroidProject
-import com.android.builder.model.Dependencies
-import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
 import org.junit.AfterClass
 import org.junit.BeforeClass
 import org.junit.ClassRule
 import org.junit.Test
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-import static com.android.builder.model.AndroidProject.ARTIFACT_ANDROID_TEST
-import static org.junit.Assert.assertEquals
-import static org.junit.Assert.assertNotNull
-
 /**
  * Test for setup with 2 modules: app and test-app
  */
+@CompileStatic
 class SeparateTestModuleTest {
     @ClassRule
     static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithAarDependencyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithAarDependencyTest.groovy
new file mode 100644
index 0000000..db30337
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithAarDependencyTest.groovy
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 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.integration.test
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Dependencies
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+/**
+ * Test separate test module testing an app with aar dependencies.
+ */
+@CompileStatic
+public class SeparateTestWithAarDependencyTest {
+
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("separateTestModule")
+            .create()
+
+    static Map<String, AndroidProject> models
+
+    @BeforeClass
+    static void setup() {
+        project.getSubproject("app").getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion = rootProject.ext.buildToolsVersion
+
+    publishNonDefault true
+
+    defaultConfig {
+        minSdkVersion 8
+    }
+}
+dependencies {
+    compile 'com.android.support:appcompat-v7:22.1.0'
+}
+        """
+
+        models = project.executeAndReturnMultiModel("clean", "assemble")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+        models = null
+    }
+
+    @Test
+    void "check app doesn't contain test app's code"() {
+        File apk = project.getSubproject('test').getApk("debug")
+        assertThatApk(apk).doesNotContainClass("Lcom/android/tests/basic/Main;")
+    }
+
+    @Test
+    void "check app doesn't contain test app's layout"() {
+        File apk = project.getSubproject('test').getApk("debug")
+        assertThatApk(apk).doesNotContainResource("layout/main.xml")
+    }
+
+    @Test
+    void "check app doesn't contain test app's dependency lib's code"() {
+        File apk = project.getSubproject('test').getApk("debug")
+        assertThatApk(apk).doesNotContainClass("Landroid/support/v7/app/ActionBar;")
+    }
+
+    @Test
+    void "check app doesn't contain test app's dependency lib's resources"() {
+        File apk = project.getSubproject('test').getApk("debug")
+        assertThatApk(apk).doesNotContainResource("layout/abc_action_bar_title_item.xml")
+    }
+
+    @Test
+    void "check test model includes the tested app"() {
+        Collection<Variant> variants = models.get(":test").getVariants()
+
+        // get the main artifact of the debug artifact and its dependencies
+        Variant variant = ModelHelper.getVariant(variants, "debug")
+        AndroidArtifact artifact = variant.getMainArtifact()
+        Dependencies dependencies = artifact.getDependencies()
+
+        // check the app project shows up as a project dependency
+        Collection<String> projects = dependencies.getProjects()
+        assertThat(projects).containsExactly(":app")
+
+        // and that nothing else shows up.
+        // TODO: fix this.
+//        Collection<JavaLibrary> javaLibs = dependencies.getJavaLibraries();
+//        assertThat(javaLibs).hasSize(0);
+//        Collection<AndroidLibrary> libs = dependencies.getLibraries();
+//        assertThat(libs).hasSize(0);
+    }
+
+    @Test
+    @Category(DeviceTests)
+    void "connected check"() {
+        GradleTestProject.assumeLocalDevice()
+        project.execute("clean",":test:connectedCheck");
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithDependenciesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithDependenciesTest.groovy
new file mode 100644
index 0000000..a4d3577
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithDependenciesTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.integration.test;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject;
+import com.android.builder.model.AndroidProject;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Map;
+
+import groovy.transform.CompileStatic;
+
+/**
+ * Test separate test module that tests an application with some complicated dependencies :
+ *  - the app imports a library importing a jar file itself.
+ *  - use minification.
+ */
+@CompileStatic
+public class SeparateTestWithDependenciesTest {
+
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("separateTestModuleWithDependencies")
+            .create()
+
+    @BeforeClass
+    static void setup() {
+        project.execute("clean", "assemble")
+    }
+    @Test
+    void "check app contains all dependent clases"() {
+        project.execute("clean", "assemble")
+        File apk = project.getSubproject('app').getApk("debug")
+        assertThatApk(apk).containsClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+    }
+
+
+    @Test
+    void "check test app does not contain any minified application's dependent classes"() {
+        project.execute("clean", "assemble")
+        File apk = project.getSubproject('test').getApk("debug")
+        assertThatApk(apk).doesNotContainClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithMinificationButNoObfuscationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithMinificationButNoObfuscationTest.groovy
new file mode 100644
index 0000000..c8dae31
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithMinificationButNoObfuscationTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.integration.test
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+/**
+ * Test for a separate test module that has minification turned on but no obfuscation
+ * (no mapping.txt file produced)
+ */
+@CompileStatic
+class SeparateTestWithMinificationButNoObfuscationTest {
+    @ClassRule
+    static public GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("separateTestWithMinificationButNoObfuscation")
+            .create()
+
+    @Test
+    void "test building"() {
+        // just building fine is enough to test the regression.
+        project.execute("clean", "assemble")
+    }
+
+    @AfterClass
+    static void cleanUp() {
+        project = null
+    }
+
+    @Test
+    @Category(DeviceTests)
+    void "connected check"() {
+        GradleTestProject.assumeLocalDevice()
+        project.execute(":test:connectedAndroidTest");
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithoutMinificationWithDependenciesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithoutMinificationWithDependenciesTest.groovy
new file mode 100644
index 0000000..07f007d
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithoutMinificationWithDependenciesTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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.integration.test;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject;
+
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Test separate test module that tests an application with some complicated dependencies :
+ *  - the app imports a library importing a jar file itself.
+ */
+public class SeparateTestWithoutMinificationWithDependenciesTest {
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("separateTestModuleWithDependencies")
+            .create()
+
+
+    @BeforeClass
+    static void setup() {
+        project.getSubproject("test").getBuildFile() << """
+        apply plugin: 'com.android.test'
+
+        android {
+            compileSdkVersion 19
+            buildToolsVersion = rootProject.buildToolsVersion
+
+            targetProjectPath ':app'
+            targetVariant 'debug'
+        }
+        """
+        project.execute("clean", "assemble")
+    }
+    @Test
+    void "check app contains all dependent clases"() {
+        File apk = project.getSubproject('app').getApk("debug")
+        assertThatApk(apk).containsClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+    }
+
+
+    @Test
+    void "check test app does not contain any application's dependent classes"() {
+        File apk = project.getSubproject('test').getApk("debug")
+        assertThatApk(apk).doesNotContainClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy
index 1c814df..9f9cd5d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.testing
-
 /**
  * Helper class for inspecting JUnit XML files.
  */
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/TestingSupportLibraryTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/TestingSupportLibraryTest.groovy
new file mode 100644
index 0000000..03124c8
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/TestingSupportLibraryTest.groovy
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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.integration.testing
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+
+/**
+ * Test project to cover the Android Gradle plugin's interaction with the testing support library.
+ */
+@CompileStatic
+class TestingSupportLibraryTest {
+
+    public static final AndroidTestApp helloWorldApp = new HelloWorldApp();
+    static {
+        /* Junit 4 now maps tests annotated with @Ignore and tests that throw
+           AssumptionFailureExceptions as skipped. */
+        helloWorldApp.addFile(new  TestSourceFile("src/androidTest/java/com/example/helloworld", "FailureAssumptionTest.java",
+                """
+package com.example.helloworld;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FailureAssumptionTest {
+    @Test
+    public void checkAssumptionIsSkipped() {
+        assumeTrue(false);
+        fail("Tests with failing assumptions should be skipped");
+    }
+
+    @Test
+    @Ignore
+    public void checkIgnoreTestsArePossible() {
+        fail("Tests with @Ignore annotation should be skipped");
+    }
+
+    @Test
+    public void checkThisTestPasses() {
+        System.err.println("Test executed");
+    }
+}
+"""))
+        helloWorldApp.addFile(new TestSourceFile("src/main", "AndroidManifest.xml",
+                """<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.example.helloworld"
+      android:versionCode="1"
+      android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="18" />
+    <application android:label="@string/app_name">
+        <activity android:name=".HelloWorld"
+                  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>
+"""))
+    }
+
+    @Rule
+    public GradleTestProject project = GradleTestProject.builder()
+        .fromTestApp(helloWorldApp)
+        .create()
+
+    @Before
+    public void setUp() {
+        project
+        project.getBuildFile() << """
+
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+    buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+    defaultConfig {
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    dependencies {
+        androidTestCompile 'com.android.support.test:runner:0.3'
+        androidTestCompile 'com.android.support.test:rules:0.3'
+    }
+}
+"""
+    }
+
+    @Test
+    public void "check compile"() {
+        project.execute("assembleDebugAndroidTest")
+    }
+
+    @Test
+    @Category(DeviceTests.class)
+    public void "test ignored tests are not run"() {
+        project.executeConnectedCheck()
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingComplexProjectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingComplexProjectTest.groovy
new file mode 100644
index 0000000..67e0db9
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingComplexProjectTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.integration.testing
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Runs tests in a big, complicated project.
+ */
+class UnitTestingComplexProjectTest {
+    @ClassRule
+    public static GradleTestProject project = GradleTestProject.builder()
+            .fromTestProject("unitTestingComplexProject")
+            .create()
+
+    @AfterClass
+    public static void freeResources() throws Exception {
+        project = null
+    }
+
+    @Test
+    public void appProject() throws Exception {
+        project.execute("clean", "test")
+    }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy
index d844105..661ca2c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy
@@ -16,6 +16,7 @@
 
 package com.android.build.gradle.integration.testing
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
 import org.junit.ClassRule
 import org.junit.Test
 
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy
index 1e6758a..da07294 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy
@@ -15,7 +15,6 @@
  */
 
 package com.android.build.gradle.integration.testing
-
 import com.android.build.gradle.integration.common.fixture.GradleTestProject
 import com.google.common.base.Throwables
 import org.gradle.tooling.BuildException
@@ -25,7 +24,6 @@
 import static com.android.build.gradle.integration.testing.JUnitResults.Outcome.FAILED
 import static com.android.build.gradle.integration.testing.JUnitResults.Outcome.PASSED
 import static org.junit.Assert.fail
-
 /**
  * Meta-level tests for the app-level unit testing support.
  */
diff --git a/build-system/integration-test/src/test/resources/basic/example.json b/build-system/integration-test/src/test/resources/basic/example.json
new file mode 100644
index 0000000..6bad0d3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/basic/example.json
@@ -0,0 +1,90 @@
+{
+
+"project_info": {
+  "project_id": "red-ant-1",
+  "project_number": "1234567890",
+  "name": "My Awesome App"
+},
+
+"client": [{
+  "client_info": {
+    "client_type": 1,
+    "android_client_info": {
+      "package_name": "com.example.app.free"
+    }
+  },
+
+  "oauth_client": [{
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.free",
+      "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+    }
+  },
+  {
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.free",
+      "certificate_hash": "AFDD9pi4klj5vQ6D8gab7UNawhw="
+    }
+  }],
+
+  "services": {
+    "analytics_service": {
+      "status": 2,
+      "analytics_property": {
+        "tracking_id": "UA-123456789"
+      }
+    },
+    "cloud_messaging_service": {
+      "status": 2
+    },
+    "appinvite_service": {
+      "status": 2
+    },
+    "google_signin_service": {
+      "status": 2
+    },
+    "ads_service": {
+      "status": 2,
+      "test_banner_ad_unit_id": "ca-app-pub-3940256099942544\/1033173712",
+      "test_interstitial_ad_unit_id": "ca-app-pub-3940256099942544\/6300978111"
+    }
+  }
+},
+
+{
+  "client_info": {
+    "client_type": 1,
+    "android_client_info": {
+      "package_name": "com.example.app.paid"
+    }
+  },
+
+  "oauth_client": [{
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.paid",
+      "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+    }
+  }],
+  "services": {
+    "analytics_service": {
+      "status": 2,
+      "analytics_property": {
+        "tracking_id": "UA-1923912413"
+      }
+    },
+    "cloud_messaging_service": {
+      "status": 2
+    },
+    "appinvite_service": {
+      "status": 2
+    },
+    "google_signin_service": {
+      "status": 2
+    }
+  }
+}]
+
+}
diff --git a/build-system/integration-test/src/test/resources/basic/global_tracker.xml b/build-system/integration-test/src/test/resources/basic/global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/basic/global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/basic/values.xml b/build-system/integration-test/src/test/resources/basic/values.xml
new file mode 100644
index 0000000..8ec1e20
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/basic/values.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+    <string name="gcm_defaultSenderId">1234567890</string>
+    <string name="test_banner_ad_unit_id">ca-app-pub-3940256099942544/1033173712</string>
+    <string name="test_interstitial_ad_unit_id">ca-app-pub-3940256099942544/6300978111</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/disabledservice/example.json b/build-system/integration-test/src/test/resources/disabledservice/example.json
new file mode 100644
index 0000000..9d58065
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/disabledservice/example.json
@@ -0,0 +1,90 @@
+{
+
+"project_info": {
+  "project_id": "red-ant-1",
+  "project_number": "1234567890",
+  "name": "My Awesome App"
+},
+
+"client": [{
+  "client_info": {
+    "client_type": 1,
+    "android_client_info": {
+      "package_name": "com.example.app.free"
+    }
+  },
+
+  "oauth_client": [{
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.free",
+      "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+    }
+  },
+  {
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.free",
+      "certificate_hash": "AFDD9pi4klj5vQ6D8gab7UNawhw="
+    }
+  }],
+
+  "services": {
+    "analytics_service": {
+      "status": 2,
+      "analytics_property": {
+        "tracking_id": "UA-123456789"
+      }
+    },
+    "cloud_messaging_service": {
+      "status": 2
+    },
+    "appinvite_service": {
+      "status": 2
+    },
+    "google_signin_service": {
+      "status": 2
+    },
+    "ads_service": {
+      "status": 1,
+      "test_banner_ad_unit_id": "ca-app-pub-3940256099942544\/1033173712",
+      "test_interstitial_ad_unit_id": "ca-app-pub-3940256099942544\/6300978111"
+    }
+  }
+},
+
+{
+  "client_info": {
+    "client_type": 1,
+    "android_client_info": {
+      "package_name": "com.example.app.paid"
+    }
+  },
+
+  "oauth_client": [{
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.paid",
+      "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+    }
+  }],
+  "services": {
+    "analytics_service": {
+      "status": 2,
+      "analytics_property": {
+        "tracking_id": "UA-1923912413"
+      }
+    },
+    "cloud_messaging_service": {
+      "status": 2
+    },
+    "appinvite_service": {
+      "status": 2
+    },
+    "google_signin_service": {
+      "status": 2
+    }
+  }
+}]
+
+}
diff --git a/build-system/integration-test/src/test/resources/disabledservice/global_tracker.xml b/build-system/integration-test/src/test/resources/disabledservice/global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/disabledservice/global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/disabledservice/values.xml b/build-system/integration-test/src/test/resources/disabledservice/values.xml
new file mode 100644
index 0000000..e43783c
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/disabledservice/values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+    <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/example.json b/build-system/integration-test/src/test/resources/flavor/example.json
new file mode 100644
index 0000000..877c44b
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/example.json
@@ -0,0 +1,85 @@
+{
+
+"project_info": {
+  "project_id": "red-ant-1",
+  "project_number": "1234567890",
+  "name": "My Awesome App"
+},
+
+"client": [{
+  "client_info": {
+    "client_type": 1,
+    "android_client_info": {
+      "package_name": "com.example.app.free"
+    }
+  },
+
+  "oauth_client": [{
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.free",
+      "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+    }
+  },
+  {
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.free",
+      "certificate_hash": "AFDD9pi4klj5vQ6D8gab7UNawhw="
+    }
+  }],
+
+  "services": {
+    "analytics_service": {
+      "status": 2,
+      "analytics_property": {
+        "tracking_id": "UA-123456789"
+      }
+    },
+    "cloud_messaging_service": {
+      "status": 2
+    },
+    "appinvite_service": {
+      "status": 2
+    },
+    "google_signin_service": {
+      "status": 2
+    }
+  }
+},
+
+{
+  "client_info": {
+    "client_type": 1,
+    "android_client_info": {
+      "package_name": "com.example.app.paid"
+    }
+  },
+
+  "oauth_client": [{
+    "client_type": 1,
+    "android_info": {
+      "package_name": "com.example.app.paid",
+      "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+    }
+  }],
+  "services": {
+    "analytics_service": {
+      "status": 2,
+      "analytics_property": {
+        "tracking_id": "UA-1923912413"
+      }
+    },
+    "cloud_messaging_service": {
+      "status": 2
+    },
+    "appinvite_service": {
+      "status": 2
+    },
+    "google_signin_service": {
+      "status": 2
+    }
+  }
+}]
+
+}
diff --git a/build-system/integration-test/src/test/resources/flavor/free.global_tracker.xml b/build-system/integration-test/src/test/resources/flavor/free.global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/free.global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/free.values.xml b/build-system/integration-test/src/test/resources/flavor/free.values.xml
new file mode 100644
index 0000000..e43783c
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/free.values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+    <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/paid.global_tracker.xml b/build-system/integration-test/src/test/resources/flavor/paid.global_tracker.xml
new file mode 100644
index 0000000..b17f063
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/paid.global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-1923912413</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/paid.values.xml b/build-system/integration-test/src/test/resources/flavor/paid.values.xml
new file mode 100644
index 0000000..ac047c1
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/paid.values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-1923912413</string>
+    <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/noservice/global_tracker.xml b/build-system/integration-test/src/test/resources/noservice/global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/noservice/global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/noservice/no_services.json b/build-system/integration-test/src/test/resources/noservice/no_services.json
new file mode 100644
index 0000000..e3ad31e
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/noservice/no_services.json
@@ -0,0 +1,11 @@
+{
+
+"project_info": {
+  "project_id": "red-ant-1",
+  "project_number": "1234567890",
+  "name": "My Awesome App"
+},
+
+"client": []
+
+}
diff --git a/build-system/integration-test/src/test/resources/noservice/values.xml b/build-system/integration-test/src/test/resources/noservice/values.xml
new file mode 100644
index 0000000..77697c1
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/noservice/values.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle b/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle
index 77efb73..5e2ea74 100644
--- a/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle
+++ b/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle
@@ -20,7 +20,7 @@
 }
 
 project.afterEvaluate {
-    configure(fakeAndroidTest) {
+    configure(fakeDebugAndroidTest) {
         doLast {
             String error = project.fakeProvider.isValid()
             if (error != null) {
diff --git a/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle b/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
index 63051c5..82e0baa 100644
--- a/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
+++ b/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
@@ -1,7 +1,11 @@
 apply plugin: 'java'
 apply plugin: 'idea'
 
-def env = new Object() { String gradleVersion };
+def env = new Object() {
+    String gradleVersion
+    String experimentalGradleVersion
+};
+
 apply from: "../../commonGradlePluginVersion.gradle", to: env
 apply from: "../../commonLocalRepo.gradle"
 
diff --git a/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle b/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle
index 755a6ab..ea70169 100644
--- a/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle
+++ b/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle
@@ -20,7 +20,7 @@
 }
 
 project.afterEvaluate {
-    configure(fakeAndroidTest) {
+    configure(fakeDebugAndroidTest) {
         doLast {
             String error = project.fakeProvider.isValid()
             if (error != null) {
diff --git a/build-system/integration-test/test-projects/api/app/build.gradle b/build-system/integration-test/test-projects/api/app/build.gradle
index 782e191..c6c66c8 100644
--- a/build-system/integration-test/test-projects/api/app/build.gradle
+++ b/build-system/integration-test/test-projects/api/app/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
 apply plugin: 'com.android.application'
 
 android {
@@ -32,4 +36,4 @@
     if (android.testVariants.size() != 1) {
         throw new GradleException("Wrong number of test variants!")
     }
-}
\ No newline at end of file
+}
diff --git a/build-system/integration-test/test-projects/api/lib/build.gradle b/build-system/integration-test/test-projects/api/lib/build.gradle
index 69c65cd..29843fd 100644
--- a/build-system/integration-test/test-projects/api/lib/build.gradle
+++ b/build-system/integration-test/test-projects/api/lib/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
 apply plugin: 'com.android.library'
 
 android {
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/build.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/build.gradle
new file mode 100644
index 0000000..35636ce
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.0"
+
+    defaultConfig {
+        applicationId "com.example.manifest_merger_example"
+        minSdkVersion 15
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    productFlavors {
+        flavor {
+            applicationId "com.example.manifest_merger_example.flavor"
+            minSdkVersion 15
+            targetSdkVersion 21
+            versionCode 1
+            versionName "1.0"
+        }
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+        }
+    }
+}
+
+dependencies {
+    compile project(':examplelibrary')
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/flavor/AndroidManifest.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/flavor/AndroidManifest.xml
new file mode 100644
index 0000000..45541ac
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/flavor/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <application
+      android:allowBackup="true"
+      android:theme="@style/AppTheme">
+
+  </application>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..67f82bd
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/AndroidManifest.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+    package="com.example.manifest_merger_example"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <application
+      android:allowBackup="true"
+      android:theme="@style/AppTheme">
+
+  </application>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/layout/activity_main.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..7445603
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:paddingLeft="@dimen/activity_horizontal_margin"
+                android:paddingRight="@dimen/activity_horizontal_margin"
+                android:paddingTop="@dimen/activity_vertical_margin"
+                android:paddingBottom="@dimen/activity_vertical_margin"
+                tools:context=".MainActivity">
+
+  <TextView
+      android:text="@string/hello_world"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"/>
+
+</RelativeLayout>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/menu/menu_main.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..db961b9
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:tools="http://schemas.android.com/tools"
+      tools:context=".MainActivity">
+  <item android:id="@+id/action_settings"
+        android:title="@string/action_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"/>
+</menu>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/dimens.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..acf94cc
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+  <!-- Default screen margins, per the Android Design guidelines. -->
+  <dimen name="activity_horizontal_margin">16dp</dimen>
+  <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..56c7ab1
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">manifest-merger-example</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/styles.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cf2867a
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+  <!-- Base application theme. -->
+  <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+    <!-- Customize your theme here. -->
+  </style>
+
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/build.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/build.gradle
new file mode 100644
index 0000000..3268a26
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/build.gradle
@@ -0,0 +1,5 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+
+apply plugin: 'android-reporting'
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/build.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/build.gradle
new file mode 100644
index 0000000..fbf7f29
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/build.gradle
@@ -0,0 +1,18 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "21.1.0"
+
+    defaultConfig {
+        minSdkVersion 15
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+}
+
+android.testVariants.all {
+    it.mergedFlavor.manifestPlaceholders = [ localApplicationId:"com.example.manifest_merger_example.flavor"]
+}
+
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/androidTest/java/com/example/examplelibrary/ApplicationTest.java b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/androidTest/java/com/example/examplelibrary/ApplicationTest.java
new file mode 100644
index 0000000..fd8c006
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/androidTest/java/com/example/examplelibrary/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.example.examplelibrary;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+  public ApplicationTest() {
+    super(Application.class);
+  }
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..db8c460
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.mylibrary" >
+
+  <!-- Permissions for Google Cloud Messaging -->
+  <permission
+      android:name="${applicationId}.permission.C2D_MESSAGE"
+      android:protectionLevel="signature"/>
+
+  <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
+  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
+  <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <activity
+            android:name="com.example.examplelibrary.LoginActivity"
+            android:label="@string/app_name"
+            android:windowSoftInputMode="adjustResize|stateHidden" >
+        </activity>
+    </application>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/java/com/example/examplelibrary/LoginActivity.java b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/java/com/example/examplelibrary/LoginActivity.java
new file mode 100644
index 0000000..50fa184
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/java/com/example/examplelibrary/LoginActivity.java
@@ -0,0 +1,13 @@
+package com.example.examplelibrary;
+
+import android.app.Activity;
+
+/**
+ * A fake login activity.
+ */
+public class LoginActivity extends Activity {
+
+}
+
+
+
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0c9f82a
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+  <string name="app_name">ExampleLibrary</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/settings.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/settings.gradle
new file mode 100644
index 0000000..f52177d
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':examplelibrary'
diff --git a/build-system/integration-test/test-projects/artifactApi/build.gradle b/build-system/integration-test/test-projects/artifactApi/build.gradle
index 545ee34..f8edf2d 100644
--- a/build-system/integration-test/test-projects/artifactApi/build.gradle
+++ b/build-system/integration-test/test-projects/artifactApi/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
 apply from: "../commonHeader.gradle"
 buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
 
diff --git a/build-system/integration-test/test-projects/basic/build.gradle b/build-system/integration-test/test-projects/basic/build.gradle
index e40ecdf..d2af2bd 100644
--- a/build-system/integration-test/test-projects/basic/build.gradle
+++ b/build-system/integration-test/test-projects/basic/build.gradle
@@ -39,6 +39,8 @@
         targetSdkVersion 16
 
         testInstrumentationRunner "android.test.InstrumentationTestRunner"
+        testInstrumentationRunnerArgument "size", "medium"
+
         testHandleProfiling false
 
         buildConfigField "boolean", "DEFAULT", "true"
diff --git a/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java
index 7712fbf..312d15a 100644
--- a/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java
+++ b/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -2,8 +2,15 @@
 
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.TextView;
 
+import java.lang.RuntimeException;
+
+/**
+ * NB: All tests not annotated with @MediumTest will be ignored as the InstrumentationTestRunner
+ * is passed the custom argument of "size medium".
+ */
 public class MainTest extends ActivityInstrumentationTestCase2<Main> {
 
     private TextView mTextView;
@@ -40,5 +47,11 @@
     public void testBuildConfig() {
         assertEquals("bar", BuildConfig.FOO);
     }
+
+    @SmallTest
+    public void testSmallTestsShouldNotBeRun() {
+        throw new RuntimeException("Should have been excluded by custom test instrumentation "
+                + "runner argument.");
+    }
 }
 
diff --git a/build-system/integration-test/test-projects/commonBuildScript.gradle b/build-system/integration-test/test-projects/commonBuildScript.gradle
index 59cf7c2..8ebc12f 100644
--- a/build-system/integration-test/test-projects/commonBuildScript.gradle
+++ b/build-system/integration-test/test-projects/commonBuildScript.gradle
@@ -14,11 +14,18 @@
  * limitations under the License.
  */
 
-def env = new Object() { String gradleVersion };
+def env = new Object() {
+    String gradleVersion
+    String experimentalGradleVersion
+};
 apply from: "commonGradlePluginVersion.gradle", to: env
 
 apply from: "commonLocalRepo.gradle"
 
 dependencies {
     classpath "com.android.tools.build:gradle:$env.gradleVersion"
+    classpath "com.google.gms:google-services:$env.gradleVersion"
+    if (System.env.TEST_CLASSPATH_DEPENDENCY) {
+        classpath System.env.TEST_CLASSPATH_DEPENDENCY
+    }
 }
diff --git a/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle b/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle
index c39c427..31b14e9 100644
--- a/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle
+++ b/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle
@@ -13,11 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-def env = new Object() { String gradleVersion };
+def env = new Object() {
+    String gradleVersion
+    String experimentalGradleVersion
+};
+
 apply from: "commonGradlePluginVersion.gradle", to: env
 
 apply from: "commonLocalRepo.gradle"
 
 dependencies {
-    classpath "com.android.tools.build:gradle-experimental:$env.gradleVersion"
+    classpath "com.android.tools.build:gradle-experimental:$env.experimentalGradleVersion"
+    if (System.env.TEST_CLASSPATH_DEPENDENCY) {
+        classpath System.env.TEST_CLASSPATH_DEPENDENCY
+    }
 }
diff --git a/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle b/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle
index d194490..5f38f00 100644
--- a/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle
+++ b/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle
@@ -15,6 +15,7 @@
  */
 
 gradleVersion = System.env.CUSTOM_GRADLE
+experimentalGradleVersion = System.env.CUSTOM_EXPERIMENTAL_GRADLE
 
 if (gradleVersion == null && System.env.CUSTOM_REPO != null) {
     // Extract the version from the top level buildSrc, relative to CUSTOM_REPO.
@@ -25,11 +26,13 @@
                 String buildVersion
                 String baseVersion
                 String apiVersion
+                String experimentalVersion
             }
         }
     }
     apply from: "$System.env.CUSTOM_REPO/../../tools/buildSrc/base/version.gradle", to: env
     gradleVersion = env.project.ext.buildVersion
+    experimentalGradleVersion = env.project.ext.experimentalVersion
 }
 
 if (gradleVersion == null) {
diff --git a/build-system/integration-test/test-projects/commonHeader.gradle b/build-system/integration-test/test-projects/commonHeader.gradle
index 9a6afe6..1499c89 100644
--- a/build-system/integration-test/test-projects/commonHeader.gradle
+++ b/build-system/integration-test/test-projects/commonHeader.gradle
@@ -18,7 +18,32 @@
     buildToolsVersion = [System.env.CUSTOM_BUILDTOOLS, project.properties["CUSTOM_BUILDTOOLS"], '22.0.1'].find()
     latestCompileSdk = 21
 
-    useJack = System.env.CUSTOM_JACK || project.properties["CUSTOM_JACK"]
+    useJack = System.env.CUSTOM_JACK ||
+            Boolean.valueOf((String) project.properties[
+                    "com.android.build.gradle.integratonTest.useJack"])
+
+    minifyEnabled = System.env.CUSTOM_MINIFY ||
+            Boolean.valueOf((String) project.properties[
+                    "com.android.build.gradle.integratonTest.minifyEnabled"])
+
+    useComponentModel = Boolean.valueOf((String) project.properties[
+                    "com.android.build.gradle.integratonTest.useComponentModel"])
+
+    def remoteTestProvider = System.env.REMOTE_TEST_PROVIDER
+    if (remoteTestProvider != null) {
+        plugins.withId('com.android.application') {
+            apply plugin: remoteTestProvider
+        }
+        plugins.withId('com.android.library') {
+            apply plugin: remoteTestProvider
+        }
+        plugins.withId('com.android.model.application') {
+            apply plugin: remoteTestProvider
+        }
+        plugins.withId('com.android.model.library') {
+            apply plugin: remoteTestProvider
+        }
+    }
 }
 
 plugins.withId("com.android.application") { plugin ->
diff --git a/build-system/integration-test/test-projects/commonLocalRepo.gradle b/build-system/integration-test/test-projects/commonLocalRepo.gradle
index c25169d..0374bb1 100644
--- a/build-system/integration-test/test-projects/commonLocalRepo.gradle
+++ b/build-system/integration-test/test-projects/commonLocalRepo.gradle
@@ -22,4 +22,7 @@
     } else {
         throw new RuntimeException("Neither CUSTOM_REPO nor USE_EXTERNAL_REPO set.")
     }
+    if (System.env.ADDITIONAL_TEST_CUSTOM_REPO != null) {
+        maven { url System.env.ADDITIONAL_TEST_CUSTOM_REPO }
+    }
 }
diff --git a/build-system/integration-test/test-projects/componentModel/build.gradle b/build-system/integration-test/test-projects/componentModel/build.gradle
index ef3f5e4..55fb3e4 100644
--- a/build-system/integration-test/test-projects/componentModel/build.gradle
+++ b/build-system/integration-test/test-projects/componentModel/build.gradle
@@ -26,8 +26,8 @@
         buildToolsVersion '19.1.0'
     }
     android.productFlavors {
-        f1 {}
-        f2 {}
+        create { name = "f1" }
+        create { name = "f2" }
     }
 }
 
diff --git a/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml
index 7eb0a67..1422948 100644
--- a/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml
+++ b/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
     <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
         <activity android:name=".Main"
                   android:label="@string/app_name"
-                  android:icon="@drawable/other">
+                  android:icon="@drawable/ic_launcher">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/A/Project/build.gradle b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/build.gradle
new file mode 100644
index 0000000..1bb4b9c
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/build.gradle
@@ -0,0 +1,2 @@
+
+apply plugin: 'android-library'
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/A/Project/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b6a45ff
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="A.Project" >
+
+    <application />
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/B/Project/build.gradle b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/build.gradle
new file mode 100644
index 0000000..95e0116
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/build.gradle
@@ -0,0 +1,5 @@
+apply plugin: 'android-library'
+
+dependencies {
+  compile project(':A:Project')
+}
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/B/Project/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..01c142f
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="B.Project" >
+
+    <application />
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/build.gradle b/build-system/integration-test/test-projects/duplicateNameImport/build.gradle
new file mode 100644
index 0000000..47e52c3
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/build.gradle
@@ -0,0 +1,26 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+allprojects {
+
+  version = '0.1'
+  group = 'a.b.c'
+
+  plugins.withId('android-library') {
+
+    android {
+      buildToolsVersion rootProject.ext.buildToolsVersion
+      compileSdkVersion 22
+
+      defaultConfig {
+        minSdkVersion 22
+        targetSdkVersion 22
+      }
+
+    }
+  }
+}
+
+
+
+
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/settings.gradle b/build-system/integration-test/test-projects/duplicateNameImport/settings.gradle
new file mode 100644
index 0000000..faaad8c
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/settings.gradle
@@ -0,0 +1,6 @@
+
+include ':A:Project'
+include ':B:Project'
+
+
+
diff --git a/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java b/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java
index 1fac08e..3236a4d 100644
--- a/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java
+++ b/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java
@@ -58,15 +58,14 @@
 
     @IntDef(flag=false, value={0, Constants.CONSTANT_1, Constants.CONSTANT_3})
     @Retention(RetentionPolicy.SOURCE)
-    private @interface NonMask {}
+    protected @interface NonMaskType {}
 
-    public void testNonMask(@NonMask int mask) {
+    public void testNonMask(@NonMaskType int mask) {
     }
 
-    /** @hide */
     @IntDef({VISIBLE, INVISIBLE, GONE, 5, 7 + 10, Constants.CONSTANT_1})
     @Retention(RetentionPolicy.SOURCE)
-    private @interface Visibility {}
+    public @interface Visibility {}
 
     public static final int VISIBLE = 0x00000000;
     public static final int INVISIBLE = 0x00000004;
diff --git a/build-system/integration-test/test-projects/genFolderApi/build.gradle b/build-system/integration-test/test-projects/genFolderApi/build.gradle
index bc14b03..b61482b 100644
--- a/build-system/integration-test/test-projects/genFolderApi/build.gradle
+++ b/build-system/integration-test/test-projects/genFolderApi/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
 apply from: "../commonHeader.gradle"
 buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
 
diff --git a/build-system/integration-test/test-projects/genFolderApi2/build.gradle b/build-system/integration-test/test-projects/genFolderApi2/build.gradle
index d9279b2..6840b4c 100644
--- a/build-system/integration-test/test-projects/genFolderApi2/build.gradle
+++ b/build-system/integration-test/test-projects/genFolderApi2/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
 apply from: "../commonHeader.gradle"
 buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
 
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/build.gradle b/build-system/integration-test/test-projects/jarjarIntegration/build.gradle
new file mode 100644
index 0000000..08e0805
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/build.gradle
@@ -0,0 +1,89 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+
+if (buildToolsVersion < '21.0.0') {
+    println ("Warning : this sample requires build-tools version 21 or above")
+}
+
+apply plugin: 'com.android.application'
+
+android {
+  compileSdkVersion 21
+  buildToolsVersion = rootProject.ext.buildToolsVersion
+
+  defaultConfig {
+    versionCode 12
+    minSdkVersion 16
+    targetSdkVersion 20
+  }
+}
+
+repositories {
+  maven { url System.env.CUSTOM_REPO }
+}
+
+dependencies {
+  compile 'com.google.code.gson:gson:2.3'
+}
+
+// JarJar tasks and related configurations\
+configurations {
+  // create new configuration for JarJar
+  jarjar
+}
+
+dependencies {
+  // add jarjar artifact to jarjar configuration
+  jarjar 'com.googlecode.jarjar:jarjar:1.3'
+}
+
+android.applicationVariants.all { variant ->
+  // create task name
+  String jarJarTaskName = "jarJar${variant.name.capitalize()}";
+
+  def workingDir = project.file('build/intermediates/jarjar');
+  // input file are the compiled files and the gson lib.
+  def inputDir = variant.javaCompiler.destinationDir
+  def outputLibrary = new File(workingDir, 'classes.jar').getCanonicalFile()
+
+  // define task
+  def jarJarTask = task("${jarJarTaskName}") {
+    logger.info "${jarJarTaskName} inputDir:   ${inputDir}"
+    logger.info "${jarJarTaskName} outputLibrary:  ${outputLibrary}"
+
+    inputs.file inputDir
+    outputs.file outputLibrary
+
+    doLast {
+      // in Ant
+      project.ant {
+        // define jarjar task, for classpath is used path from jarjar configuration
+        taskdef name: 'jarjar',
+                classname: 'com.tonicsystems.jarjar.JarJarTask',
+                classpath: configurations.jarjar.asPath
+
+        // start jarjar task
+        jarjar(jarfile: outputLibrary) {
+          // input is our inputDir
+          fileset(dir: inputDir)
+          // this project has only ONE dependency, gson so this is easy but obviously, this is not
+          // the best way to include compile dependencies.
+          zipFileset(src: variant.compileLibraries.toArray()[0])
+          // rule to repackage gson to new package
+          rule pattern: 'com.google.gson.**', result: 'com.google.repacked.gson.@1'
+        }
+      }
+
+      // replace jar generated by compile with jar generated by JarJar
+      variant.dex.inputFiles = [outputLibrary]
+      // and remove our preDex'ed libraries.
+      variant.dex.libraries = []
+    }
+  }
+
+  // plan task to be started between Compile task and Dex task
+  // if the task was using proguard, we would need to reset the proguard inputs rather than dex.
+  variant.dex.dependsOn jarJarTask
+  jarJarTask.dependsOn variant.javaCompiler
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/proguard-rules.pro b/build-system/integration-test/test-projects/jarjarIntegration/proguard-rules.pro
new file mode 100644
index 0000000..f5df2a5
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/proguard-rules.pro
@@ -0,0 +1 @@
+-keep public class com.android.tests.basic.IndirectlyReferencedClass
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/jarjarIntegration/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..b7cbd6d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,47 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import com.android.tests.basic.Main;
+
+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.text);
+    }
+
+    /**
+     * 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);
+        Gson gson = new Gson();
+        String jsonString = gson.toJson(Main.STRINGS);
+        // assert that our gson strings is correct.
+        assertEquals(jsonString, mTextView.getText());
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7eb0a67
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?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/ic_launcher">
+        <activity android:name=".Main"
+                  android:label="@string/app_name"
+                  android:icon="@drawable/other">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/jarjarIntegration/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..5c1e17d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,26 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.google.gson.Gson;
+
+import java.lang.String;
+
+public class Main extends Activity
+{
+    static final String[] STRINGS = { "some", "radom", "strings"};
+    
+    /** 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);
+        Gson gson = new Gson();
+        String jsonString = gson.toJson(STRINGS);
+        tv.setText(jsonString);
+    }
+}
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-hdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/other.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/other.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/other.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xxhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/layout/main.xml
new file mode 100644
index 0000000..b199751
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/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 - Basic"
+    android:id="@+id/text"
+    />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/values/strings.xml
new file mode 100644
index 0000000..6d2c9ba
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">JarJarTest</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/release/res/values/strings.xml
new file mode 100644
index 0000000..532909c
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/release/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/build.gradle b/build-system/integration-test/test-projects/jarjarWithJack/build.gradle
new file mode 100644
index 0000000..8cefaf9
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/build.gradle
@@ -0,0 +1,37 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+
+if (buildToolsVersion < '21.0.0') {
+    println ("Warning : this sample requires build-tools version 21 or above")
+}
+
+apply plugin: 'com.android.application'
+
+android {
+  compileSdkVersion 21
+  buildToolsVersion = rootProject.ext.buildToolsVersion
+
+  defaultConfig {
+    versionCode 12
+    minSdkVersion 16
+    targetSdkVersion 20
+    useJack true
+    jarJarRuleFile 'jarjar.rules'
+  }
+
+  buildTypes {
+    release {
+      minifyEnabled true
+      proguardFiles getDefaultProguardFile('proguard-android.txt')
+    }
+  }
+}
+
+repositories {
+  maven { url System.env.CUSTOM_REPO }
+}
+
+dependencies {
+  compile 'com.google.code.gson:gson:2.3'
+}
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/jarjar.rules b/build-system/integration-test/test-projects/jarjarWithJack/jarjar.rules
new file mode 100644
index 0000000..f64843f
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/jarjar.rules
@@ -0,0 +1 @@
+rule com.google.gson.** com.google.repacked.gson.@1
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/proguard-rules.pro b/build-system/integration-test/test-projects/jarjarWithJack/proguard-rules.pro
new file mode 100644
index 0000000..f5df2a5
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/proguard-rules.pro
@@ -0,0 +1 @@
+-keep public class com.android.tests.basic.IndirectlyReferencedClass
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/jarjarWithJack/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..7cf7329
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,38 @@
+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.text);
+    }
+
+    /**
+     * 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);
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7eb0a67
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?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/ic_launcher">
+        <activity android:name=".Main"
+                  android:label="@string/app_name"
+                  android:icon="@drawable/other">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/jarjarWithJack/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..f201320
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,26 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.google.gson.Gson;
+
+import java.lang.String;
+
+public class Main extends Activity
+{
+    private static final String[] strings = { "some", "radom", "strings"};
+    
+    /** 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);
+        Gson gson = new Gson();
+        String jsonString = gson.toJson(strings);
+        tv.setText(jsonString);
+    }
+}
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-hdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/other.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/other.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/other.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xxhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/layout/main.xml
new file mode 100644
index 0000000..b199751
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/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 - Basic"
+    android:id="@+id/text"
+    />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bf812b2
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">JarJarWithJackTest</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/release/res/values/strings.xml
new file mode 100644
index 0000000..532909c
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/release/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java
index 46a3375..5719946 100644
--- a/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java
+++ b/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -3,6 +3,8 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.TextView;
+import android.test.UiThreadTest;
+import android.os.Looper;
 
 public class MainTest extends ActivityInstrumentationTestCase2<Main> {
 
@@ -56,5 +58,10 @@
 
         assertEquals("com.android.tests.basic.UsedTestClass", UsedTestClass.class.getName());
     }
+
+    @UiThreadTest
+    public void testAnnotationNotStripped() {
+        assertTrue("Should be running on UI thread", Looper.myLooper() == Looper.getMainLooper());
+    }
 }
 
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/build.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/build.gradle
new file mode 100644
index 0000000..20b0991
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: 'com.android.application'
+
+dependencies {
+    compile project(':lib')
+}
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion = rootProject.ext.buildToolsVersion
+
+    signingConfigs {
+        myConfig {
+            storeFile file("debug.keystore")
+            storePassword "android"
+            keyAlias "androiddebugkey"
+            keyPassword "android"
+        }
+    }
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        useJack rootProject.ext.useJack
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled true
+            signingConfig signingConfigs.myConfig
+            proguardFile getDefaultProguardFile('proguard-android.txt')
+        }
+    }
+
+    dexOptions {
+        incremental false
+    }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/debug.keystore b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/debug.keystore
new file mode 100644
index 0000000..389278e
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/debug.keystore
Binary files differ
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..ccae828
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,43 @@
+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("AppNameFromResources-RandomApp_\"Success : Valid number : 123\"",
+                mTextView.getText().toString());
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a34d937
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/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/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..3bc69ad
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,60 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import com.android.tests.basic.StringProvider;
+import com.android.tests.util.AppStringProvider;
+
+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.dateText);
+
+        String message;
+        try {
+            // load untouched xml file.
+            InputStream inputStream = null;
+            try {
+                inputStream = getClass().getResourceAsStream("/com/android/tests/other/some.xml");
+                if (inputStream == null) {
+                    message = "Cannot load some.xml";
+                } else {
+                    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+                    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+                    Document doc = dBuilder.parse(inputStream);
+                    String value = doc.getDocumentElement().getAttribute("value");
+                    message = StringProvider.getString(Integer.parseInt(value));
+                }
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            }
+
+            String appName = AppStringProvider.getProperty("app.name");
+            String appString = AppStringProvider.getProperty("app.string");
+
+
+            tv.setText(appName + '-' + appString + '_' + message);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/util/AppStringProvider.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/util/AppStringProvider.java
new file mode 100644
index 0000000..22be315
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/util/AppStringProvider.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.tests.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.RuntimeException;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * String provider getting the string format from a co-bundled resources.properties file.
+ */
+public class AppStringProvider {
+
+    private final static Properties properties = new Properties();
+
+    static {
+        try {
+            InputStream inputStream = null;
+            try {
+                inputStream = AppStringProvider.class.getResourceAsStream("resources.properties");
+                if (inputStream == null) {
+                    properties.put("app.name", "Error, cannot find resources.properties for %d");
+                } else {
+                    properties.load(inputStream);
+                }
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            }
+            try {
+                // load the second resource file, using absolute path.
+                inputStream = AppStringProvider.class.getResourceAsStream("/com/android/tests/util/another.properties");
+                if (inputStream == null) {
+                    properties.put("app.string", "Error, cannot load another.properties %s %d");
+                } else {
+                    properties.load(inputStream);
+                }
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            }
+
+        } catch(IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String getProperty(String key) {
+        return properties.getProperty(key);
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/drawable/icon.png
similarity index 100%
copy from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
copy to build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..89ab091
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/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/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60ea2d0
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/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/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/another.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/another.properties
new file mode 100644
index 0000000..001a110
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/another.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 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.
+#
+
+app.string=RandomApp
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/resources.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/resources.properties
new file mode 100644
index 0000000..8caea57
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/resources.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 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.
+#
+
+app.name=AppNameFromResources
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/build.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/build.gradle
new file mode 100644
index 0000000..bba07fc
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/build.gradle
@@ -0,0 +1,2 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/build.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/build.gradle
new file mode 100644
index 0000000..6f194df
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion = rootProject.ext.buildToolsVersion
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+        consumerProguardFiles 'config.pro'
+    }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/config.pro b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/config.pro
new file mode 100644
index 0000000..a4e4e35
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/config.pro
@@ -0,0 +1 @@
+-adaptresourcefilenames    !**another.properties,**.properties
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/androidTest/java/com/android/tests/basic/StringProviderTest.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/androidTest/java/com/android/tests/basic/StringProviderTest.java
new file mode 100644
index 0000000..8195e4d
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/androidTest/java/com/android/tests/basic/StringProviderTest.java
@@ -0,0 +1,10 @@
+package com.android.tests.basic;
+
+import junit.framework.TestCase;
+
+public class StringProviderTest extends TestCase {
+
+    public void testGetString() {
+        assertEquals("\"Success : Valid number : 123\"", StringProvider.getString(123));
+    }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..45f6ffd
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/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.lib">
+</manifest>
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/basic/StringProvider.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/basic/StringProvider.java
new file mode 100644
index 0000000..a9bad9b
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/basic/StringProvider.java
@@ -0,0 +1,23 @@
+package com.android.tests.basic;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.RuntimeException;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.net.URL;
+
+import com.android.tests.other.PropertiesProvider;
+
+/**
+ * String provider getting the string format from a co-bundled resources.properties file.
+ */
+public class StringProvider {
+
+    public static String getString(int aNumber) {
+        return String.format(
+                PropertiesProvider.getProperty("the.format"),
+                PropertiesProvider.getProperty("the.string"),
+                aNumber);
+    }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/other/PropertiesProvider.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/other/PropertiesProvider.java
new file mode 100644
index 0000000..2174f8d
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/other/PropertiesProvider.java
@@ -0,0 +1,54 @@
+package com.android.tests.other;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.RuntimeException;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * String provider getting the string format from a co-bundled resources.properties file.
+ */
+public class PropertiesProvider {
+
+    private final static Properties properties = new Properties();
+
+    static {
+        try {
+            InputStream inputStream = null;
+            try {
+                inputStream = PropertiesProvider.class.getResourceAsStream("resources.properties");
+                if (inputStream == null) {
+                    properties.put("the.string", "Error, cannot find resources.properties for %d");
+                } else {
+                    properties.load(inputStream);
+                }
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            }
+            try {
+               // load the second resource file, using absolute path.
+                inputStream = PropertiesProvider.class.getResourceAsStream("/com/android/tests/other/another.properties");
+                if (inputStream == null) {
+                    properties.put("the.format", "Error, cannot load another.properties %s %d");
+                } else {
+                    properties.load(inputStream);
+                }
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            }
+
+        } catch(IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String getProperty(String key) {
+        return properties.getProperty(key);
+    }
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/another.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/another.properties
new file mode 100644
index 0000000..b239ae8
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/another.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 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.
+#
+
+the.format="Success : %s : %d"
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/resources.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/resources.properties
new file mode 100644
index 0000000..ee55e43
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/resources.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 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.
+#
+
+the.string=Valid number
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/some.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/some.xml
new file mode 100644
index 0000000..e6046b7
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/some.xml
@@ -0,0 +1,17 @@
+<!--
+  ~ Copyright (C) 2015 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.
+  -->
+
+<xml value="123"/>
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/settings.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/settings.gradle
new file mode 100644
index 0000000..eedb2a1
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/settings.gradle
@@ -0,0 +1,2 @@
+include 'app'
+include 'lib'
diff --git a/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle b/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle
index 7db669d..9e66cb0 100644
--- a/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle
+++ b/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle
@@ -1,12 +1,12 @@
 apply plugin: 'com.android.model.library'
 
 model {
-    android.config {
+    android {
         compileSdkVersion 19
         buildToolsVersion = rootProject.ext.buildToolsVersion
     }
 
     android.ndk {
-        moduleName "hello-jni"
+        moduleName = "hello-jni"
     }
 }
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle b/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle
index 59773de..bb0bb79 100644
--- a/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle
+++ b/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle
@@ -5,63 +5,43 @@
 apply plugin: 'com.android.model.application'
 
 model {
-    android.config {
-        compileSdkVersion 19
+    android {
+        compileSdkVersion = 19
         buildToolsVersion = rootProject.ext.buildToolsVersion
 
-        defaultConfig {
+        defaultConfig.with {
             // This actual the app version code. Giving ourselves 1,000,000 values
             versionCode = 123
         }
-
-        // make per-variant version code
-        applicationVariants.all { variant ->
-            // get the single flavor
-            def flavorVersion = variant.productFlavors.get(0).versionCode
-
-            // set the composite code
-            variant.mergedFlavor.versionCode = flavorVersion * 1000000 + defaultConfig.versionCode
-        }
-    }
-    android.buildTypes {
-        debug {
-            debuggable true
-        }
     }
 
     android.ndk {
-        compileSdkVersion 19
-        moduleName "sanangeles"
-        cFlags "-DDISABLE_IMPORTGL"
-        ldLibs "GLESv1_CM", "dl", "log"
-        stl "stlport_static"
-        toolchain "clang"
+        moduleName = "sanangeles"
+        CFlags += "-DDISABLE_IMPORTGL"
+        ldLibs += "GLESv1_CM"
+        ldLibs += "dl"
+        ldLibs += "log"
+        stl = "stlport_static"
+        toolchain = "clang"
     }
 
     android.productFlavors {
-        x86 {
-            ndk {
-                abiFilter "x86"
-            }
-
+        create("x86") {
+            ndk.abiFilters += "x86"
             // this is the flavor part of the version code.
             // It must be higher than the arm one for devices supporting
             // both, as x86 is preferred.
             versionCode = 3
         }
-        arm {
-            ndk {
-                abiFilter "armeabi-v7a"
-            }
+        create("arm") {
+            ndk.abiFilters += "armeabi-v7a"
             versionCode = 2
         }
-        mips {
-            ndk {
-                abiFilter "mips"
-            }
+        create("mips") {
+            ndk.abiFilters += "mips"
             versionCode = 1
         }
-        fat {
+        create("fat") {
             // fat binary, lowest version code to be
             // the last option
             versionCode = 0
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app-android.c b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app-android.c
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app-android.c
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app-android.c
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app.h
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/cams.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/cams.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/cams.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/cams.h
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/demo.c b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/demo.c
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/demo.c
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/demo.c
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.c b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.c
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.c
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.c
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.h
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/shapes.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/shapes.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/shapes.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/shapes.h
diff --git a/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle b/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle
index b2ee58e..219efc9 100644
--- a/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle
+++ b/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle
@@ -2,7 +2,7 @@
 
 model {
     android.ndk {
-        compileSdkVersion 19
-        moduleName "hello-jni"
+        compileSdkVersion = 19
+        moduleName = "hello-jni"
     }
 }
diff --git a/build-system/integration-test/test-projects/ndkVariants/build.gradle b/build-system/integration-test/test-projects/ndkVariants/build.gradle
index 53607e0..535e44b 100644
--- a/build-system/integration-test/test-projects/ndkVariants/build.gradle
+++ b/build-system/integration-test/test-projects/ndkVariants/build.gradle
@@ -1,26 +1,34 @@
 apply from: "../commonHeader.gradle"
 buildscript { apply from: "../commonBuildScriptExperimental.gradle", to: buildscript }
 
+// This test ensures each variant compiles the correct source set with the appropriate NDK settings.
 
 apply plugin: 'com.android.model.application'
 
 model {
-    android.config {
-        compileSdkVersion 19
+    android {
+        compileSdkVersion = 19
         buildToolsVersion = rootProject.ext.buildToolsVersion
-
     }
 
     android.ndk {
-        moduleName "simple-jni"
+        moduleName = "simple-jni"
 
         // TODO: Include a way to set include directories the DSL.
-        cppFlags "-I$rootDir/src/include"
-        stl "stlport_static"
+        cppFlags += "-I$rootDir/src/include".toString()
+        stl = "stlport_static"
     }
 
     android.productFlavors {
-        free
-        premium
+        create("free")
+        create("premium")
+    }
+
+    // Set binary specific C++ flags.
+    components.android {
+        binaries.afterEach { binary ->
+            binary.mergedNdkConfig.cppFlags.add("-DVARIANT=\"" + binary.name + "\"")
+        }
     }
 }
+
diff --git a/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java b/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java
index 1b5b9d5..255b0b1 100644
--- a/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java
+++ b/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java
@@ -29,13 +29,10 @@
          * the text is retrieved by calling a native
          * function.
          */
-        StringBuilder sb = new StringBuilder(
-                productFlavorFromJni().equals("free") ?
-                        "Free as in beer!\n" :
-                        "Show me the money!\n");
-        sb.append("(");
-        sb.append(buildTypeFromJni());
-        sb.append(")");
+        StringBuilder sb = new StringBuilder();
+        sb.append("productFlavor: " + productFlavorFromJni() + "\n");
+        sb.append("buildType: " + buildTypeFromJni() + "\n");
+        sb.append("variant: " + variantFromJni() + "\n");
 
         TextView  tv = new TextView(this);
         tv.setText(sb.toString());
@@ -49,6 +46,9 @@
 
     public native String productFlavorFromJni();
 
+    /* Native method that expected the VARIANT macro to be defined. */
+    public native String variantFromJni();
+
     /* Load the 'simple-jni' library on application startup. */
     static {
         System.loadLibrary("simple-jni");
diff --git a/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp b/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp
index ac3ffd5..fff200e 100644
--- a/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp
+++ b/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp
@@ -39,3 +39,10 @@
 {
     return env->NewStringUTF(PRODUCT_FLAVOR);
 }
+
+extern "C"
+jstring
+Java_com_example_simplejni_SimpleJni_variantFromJni(JNIEnv* env, jobject thiz)
+{
+    return env->NewStringUTF(VARIANT);
+}
diff --git a/build-system/integration-test/test-projects/packagingOptions/build.gradle b/build-system/integration-test/test-projects/packagingOptions/build.gradle
index bc56782..55c0861 100644
--- a/build-system/integration-test/test-projects/packagingOptions/build.gradle
+++ b/build-system/integration-test/test-projects/packagingOptions/build.gradle
@@ -10,6 +10,9 @@
     packagingOptions {
         exclude 'excluded.txt'
         pickFirst 'first_pick.txt'
+        merge 'merge.txt'
+
+        pickFirst 'lib/x86/libdummy.so'
     }
 }
 
diff --git a/build-system/integration-test/test-projects/packagingOptions/jar1.jar b/build-system/integration-test/test-projects/packagingOptions/jar1.jar
index 41e058b..c4701d9 100644
--- a/build-system/integration-test/test-projects/packagingOptions/jar1.jar
+++ b/build-system/integration-test/test-projects/packagingOptions/jar1.jar
Binary files differ
diff --git a/build-system/integration-test/test-projects/packagingOptions/jar2.jar b/build-system/integration-test/test-projects/packagingOptions/jar2.jar
index 7ff93d1..acbf286 100644
--- a/build-system/integration-test/test-projects/packagingOptions/jar2.jar
+++ b/build-system/integration-test/test-projects/packagingOptions/jar2.jar
Binary files differ
diff --git a/build-system/integration-test/test-projects/packagingOptions/jars/jar1/src/main/resources/merge.txt b/build-system/integration-test/test-projects/packagingOptions/jars/jar1/src/main/resources/merge.txt
new file mode 100644
index 0000000..ef600d9
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/jars/jar1/src/main/resources/merge.txt
@@ -0,0 +1 @@
+merge1
diff --git a/build-system/integration-test/test-projects/packagingOptions/jars/jar2/src/main/resources/merge.txt b/build-system/integration-test/test-projects/packagingOptions/jars/jar2/src/main/resources/merge.txt
new file mode 100644
index 0000000..0a040d9
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/jars/jar2/src/main/resources/merge.txt
@@ -0,0 +1 @@
+merge2
diff --git a/build-system/integration-test/test-projects/packagingOptions/src/debug/jniLibs/x86/libdummy.so b/build-system/integration-test/test-projects/packagingOptions/src/debug/jniLibs/x86/libdummy.so
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/src/debug/jniLibs/x86/libdummy.so
diff --git a/build-system/integration-test/test-projects/packagingOptions/src/main/jniLibs/x86/libdummy.so b/build-system/integration-test/test-projects/packagingOptions/src/main/jniLibs/x86/libdummy.so
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/src/main/jniLibs/x86/libdummy.so
diff --git a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/layout/main_layout.xml b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/layout/main_layout.xml
new file mode 100644
index 0000000..0844dda
--- /dev/null
+++ b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/layout/main_layout.xml
@@ -0,0 +1,5 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/shared_name"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml
index 694459d..0e702a9 100644
--- a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml
+++ b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml
@@ -1,4 +1,6 @@
 <resources>
     <public name="mylib_app_name" type="string"/>
     <public name="mylib_public_string" type="string"/>
+    <public name="shared_name" type="string"/>
+    <public name="shared_name" type="id"/>
 </resources>
diff --git a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml
index 7a19f7e..606f0b8 100644
--- a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml
+++ b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml
@@ -2,4 +2,5 @@
     <string name="mylib_app_name">My Library</string>
     <string name="mylib_public_string">Public String in Library</string>
     <string name="mylib_private_string">Private String in Library</string>
+    <string name="shared_name">String that has the same name as an ID</string>
 </resources>
diff --git a/build-system/integration-test/test-projects/projectWithModules/library/src/main/res/layout/liblayout.xml b/build-system/integration-test/test-projects/projectWithModules/library/src/main/res/layout/liblayout.xml
new file mode 100644
index 0000000..25c0817
--- /dev/null
+++ b/build-system/integration-test/test-projects/projectWithModules/library/src/main/res/layout/liblayout.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+</LinearLayout>
diff --git a/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java
index 6c740b7..5dd2ed0 100644
--- a/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java
+++ b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java
@@ -4,7 +4,7 @@
 import android.widget.TextView;
 
 class PersonView2 extends TextView {
-    public PersonView(Context context, String name) {
+    public PersonView2(Context context, String name) {
         super(context);
         setTextSize(20);
         setText(name);
diff --git a/build-system/integration-test/test-projects/projectWithModules/library2/src/main/res/layout/lib2layout.xml b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/res/layout/lib2layout.xml
new file mode 100644
index 0000000..25c0817
--- /dev/null
+++ b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/res/layout/lib2layout.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+</LinearLayout>
diff --git a/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java b/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java
index 47d28fc..8015970 100644
--- a/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java
+++ b/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java
@@ -46,6 +46,7 @@
 
     @Override
     protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
         if(mRS != null) {
             mRS = null;
             destroyRenderScriptGL();
diff --git a/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java b/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java
index 47d28fc..8015970 100644
--- a/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java
+++ b/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java
@@ -46,6 +46,7 @@
 
     @Override
     protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
         if(mRS != null) {
             mRS = null;
             destroyRenderScriptGL();
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/build.gradle
new file mode 100644
index 0000000..3686af6
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion = rootProject.ext.buildToolsVersion
+
+    publishNonDefault true
+
+    defaultConfig {
+        minSdkVersion 16
+    }
+
+    buildTypes {
+        minified.initWith(buildTypes.debug)
+        minified {
+            minifyEnabled true
+            useJack rootProject.ext.useJack
+            proguardFiles getDefaultProguardFile('proguard-android.txt')
+        }
+    }
+}
+
+dependencies {
+    compile project(':lib')
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..7c2efd4
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,39 @@
+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.text);
+
+    }
+
+    /**
+     * 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);
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a34d937
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/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/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..2b0e698
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,15 @@
+package com.android.tests.basic;
+
+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/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/drawable/icon.png
similarity index 100%
copy from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
copy to build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..b199751
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/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 - Basic"
+    android:id="@+id/text"
+    />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60ea2d0
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/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/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/release/res/values/strings.xml
new file mode 100644
index 0000000..532909c
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/release/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/build.gradle
new file mode 100644
index 0000000..bba07fc
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/build.gradle
@@ -0,0 +1,2 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/build.gradle
new file mode 100644
index 0000000..bbfeb03
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/build.gradle
@@ -0,0 +1 @@
+apply plugin: 'java'
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/src/main/java/com/android/tests/jarDep/JarDependencyUtil.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/src/main/java/com/android/tests/jarDep/JarDependencyUtil.java
new file mode 100644
index 0000000..e0d9826
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/src/main/java/com/android/tests/jarDep/JarDependencyUtil.java
@@ -0,0 +1,7 @@
+package com.android.tests.jarDep;
+
+public class JarDependencyUtil {
+    public static String getString() {
+        return "jar dependency string";
+    }
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/build.gradle
new file mode 100644
index 0000000..af57e6f
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'com.android.library'
+
+dependencies {
+    compile project(':jar')
+}
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion = rootProject.ext.buildToolsVersion
+
+    defaultConfig {
+        versionCode 12
+        versionName "2.0"
+        minSdkVersion 16
+        targetSdkVersion 16
+    }
+}
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/androidTest/java/com/android/tests/basic/StringGetterTest.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/androidTest/java/com/android/tests/basic/StringGetterTest.java
new file mode 100644
index 0000000..ac09526
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/androidTest/java/com/android/tests/basic/StringGetterTest.java
@@ -0,0 +1,48 @@
+package com.android.tests.basic;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Method;
+
+public class StringGetterTest extends TestCase {
+
+    public void testNonObfuscatedMethod1() {
+        // this should not be obfuscated
+        String className = "com.android.tests.basic.StringGetter";
+        String methodName = "getString";
+
+        searchMethod(className, methodName, true /*shouldExist*/);
+    }
+
+    public void testNonObfuscatedMethod2() {
+        // this should not be obfuscated
+        String className = "com.android.tests.basic.StringGetter";
+        String methodName = "getString2";
+        searchMethod(className, methodName, true /*shouldExist*/);
+    }
+
+    public void testObduscatedMethod() {
+        String className = "com.android.tests.basic.StringGetter";
+        String methodName = "getStringInternal";
+
+        searchMethod(className, methodName, false /*shouldExist*/);
+    }
+
+    private void searchMethod(String className, String methodName, boolean shouldExist) {
+        try {
+            Class<?> theClass = Class.forName(className);
+            Method method = theClass.getDeclaredMethod(methodName, int.class);
+            if (!shouldExist) {
+                fail("Found " + className + "." + methodName);
+            }
+        } catch (ClassNotFoundException e) {
+            fail("Failed to find com.android.tests.basic.StringGetter");
+        } catch (NoSuchMethodException e) {
+            if (shouldExist) {
+                fail("Did not find " + className + "." + methodName);
+            }
+        }
+    }
+
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8c2efe0
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/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.lib">
+    <application />
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/java/com/android/tests/basic/StringGetter.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/java/com/android/tests/basic/StringGetter.java
new file mode 100644
index 0000000..d4ffa1b
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/java/com/android/tests/basic/StringGetter.java
@@ -0,0 +1,31 @@
+package com.android.tests.basic;
+
+import com.android.tests.jarDep.JarDependencyUtil;
+
+public class StringGetter{
+
+    /**
+     * Public method that will not get obfuscated
+     */
+    public static String getString(int foo) {
+         return getStringInternal(foo);
+    }
+
+    /**
+     * Public method that will get obfuscated by the app project.
+     */
+    public static String getString2(int foo) {
+        return getStringInternal(foo);
+    }
+
+    /**
+     * method that will get obfuscated by the library.
+     */
+    public static String getStringInternal(int foo) {
+        return Integer.toString(foo);
+    }
+
+    public static String getStringFromJarDep() {
+        return JarDependencyUtil.getString();
+    }
+}
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/settings.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/settings.gradle
new file mode 100644
index 0000000..9a46a17
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/settings.gradle
@@ -0,0 +1,4 @@
+include 'app'
+include 'test'
+include 'lib'
+include 'jar'
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/build.gradle
new file mode 100644
index 0000000..4edf874
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'com.android.test'
+
+android {
+    compileSdkVersion 19
+    buildToolsVersion = rootProject.buildToolsVersion
+
+    targetProjectPath ':app'
+    targetVariant 'minified'
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..21a998c
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/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.android.tests.basic.test">
+
+      <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
+      <application>
+            <uses-library android:name="android.test.runner" />
+      </application>
+
+      <instrumentation android:name="android.test.InstrumentationTestRunner"
+                       android:targetPackage="com.android.tests.basic"
+                       android:handleProfiling="false"
+                       android:functionalTest="false"
+                       android:label="Tests for com.android.tests.basic"/>
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..7c2efd4
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,39 @@
+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.text);
+
+    }
+
+    /**
+     * 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);
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle
index 3bf0fa0..79e4473 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle
@@ -16,6 +16,12 @@
 
 apply plugin: 'com.android.application'
 
+apply from: "../../commonLocalRepo.gradle"
+
+dependencies {
+    compile 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
 android {
     compileSdkVersion 21
     buildToolsVersion = rootProject.ext.buildToolsVersion
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java
index de80134..f1ae741 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java
@@ -21,6 +21,8 @@
 import android.os.Bundle;
 import android.widget.TextView;
 
+import javax.annotation.Nullable;
+
 public class Main extends Activity
 {
     /** Called when the activity is first created. */
@@ -29,7 +31,12 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         TextView mTextView = (TextView) findViewById(R.id.text);
-        mTextView.setText(getUtility().getValue());
+        mTextView.setText(getString());
+    }
+
+    @Nullable
+    private String getString() {
+        return getUtility().getValue();
     }
 
     public Utility getUtility() {
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle
index 2d7fffe..df07319 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle
@@ -16,3 +16,4 @@
 
 apply from: "../commonHeader.gradle"
 buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+apply from: "../commonLocalRepo.gradle"
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle
index 07230d0..5b31aa0 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle
@@ -16,6 +16,8 @@
 
 apply plugin: 'com.android.test'
 
+apply from: "../../commonLocalRepo.gradle"
+
 android {
     compileSdkVersion 19
     buildToolsVersion = rootProject.buildToolsVersion
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/build.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/build.gradle
new file mode 100644
index 0000000..5509d41
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.application'
+
+apply from: "../../commonLocalRepo.gradle"
+
+dependencies {
+    compile 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion = rootProject.ext.buildToolsVersion
+
+    publishNonDefault true
+
+    defaultConfig {
+        minSdkVersion 8
+    }
+
+    buildTypes {
+        minified.initWith(buildTypes.debug)
+        minified {
+            minifyEnabled true
+            useJack rootProject.ext.useJack
+            proguardFiles 'proguard.txt'
+        }
+    }
+}
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/proguard.txt b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/proguard.txt
new file mode 100644
index 0000000..30ccaf5
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/proguard.txt
@@ -0,0 +1,2 @@
+-dontobfuscate
+
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..54b6471
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.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.text);
+
+    }
+
+    /**
+     * 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);
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e65a22f
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.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/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..f1ae741
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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.tests.basic;
+
+import com.android.tests.utils.Utility;
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import javax.annotation.Nullable;
+
+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 mTextView = (TextView) findViewById(R.id.text);
+        mTextView.setText(getString());
+    }
+
+    @Nullable
+    private String getString() {
+        return getUtility().getValue();
+    }
+
+    public Utility getUtility() {
+        return new Utility();
+    }
+}
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/utils/Utility.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/utils/Utility.java
new file mode 100644
index 0000000..e3569bf
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/utils/Utility.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.tests.utils;
+
+public class Utility {
+
+    public String getValue() {
+        return this.getClass().getName() + " someValue";
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/drawable/icon.png
similarity index 100%
copy from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
copy to build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..2754a4c
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/layout/main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Test App - Basic"
+    android:id="@+id/text"
+    />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..026c1fc
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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>
+    <string name="app_name">_Test-Basic</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/release/res/values/strings.xml
new file mode 100644
index 0000000..80a41e2
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/release/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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>
+    <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/build.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/build.gradle
new file mode 100644
index 0000000..df07319
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/build.gradle
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+apply from: "../commonLocalRepo.gradle"
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/settings.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/settings.gradle
new file mode 100644
index 0000000..6b1c0f8
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 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 'app'
+include 'test'
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/build.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/build.gradle
new file mode 100644
index 0000000..5b31aa0
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/build.gradle
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.test'
+
+apply from: "../../commonLocalRepo.gradle"
+
+android {
+    compileSdkVersion 19
+    buildToolsVersion = rootProject.buildToolsVersion
+
+    targetProjectPath ':app'
+    targetVariant 'minified'
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7d5d647
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.android.tests.basic.test">
+
+
+      <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
+      <application>
+            <uses-library android:name="android.test.runner" />
+      </application>
+
+      <instrumentation android:name="android.test.InstrumentationTestRunner"
+                       android:targetPackage="com.android.tests.basic"
+                       android:handleProfiling="false"
+                       android:functionalTest="false"
+                       android:label="Tests for com.android.tests.basic"/>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..42c4e26
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.tests.basic;
+
+import com.android.tests.utils.Utility;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+    private TextView mTextView;
+    private Main mMain;
+
+    /**
+     * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+     */
+    public MainTest() {
+        super(Main.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mMain = getActivity();
+        // ensure a valid handle to the activity has been returned
+        assertNotNull(mMain);
+        mTextView = (TextView) mMain.findViewById(R.id.text);
+
+    }
+
+    /**
+     * 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);
+        assertEquals(mMain.getUtility().getValue(), mTextView.getText());
+    }
+}
+
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/build.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/app/build.gradle
new file mode 100644
index 0000000..66afaca
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.application'
+
+apply from: "../../commonLocalRepo.gradle"
+
+android {
+    // We need an android.jar that contains Java 6 bytecode, since Jenkins runs on Java 6.
+    compileSdkVersion 19
+    buildToolsVersion = rootProject.buildToolsVersion
+
+    testOptions {
+        unitTests.all {
+            systemProperty 'foo', 'bar'
+        }
+    }
+}
+
+dependencies {
+    compile project(':lib')
+
+    testCompile 'junit:junit:4.12'
+    testCompile 'org.mockito:mockito-core:1.9.5'
+    testCompile 'org.jdeferred:jdeferred-android-aar:1.2.3'
+    testCompile 'commons-logging:commons-logging:1.1.1'
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..867ae14
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 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.android.tests">
+</manifest>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/Foo.java b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/Foo.java
new file mode 100644
index 0000000..ff506f2
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/Foo.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 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.tests;
+
+import com.android.tests.lib.LibFoo;
+
+public class Foo {
+  public String foo() {
+    return "production code";
+  }
+
+  public String callLibFoo() {
+    LibFoo libFoo = new LibFoo();
+    return libFoo.foo();
+  }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/MainActivity.java b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/MainActivity.java
new file mode 100644
index 0000000..8c1a956
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/MainActivity.java
@@ -0,0 +1,5 @@
+package com.android.tests;
+
+import android.app.Activity;
+
+public class MainActivity extends Activity {}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..01f019e
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Unit tests</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/resources/prod_resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/resources/prod_resource_file.txt
new file mode 100644
index 0000000..b551bb0
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/resources/prod_resource_file.txt
@@ -0,0 +1 @@
+prod
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/java/com/android/tests/UnitTest.java b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/java/com/android/tests/UnitTest.java
new file mode 100644
index 0000000..236751c
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/java/com/android/tests/UnitTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 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.tests;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.android.tests.lib.LibFoo;
+
+import android.app.Activity;
+import android.app.Application;
+import android.bluetooth.BluetoothAdapter;
+import android.content.SyncResult;
+import android.content.SyncStats;
+import android.util.ArrayMap;
+import android.os.AsyncTask;
+import android.os.Debug;
+import android.os.PowerManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.io.InputStream;
+import java.net.URL;
+
+public class UnitTest {
+    @Test
+    public void referenceProductionCode() {
+        // Reference production code:
+        Foo foo = new Foo();
+        assertEquals("production code", foo.foo());
+    }
+
+    @Test
+    public void mockFinalMethod() {
+        Activity activity = mock(Activity.class);
+        Application app = mock(Application.class);
+        when(activity.getApplication()).thenReturn(app);
+
+        assertSame(app, activity.getApplication());
+
+        verify(activity).getApplication();
+        verifyNoMoreInteractions(activity);
+    }
+
+    @Test
+    public void mockFinalClass() {
+        BluetoothAdapter adapter = mock(BluetoothAdapter.class);
+        when(adapter.isEnabled()).thenReturn(true);
+
+        assertTrue(adapter.isEnabled());
+
+        verify(adapter).isEnabled();
+        verifyNoMoreInteractions(adapter);
+    }
+
+    @Test
+    public void mockInnerClass() throws Exception {
+        PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
+        when(wakeLock.isHeld()).thenReturn(true);
+        assertTrue(wakeLock.isHeld());
+    }
+
+    @Test
+    public void aarDependencies() throws Exception {
+        org.jdeferred.Deferred<Integer, Integer, Integer> deferred =
+                new org.jdeferred.impl.DeferredObject<Integer, Integer, Integer>();
+        org.jdeferred.Promise promise = deferred.promise();
+        deferred.resolve(42);
+        assertTrue(promise.isResolved());
+    }
+
+    @Test
+    public void exceptions() {
+        try {
+            ArrayMap map = new ArrayMap();
+            map.isEmpty();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals(RuntimeException.class, e.getClass());
+            assertTrue(e.getMessage().contains("isEmpty"));
+            assertTrue(e.getMessage().contains("not mocked"));
+            assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+        }
+
+        try {
+            Debug.getThreadAllocCount();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals(RuntimeException.class, e.getClass());
+            assertTrue(e.getMessage().contains("getThreadAllocCount"));
+            assertTrue(e.getMessage().contains("not mocked"));
+            assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+        }
+
+    }
+
+    @Test
+    public void enums() throws Exception {
+        assertNotNull(AsyncTask.Status.RUNNING);
+        assertNotEquals(AsyncTask.Status.RUNNING, AsyncTask.Status.FINISHED);
+
+        assertEquals(AsyncTask.Status.FINISHED, AsyncTask.Status.valueOf("FINISHED"));
+        assertEquals(1, AsyncTask.Status.PENDING.ordinal());
+        assertEquals("RUNNING", AsyncTask.Status.RUNNING.name());
+
+        assertEquals(AsyncTask.Status.RUNNING, Enum.valueOf(AsyncTask.Status.class, "RUNNING"));
+
+        AsyncTask.Status[] values = AsyncTask.Status.values();
+        assertEquals(3, values.length);
+        assertEquals(AsyncTask.Status.FINISHED, values[0]);
+        assertEquals(AsyncTask.Status.PENDING, values[1]);
+        assertEquals(AsyncTask.Status.RUNNING, values[2]);
+    }
+
+    @Test
+    public void instanceFields() throws Exception {
+        SyncResult result = mock(SyncResult.class);
+        Field statsField = result.getClass().getField("stats");
+        SyncStats syncStats = mock(SyncStats.class);
+        statsField.set(result, syncStats);
+
+        syncStats.numDeletes = 42;
+        assertEquals(42, result.stats.numDeletes);
+    }
+
+    @Test
+    public void javaResourcesOnClasspath() throws Exception {
+        URL url = UnitTest.class.getClassLoader().getResource("resource_file.txt");
+        assertNotNull(url);
+
+        InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("resource_file.txt");
+        assertNotNull(stream);
+        byte[] line = new byte[1024];
+        assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+        String s = new String(line, "UTF-8").trim();
+        assertEquals("success", s);
+    }
+
+    @Test
+    public void prodJavaResourcesOnClasspath() throws Exception {
+        URL url = UnitTest.class.getClassLoader().getResource("prod_resource_file.txt");
+        assertNotNull(url);
+
+        InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("prod_resource_file.txt");
+        assertNotNull(stream);
+        byte[] line = new byte[1024];
+        assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+        String s = new String(line, "UTF-8").trim();
+        assertEquals("prod", s);
+    }
+
+    @Test
+    public void libJavaResourcesOnClasspath() throws Exception {
+        URL url = UnitTest.class.getClassLoader().getResource("lib_prod_resource_file.txt");
+        assertNotNull(url);
+
+        InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_prod_resource_file.txt");
+        assertNotNull(stream);
+        byte[] line = new byte[1024];
+        assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+        String s = new String(line, "UTF-8").trim();
+        assertEquals("lib prod", s);
+    }
+
+    @Test
+    public void testLibJavaResourcesNotOnClasspath() throws Exception {
+        URL url = UnitTest.class.getClassLoader().getResource("lib_resource_file.txt");
+        assertNull(url);
+
+        InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_resource_file.txt");
+        assertNull(stream);
+    }
+
+    @Test
+    public void prodRClass() {
+        int id = R.string.app_name;
+        assertTrue(id > 0);
+    }
+
+    @Test
+    @Ignore
+    public void thisIsIgnored() {
+        // Just excercise more JUnit features.
+    }
+
+    @Test
+    public void taskConfiguration() {
+        // This property is set in build.gradle:
+        assertEquals("bar", System.getProperty("foo"));
+    }
+
+    @Test
+    public void commonsLogging() {
+        Log log = LogFactory.getLog(getClass());
+        log.info("I can use commons-logging!");
+    }
+
+    @Test
+    public void libraryCode() {
+        LibFoo libFoo = new LibFoo();
+        assertEquals("library code", libFoo.foo());
+
+        Foo foo = new Foo();
+        assertEquals("library code", foo.callLibFoo());
+    }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/resources/resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/resources/resource_file.txt
new file mode 100644
index 0000000..2e9ba47
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/resources/resource_file.txt
@@ -0,0 +1 @@
+success
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/build.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/build.gradle
new file mode 100644
index 0000000..ea987b9
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/build.gradle
@@ -0,0 +1,3 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/build.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/build.gradle
new file mode 100644
index 0000000..27abd09
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/build.gradle
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.library'
+
+apply from: "../../commonLocalRepo.gradle"
+
+android {
+    // We need an android.jar that contains Java 6 bytecode, since Jenkins runs on Java 6.
+    compileSdkVersion 19
+    buildToolsVersion = rootProject.buildToolsVersion
+    
+    testOptions {
+        unitTests.all {
+            systemProperty 'foo', 'bar'
+        }
+    }
+}
+
+dependencies {
+    testCompile 'junit:junit:4.12'
+    testCompile 'org.mockito:mockito-core:1.9.5'
+    testCompile 'org.jdeferred:jdeferred-android-aar:1.2.3'
+    testCompile 'commons-logging:commons-logging:1.1.1'
+}
+
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4577858
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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.android.tests.lib">
+</manifest>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/java/com/android/tests/lib/LibFoo.java b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/java/com/android/tests/lib/LibFoo.java
new file mode 100644
index 0000000..3cdccde
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/java/com/android/tests/lib/LibFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 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.tests.lib;
+
+public class LibFoo {
+  public String foo() {
+    return "library code";
+  }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/res/values/strings.xml
new file mode 100644
index 0000000..01f019e
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Unit tests</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/resources/lib_prod_resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/resources/lib_prod_resource_file.txt
new file mode 100644
index 0000000..9537601
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/resources/lib_prod_resource_file.txt
@@ -0,0 +1 @@
+lib prod
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/java/com/android/tests/lib/UnitTest.java b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/java/com/android/tests/lib/UnitTest.java
new file mode 100644
index 0000000..68f968c
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/java/com/android/tests/lib/UnitTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2015 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.tests.lib;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Application;
+import android.bluetooth.BluetoothAdapter;
+import android.content.SyncResult;
+import android.content.SyncStats;
+import android.util.ArrayMap;
+import android.os.AsyncTask;
+import android.os.Debug;
+import android.os.PowerManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.io.InputStream;
+import java.net.URL;
+
+public class UnitTest {
+    @Test
+    public void referenceProductionCode() {
+        // Reference production code:
+        LibFoo foo = new LibFoo();
+        assertEquals("library code", foo.foo());
+    }
+
+    @Test
+    public void mockFinalMethod() {
+        Activity activity = mock(Activity.class);
+        Application app = mock(Application.class);
+        when(activity.getApplication()).thenReturn(app);
+
+        assertSame(app, activity.getApplication());
+
+        verify(activity).getApplication();
+        verifyNoMoreInteractions(activity);
+    }
+
+    @Test
+    public void mockFinalClass() {
+        BluetoothAdapter adapter = mock(BluetoothAdapter.class);
+        when(adapter.isEnabled()).thenReturn(true);
+
+        assertTrue(adapter.isEnabled());
+
+        verify(adapter).isEnabled();
+        verifyNoMoreInteractions(adapter);
+    }
+
+    @Test
+    public void mockInnerClass() throws Exception {
+        PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
+        when(wakeLock.isHeld()).thenReturn(true);
+        assertTrue(wakeLock.isHeld());
+    }
+
+    @Test
+    public void aarDependencies() throws Exception {
+        org.jdeferred.Deferred<Integer, Integer, Integer> deferred =
+                new org.jdeferred.impl.DeferredObject<Integer, Integer, Integer>();
+        org.jdeferred.Promise promise = deferred.promise();
+        deferred.resolve(42);
+        assertTrue(promise.isResolved());
+    }
+
+    @Test
+    public void exceptions() {
+        try {
+            ArrayMap map = new ArrayMap();
+            map.isEmpty();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals(RuntimeException.class, e.getClass());
+            assertTrue(e.getMessage().contains("isEmpty"));
+            assertTrue(e.getMessage().contains("not mocked"));
+            assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+        }
+
+        try {
+            Debug.getThreadAllocCount();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals(RuntimeException.class, e.getClass());
+            assertTrue(e.getMessage().contains("getThreadAllocCount"));
+            assertTrue(e.getMessage().contains("not mocked"));
+            assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+        }
+
+    }
+
+    @Test
+    public void enums() throws Exception {
+        assertNotNull(AsyncTask.Status.RUNNING);
+        assertNotEquals(AsyncTask.Status.RUNNING, AsyncTask.Status.FINISHED);
+
+        assertEquals(AsyncTask.Status.FINISHED, AsyncTask.Status.valueOf("FINISHED"));
+        assertEquals(1, AsyncTask.Status.PENDING.ordinal());
+        assertEquals("RUNNING", AsyncTask.Status.RUNNING.name());
+
+        assertEquals(AsyncTask.Status.RUNNING, Enum.valueOf(AsyncTask.Status.class, "RUNNING"));
+
+        AsyncTask.Status[] values = AsyncTask.Status.values();
+        assertEquals(3, values.length);
+        assertEquals(AsyncTask.Status.FINISHED, values[0]);
+        assertEquals(AsyncTask.Status.PENDING, values[1]);
+        assertEquals(AsyncTask.Status.RUNNING, values[2]);
+    }
+
+    @Test
+    public void instanceFields() throws Exception {
+        SyncResult result = mock(SyncResult.class);
+        Field statsField = result.getClass().getField("stats");
+        SyncStats syncStats = mock(SyncStats.class);
+        statsField.set(result, syncStats);
+
+        syncStats.numDeletes = 42;
+        assertEquals(42, result.stats.numDeletes);
+    }
+
+    @Test
+    public void javaResourcesOnClasspath() throws Exception {
+        URL url = UnitTest.class.getClassLoader().getResource("lib_resource_file.txt");
+        assertNotNull(url);
+
+        InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_resource_file.txt");
+        assertNotNull(stream);
+        byte[] line = new byte[1024];
+        assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+        String s = new String(line, "UTF-8").trim();
+        assertEquals("success", s);
+    }
+
+    @Test
+    public void prodJavaResourcesOnClasspath() throws Exception {
+        URL url = UnitTest.class.getClassLoader().getResource("lib_prod_resource_file.txt");
+        assertNotNull(url);
+
+        InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_prod_resource_file.txt");
+        assertNotNull(stream);
+        byte[] line = new byte[1024];
+        assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+        String s = new String(line, "UTF-8").trim();
+        assertEquals("lib prod", s);
+    }
+
+    @Test
+    public void prodRClass() {
+        int id = R.string.app_name;
+        assertTrue(id > 0);
+    }
+
+    @Test
+    @Ignore
+    public void thisIsIgnored() {
+        // Just excercise more JUnit features.
+    }
+
+    @Test
+    public void taskConfiguration() {
+        // This property is set in build.gradle:
+        assertEquals("bar", System.getProperty("foo"));
+    }
+
+    @Test
+    public void commonsLogging() {
+        Log log = LogFactory.getLog(getClass());
+        log.info("I can use commons-logging!");
+    }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/resources/lib_resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/resources/lib_resource_file.txt
new file mode 100644
index 0000000..2e9ba47
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/resources/lib_resource_file.txt
@@ -0,0 +1 @@
+success
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/settings.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/settings.gradle
new file mode 100644
index 0000000..2f4a882
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 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 'app'
+include 'lib'
diff --git a/build-system/integration-test/test-projects/vectorDrawables/build.gradle b/build-system/integration-test/test-projects/vectorDrawables/build.gradle
index 238f69a..10a58e8 100644
--- a/build-system/integration-test/test-projects/vectorDrawables/build.gradle
+++ b/build-system/integration-test/test-projects/vectorDrawables/build.gradle
@@ -13,5 +13,11 @@
         minSdkVersion 19
     }
 
-    preprocessResources = true
+    preprocessingOptions {
+      densities = ["hdpi"]
+      densities += "xhdpi"
+    }
+
+    // Don't modify files when merging.
+    aaptOptions.cruncherEnabled = false
 }
diff --git a/build-system/integration-test/test-projects/vectorDrawables/gradle.properties b/build-system/integration-test/test-projects/vectorDrawables/gradle.properties
new file mode 100644
index 0000000..bbf0dc8
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/gradle.properties
@@ -0,0 +1 @@
+com.android.build.gradle.experimentalPreprocessResources=true
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-fr/french_heart.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-fr/french_heart.xml
new file mode 100644
index 0000000..8419d57
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-fr/french_heart.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="256dp"
+    android:width="256dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+    <!-- draw a path -->
+    <path
+        android:fillColor="#00ff00"
+        android:pathData="M20.5,9.5
+                        c-1.965,0,-3.83,1.268,-4.5,3
+                        c-0.17,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-hdpi/special_heart.png b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-hdpi/special_heart.png
new file mode 100644
index 0000000..954fb1c
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-hdpi/special_heart.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v16/modern_heart.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v16/modern_heart.xml
new file mode 100644
index 0000000..5af0e5a
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v16/modern_heart.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="256dp"
+    android:width="256dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+    <!-- draw a path -->
+    <path
+        android:fillColor="#ff0000"
+        android:pathData="M20.5,9.5
+                        c-1.965,0,-3.83,1.268,-4.5,3
+                        c-0.17,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v22/no_need.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v22/no_need.xml
new file mode 100644
index 0000000..5af0e5a
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v22/no_need.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="256dp"
+    android:width="256dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+    <!-- draw a path -->
+    <path
+        android:fillColor="#ff0000"
+        android:pathData="M20.5,9.5
+                        c-1.965,0,-3.83,1.268,-4.5,3
+                        c-0.17,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable/special_heart.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable/special_heart.xml
new file mode 100644
index 0000000..5af0e5a
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable/special_heart.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="256dp"
+    android:width="256dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+    <!-- draw a path -->
+    <path
+        android:fillColor="#ff0000"
+        android:pathData="M20.5,9.5
+                        c-1.965,0,-3.83,1.268,-4.5,3
+                        c-0.17,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/manifest-merger/build.gradle b/build-system/manifest-merger/build.gradle
index 4ba6a01..3ca48a0 100644
--- a/build-system/manifest-merger/build.gradle
+++ b/build-system/manifest-merger/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 evaluationDependsOn(':base:sdklib')
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java
index a8b815d..a194534 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java
@@ -21,7 +21,8 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.concurrency.GuardedBy;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.google.common.collect.ImmutableMap;
 
 import java.util.ArrayList;
@@ -57,7 +58,7 @@
  * <ul>
  *     <li>{@link com.android.manifmerger.Actions.ActionType} to identify whether the action
  *     applies to an attribute or an element.</li>
- *     <li>{@link com.android.manifmerger.Actions.ActionLocation} to identify the source xml
+ *     <li>{@link com.android.ide.common.blame.SourceFilePosition} to identify the source xml
  *     location for the node.</li>
  * </ul>
  *
@@ -123,8 +124,8 @@
             mRecords.put(storageKey, nodeDecisionTree);
         }
         Actions.NodeRecord record = new Actions.NodeRecord(Actions.ActionType.IMPLIED,
-                new Actions.ActionLocation(
-                        xmlElement.getDocument().getSourceLocation(),
+                new SourceFilePosition(
+                        xmlElement.getDocument().getSourceFile(),
                         xmlElement.getDocument().getRootNode().getPosition()),
                 xmlElement.getOriginalId(),
                 reason,
@@ -159,8 +160,8 @@
             XmlElement targetElement) {
 
         Actions.NodeRecord record = new Actions.NodeRecord(actionType,
-                new Actions.ActionLocation(
-                        targetElement.getDocument().getSourceLocation(),
+                new SourceFilePosition(
+                        targetElement.getDocument().getSourceFile(),
                         targetElement.getPosition()),
                 targetElement.getOriginalId(),
                 null, /* reason */
@@ -215,15 +216,15 @@
      */
     synchronized void recordAttributeAction(
             @NonNull XmlAttribute attribute,
-            @NonNull PositionXmlParser.Position attributePosition,
+            @NonNull SourcePosition attributePosition,
             @NonNull Actions.ActionType actionType,
             @Nullable AttributeOperationType attributeOperationType) {
 
         XmlElement originElement = attribute.getOwnerElement();
         Actions.AttributeRecord attributeRecord = new Actions.AttributeRecord(
                 actionType,
-                new Actions.ActionLocation(
-                        originElement.getDocument().getSourceLocation(),
+                new SourceFilePosition(
+                        originElement.getDocument().getSourceFile(),
                         attributePosition),
                 attribute.getOriginalId(),
                 null, /* reason */
@@ -260,8 +261,8 @@
         List<Actions.AttributeRecord> attributeRecords = getAttributeRecords(attribute);
         Actions.AttributeRecord attributeRecord = new Actions.AttributeRecord(
                 Actions.ActionType.REJECTED,
-                new Actions.ActionLocation(
-                        implicitAttributeOwner.getDocument().getSourceLocation(),
+                new SourceFilePosition(
+                        implicitAttributeOwner.getDocument().getSourceFile(),
                         implicitAttributeOwner.getPosition()),
                 attribute.getOriginalId(),
                 null, /* reason */
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java
index 71544a1..79c8064 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java
@@ -20,8 +20,10 @@
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 import com.android.annotations.concurrency.Immutable;
+import com.android.ide.common.blame.MessageJsonSerializer;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
 import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -33,10 +35,7 @@
 import com.google.gson.JsonDeserializationContext;
 import com.google.gson.JsonDeserializer;
 import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
 import com.google.gson.JsonParseException;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
 
 import org.xml.sax.SAXException;
 
@@ -192,12 +191,12 @@
     public abstract static class Record {
 
         @NonNull protected final ActionType mActionType;
-        @NonNull protected final ActionLocation mActionLocation;
+        @NonNull protected final SourceFilePosition mActionLocation;
         @NonNull protected final XmlNode.NodeKey mTargetId;
         @Nullable protected final String mReason;
 
         private Record(@NonNull ActionType actionType,
-                @NonNull ActionLocation actionLocation,
+                @NonNull SourceFilePosition actionLocation,
                 @NonNull XmlNode.NodeKey targetId,
                 @Nullable String reason) {
             mActionType = Preconditions.checkNotNull(actionType);
@@ -210,7 +209,7 @@
             return mActionType;
         }
 
-        public ActionLocation getActionLocation() {
+        public SourceFilePosition getActionLocation() {
             return mActionLocation;
         }
 
@@ -237,7 +236,7 @@
         private final NodeOperationType mNodeOperationType;
 
         NodeRecord(@NonNull ActionType actionType,
-                @NonNull ActionLocation actionLocation,
+                @NonNull SourceFilePosition actionLocation,
                 @NonNull XmlNode.NodeKey targetId,
                 @Nullable String reason,
                 @NonNull NodeOperationType nodeOperationType) {
@@ -264,7 +263,7 @@
 
         AttributeRecord(
                 @NonNull ActionType actionType,
-                @NonNull ActionLocation actionLocation,
+                @NonNull SourceFilePosition actionLocation,
                 @NonNull XmlNode.NodeKey targetId,
                 @Nullable String reason,
                 @Nullable AttributeOperationType operationType) {
@@ -285,80 +284,10 @@
         }
     }
 
-    /**
-     * Defines an action location which is composed of a pointer to the source location (e.g. a
-     * file) and a position within that source location.
-     */
-    public static final class ActionLocation {
-
-        private final XmlLoader.SourceLocation mSourceLocation;
-
-        private final PositionXmlParser.Position mPosition;
-
-        public ActionLocation(@NonNull XmlLoader.SourceLocation sourceLocation,
-                @NonNull PositionXmlParser.Position position) {
-            mSourceLocation = Preconditions.checkNotNull(sourceLocation);
-            mPosition = Preconditions.checkNotNull(position);
-        }
-
-        public PositionXmlParser.Position getPosition() {
-            return mPosition;
-        }
-
-        public XmlLoader.SourceLocation getSourceLocation() {
-            return mSourceLocation;
-        }
-
-        @Override
-        public String toString() {
-            String toString = mSourceLocation.print(true);
-            if (mPosition != null) {
-                toString += ":" + mPosition.getLine() + ":" + mPosition.getColumn();
-            }
-            return toString;
-        }
-
-        private static final class ActionLocationAdapter
-                implements JsonSerializer<ActionLocation>, JsonDeserializer<ActionLocation> {
-
-            private static final String POSITION_ELEMENT = "position";
-
-            private static final String LINE_ATTRIBUTE = "line";
-
-            private static final String COLUMN_ATTRIBUTE = "col";
-
-            private static final String OFFSET_ATTRIBUTE = "offset";
-
-            @Override
-            public ActionLocation deserialize(JsonElement json, Type typeOfT,
-                    JsonDeserializationContext context) throws JsonParseException {
-                XmlLoader.SourceLocation sourceLocation =
-                        context.deserialize(json.getAsJsonObject().get(POSITION_ELEMENT),
-                                XmlLoader.SourceLocation.class);
-                PositionImpl position = new PositionImpl(
-                        json.getAsJsonObject().get(LINE_ATTRIBUTE).getAsInt(),
-                        json.getAsJsonObject().get(COLUMN_ATTRIBUTE).getAsInt(),
-                        json.getAsJsonObject().get(OFFSET_ATTRIBUTE).getAsInt());
-                return new ActionLocation(sourceLocation, position);
-            }
-
-            @Override
-            public JsonElement serialize(ActionLocation src, Type typeOfSrc,
-                    JsonSerializationContext context) {
-                JsonObject jsonObject = new JsonObject();
-                jsonObject.add(POSITION_ELEMENT, context.serialize(src.getSourceLocation()));
-                jsonObject.addProperty(LINE_ATTRIBUTE, src.getPosition().getLine());
-                jsonObject.addProperty(COLUMN_ATTRIBUTE, src.getPosition().getColumn());
-                jsonObject.addProperty(OFFSET_ATTRIBUTE, src.getPosition().getOffset());
-                return jsonObject;
-            }
-        }
-    }
-
     public String persist() throws IOException  {
         GsonBuilder gson = new GsonBuilder().setPrettyPrinting();
         gson.enableComplexMapKeySerialization();
-        gson.registerTypeAdapter(ActionLocation.class, new ActionLocation.ActionLocationAdapter());
+        MessageJsonSerializer.registerTypeAdapters(gson);
         return gson.create().toJson(this);
     }
 
@@ -392,14 +321,14 @@
         GsonBuilder gsonBuilder = new GsonBuilder();
         gsonBuilder.enableComplexMapKeySerialization();
         gsonBuilder.registerTypeAdapter(XmlNode.NodeName.class, new NodeNameDeserializer());
-        gsonBuilder.registerTypeAdapter(ActionLocation.class, new ActionLocation.ActionLocationAdapter());
+        MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
         return gsonBuilder.create();
     }
 
     public ImmutableMultimap<Integer, Record> getResultingSourceMapping(XmlDocument xmlDocument)
             throws ParserConfigurationException, SAXException, IOException {
 
-        XmlLoader.SourceLocation inMemory = XmlLoader.UNKNOWN;
+        SourceFile inMemory = SourceFile.UNKNOWN;
 
         XmlDocument loadedWithLineNumbers = XmlLoader.load(
                 xmlDocument.getSelectors(),
@@ -422,13 +351,13 @@
         if (decisionTreeRecord != null) {
             Actions.NodeRecord nodeRecord = findNodeRecord(decisionTreeRecord);
             if (nodeRecord != null) {
-                mappings.put(element.getPosition().getLine(), nodeRecord);
+                mappings.put(element.getPosition().getStartLine(), nodeRecord);
             }
             for (XmlAttribute xmlAttribute : element.getAttributes()) {
                 Actions.AttributeRecord attributeRecord = findAttributeRecord(decisionTreeRecord,
                         xmlAttribute);
                 if (attributeRecord != null) {
-                    mappings.put(xmlAttribute.getPosition().getLine(), attributeRecord);
+                    mappings.put(xmlAttribute.getPosition().getStartLine(), attributeRecord);
                 }
             }
         }
@@ -447,12 +376,12 @@
 
         StringBuilder actualMappings = new StringBuilder();
         String line;
-        int count = 1;
+        int count = 0;
         while ((line = lineReader.readLine()) != null) {
-            actualMappings.append(count).append(line).append("\n");
+            actualMappings.append(count + 1).append(line).append("\n");
             if (resultingSourceMapping.containsKey(count)) {
                 for (Record record : resultingSourceMapping.get(count)) {
-                    actualMappings.append(count).append("-->")
+                    actualMappings.append(count + 1).append("-->")
                             .append(record.getActionLocation().toString())
                             .append("\n");
                 }
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java
index c4f95e5..4d35de4 100755
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java
@@ -482,7 +482,7 @@
             pkg = manifest.getAttribute("package");
         }
 
-        if (pkg == null || pkg.length() == 0) {
+        if (pkg == null || pkg.isEmpty()) {
             // We can't adjust FQCNs if we don't know the root package name.
             // It's not a proper manifest if this is missing anyway.
             assert manifest != null;
@@ -509,7 +509,7 @@
 
                         // We know it's a shortened FQCN if it starts with a dot
                         // or does not contain any dot.
-                        if (value != null && value.length() > 0 &&
+                        if (value != null && !value.isEmpty() &&
                                 (value.indexOf('.') == -1 || value.charAt(0) == '.')) {
                             if (value.charAt(0) == '.') {
                                 value = pkg + value;
@@ -540,7 +540,7 @@
             pkg = manifest.getAttribute("package");
         }
 
-        if (pkg == null || pkg.length() == 0) {
+        if (pkg == null || pkg.isEmpty()) {
             return;
         }
 
@@ -602,7 +602,7 @@
 
         for (String attrName : new String[] { "name", "backupAgent" }) {
             String libValue  = getAttributeValue(libApp, attrName);
-            if (libValue == null || libValue.length() == 0) {
+            if (libValue == null || libValue.isEmpty()) {
                 // Nothing to do if the attribute is not defined in the lib.
                 continue;
             }
@@ -712,7 +712,7 @@
 
         nextSource: for (Element src : findElements(libDoc, path)) {
             String name = getAttributeValue(src, keyAttr);
-            if (name.length() == 0) {
+            if (name.isEmpty()) {
                 mLog.error(Severity.ERROR,
                         xmlFileAndLine(src),
                         "Undefined '%1$s' attribute in %2$s.",
@@ -832,11 +832,11 @@
         for (Element src : findElements(libDoc, path)) {
             Attr attr = src.getAttributeNodeNS(NS_URI, keyAttr);
             String name = attr == null ? "" : attr.getNodeValue().trim();  //$NON-NLS-1$
-            if (name.length() == 0) {
+            if (name.isEmpty()) {
                 if (alternateKeyAttr != null) {
                     attr = src.getAttributeNodeNS(NS_URI, alternateKeyAttr);
                     String s = attr == null ? "" : attr.getNodeValue().trim(); //$NON-NLS-1$
-                    if (s.length() != 0) {
+                    if (!s.isEmpty()) {
                         // This element lacks the keyAttr but has the alternateKeyAttr. Skip it.
                         continue;
                     }
@@ -859,7 +859,7 @@
                         "Manifest has more than one %1$s[@%2$s=%3$s] element.",
                         path, keyAttr, name);
             }
-            if (dests.size() > 0) {
+            if (!dests.isEmpty()) {
 
                 attr = src.getAttributeNodeNS(NS_URI, requiredAttr);
                 String value = attr == null ? "true" : attr.getNodeValue();    //$NON-NLS-1$
@@ -987,7 +987,7 @@
         for (Element dest : findElements(mMainDoc, path)) {
             Attr attr = dest.getAttributeNodeNS(NS_URI, keyAttr);
             String value = attr == null ? "" : attr.getNodeValue().trim();   //$NON-NLS-1$
-            if (value.length() != 0) {
+            if (!value.isEmpty()) {
                 try {
                     // Note that the value can be an hex number such as 0x00020001 so we
                     // need Integer.decode instead of Integer.parseInt.
@@ -1030,7 +1030,7 @@
         for (Element src : findElements(libDoc, path)) {
             Attr attr = src.getAttributeNodeNS(NS_URI, keyAttr);
             String value = attr == null ? "" : attr.getNodeValue().trim();   //$NON-NLS-1$
-            if (value.length() != 0) {
+            if (!value.isEmpty()) {
                 try {
                     // See comment on Long.decode above.
                     long version = Long.decode(value);
@@ -1195,7 +1195,7 @@
         assert s != null;
         s = s.trim();
         try {
-            if (s.length() > 0) {
+            if (!s.isEmpty()) {
                 destValue.set(Integer.parseInt(s));
                 destImplied.set(false);
             }
@@ -1228,7 +1228,7 @@
         assert s != null;
         s = s.trim();
         try {
-            if (s.length() > 0) {
+            if (!s.isEmpty()) {
                 srcValue.set(Integer.parseInt(s));
                 srcImplied.set(false);
             }
@@ -1277,7 +1277,7 @@
             short t = prev.getNodeType();
             if (t == Node.TEXT_NODE) {
                 String text = prev.getNodeValue();
-                if (text == null || text.trim().length() != 0) {
+                if (text == null || !text.trim().isEmpty()) {
                     // Not whitespace, we don't want it.
                     break;
                 }
@@ -1322,7 +1322,7 @@
         while (target != null) {
             if (target.getNodeType() == Node.TEXT_NODE) {
                 String text = target.getNodeValue();
-                if (text == null || text.trim().length() != 0) {
+                if (text == null || !text.trim().isEmpty()) {
                     // Not whitespace, insert after.
                     break;
                 }
@@ -1599,7 +1599,7 @@
                 root.removeChild(child);
                 // If there's some whitespace just before that element, clean it up too.
                 while (prev != null && prev.getNodeType() == Node.TEXT_NODE) {
-                    if (prev.getNodeValue().trim().length() == 0) {
+                    if (prev.getNodeValue().trim().isEmpty()) {
                         Node prevPrev = prev.getPreviousSibling();
                         root.removeChild(prev);
                         prev = prevPrev;
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java
index 99f0ada..c75e715 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java
@@ -24,6 +24,8 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.concurrency.Immutable;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.utils.ILogger;
 import com.android.utils.Pair;
 import com.android.utils.SdkUtils;
@@ -122,26 +124,16 @@
                 loadedMainManifestInfo.getXmlDocument().getPackage();
         if (!mainPackageAttribute.isPresent()) {
             mergingReportBuilder.addMessage(
-                    loadedMainManifestInfo.getXmlDocument().getSourceLocation(), 0, 0,
+                    loadedMainManifestInfo.getXmlDocument().getSourceFile(),
                     MergingReport.Record.Severity.ERROR,
                     String.format(
                             "Main AndroidManifest.xml at %1$s manifest:package attribute "
                                     + "is not declared",
-                            loadedMainManifestInfo.getXmlDocument().getSourceLocation()
+                            loadedMainManifestInfo.getXmlDocument().getSourceFile()
                                     .print(true)));
             return mergingReportBuilder.build();
         }
 
-        // check for placeholders presence.
-        Map<String, Object> finalPlaceHolderValues = mPlaceHolderValues;
-        if (!mPlaceHolderValues.containsKey(APPLICATION_ID)) {
-            finalPlaceHolderValues =
-                    ImmutableMap.<String, Object>builder().putAll(mPlaceHolderValues)
-                            .put(PACKAGE_NAME, mainPackageAttribute.get().getValue())
-                            .put(APPLICATION_ID, mainPackageAttribute.get().getValue())
-                            .build();
-        }
-
         // perform system property injection
         performSystemPropertiesInjection(mergingReportBuilder,
                 loadedMainManifestInfo.getXmlDocument());
@@ -187,7 +179,7 @@
                                 packageAttribute.get().getValue(),
                                 mainPackageAttribute.get().getValue(),
                                 mainPackageAttribute.get().printPosition(),
-                                packageAttribute.get().getSourceLocation().print(true))
+                                packageAttribute.get().getSourceFile().print(true))
                         : String.format(
                                 "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
                                         + "\thas a different value=(%3$s) "
@@ -197,7 +189,7 @@
                                 mainPackageAttribute.get().getValue(),
                                 mainPackageAttribute.get().printPosition());
                 mergingReportBuilder.addMessage(
-                        overlayDocument.getXmlDocument().getSourceLocation(), 0, 0,
+                        overlayDocument.getXmlDocument().getSourceFile(),
                         MergingReport.Record.Severity.ERROR,
                         message);
                 return mergingReportBuilder.build();
@@ -253,7 +245,7 @@
             // been overridden so the problem was transient. However, with the final document
             // ready, all placeholders values must have been provided.
             KeyBasedValueResolver<String> placeHolderValueResolver =
-                    new MapBasedKeyBasedValueResolver<String>(finalPlaceHolderValues);
+                    new MapBasedKeyBasedValueResolver<String>(mPlaceHolderValues);
             PlaceholderHandler placeholderHandler = new PlaceholderHandler();
             placeholderHandler.visit(
                     mMergeType,
@@ -424,7 +416,7 @@
         // check for placeholders presence, switch first the packageName and application id if
         // it is not explicitly set.
         Map<String, Object> finalPlaceHolderValues = mPlaceHolderValues;
-        if (!mPlaceHolderValues.containsKey("applicationId")) {
+        if (!mPlaceHolderValues.containsKey(PlaceholderHandler.APPLICATION_ID)) {
             String packageName = manifestInfo.getMainManifestPackageName().isPresent()
                     ? manifestInfo.getMainManifestPackageName().get()
                     : xmlDocument.getPackageName();
@@ -435,9 +427,11 @@
                     builder.put(entry);
                 }
             }
-            finalPlaceHolderValues = builder.put(PlaceholderHandler.PACKAGE_NAME, packageName)
-                            .put(PlaceholderHandler.APPLICATION_ID, packageName)
-                            .build();
+            builder.put(PlaceholderHandler.PACKAGE_NAME, packageName);
+            if (mMergeType != MergeType.LIBRARY) {
+                builder.put(PlaceholderHandler.APPLICATION_ID, packageName);
+            }
+            finalPlaceHolderValues = builder.build();
         }
 
         KeyBasedValueResolver<String> placeHolderValueResolver =
@@ -460,7 +454,7 @@
                 .validate(mergingReportBuilder, lowerPriorityDocument.getXmlDocument());
         if (validationResult == MergingReport.Result.ERROR) {
             mergingReportBuilder.addMessage(
-                    lowerPriorityDocument.getXmlDocument().getSourceLocation(), 0, 0,
+                    lowerPriorityDocument.getXmlDocument().getSourceFile(),
                     MergingReport.Record.Severity.ERROR,
                     "Validation failed, exiting");
             return Optional.absent();
@@ -634,9 +628,7 @@
                     to.getXml().getAttributeNode(systemProperty.toCamelCase()), null);
             actionRecorder.recordAttributeAction(xmlAttribute, new Actions.AttributeRecord(
                     Actions.ActionType.INJECTED,
-                    new Actions.ActionLocation(
-                            to.getSourceLocation(),
-                            PositionImpl.UNKNOWN),
+                    new SourceFilePosition(to.getSourceFile(), SourcePosition.UNKNOWN),
                     xmlAttribute.getId(),
                     null, /* reason */
                     null /* attributeOperationType */));
@@ -661,9 +653,7 @@
             actionRecorder.recordAttributeAction(xmlAttribute,
                     new Actions.AttributeRecord(
                             Actions.ActionType.INJECTED,
-                            new Actions.ActionLocation(
-                                    to.getSourceLocation(),
-                                    PositionImpl.UNKNOWN),
+                            new SourceFilePosition(to.getSourceFile(), SourcePosition.UNKNOWN),
                             xmlAttribute.getId(),
                             null, /* reason */
                             null /* attributeOperationType */
@@ -695,8 +685,8 @@
                 XmlElement xmlElement = new XmlElement(useSdk, document);
                 Actions.NodeRecord nodeRecord = new Actions.NodeRecord(
                         Actions.ActionType.INJECTED,
-                        new Actions.ActionLocation(xmlElement.getSourceLocation(),
-                                PositionImpl.UNKNOWN),
+                        new SourceFilePosition(xmlElement.getSourceFile(),
+                                SourcePosition.UNKNOWN),
                         xmlElement.getId(),
                         "use-sdk injection requested",
                         NodeOperationType.STRICT);
@@ -877,7 +867,7 @@
             /**
              * Do no perform placeholders replacement.
              */
-            NO_PLACEHOLDER_REPLACEMENT;
+            NO_PLACEHOLDER_REPLACEMENT
         }
 
         /**
@@ -991,8 +981,14 @@
             // provide some free placeholders values.
             ImmutableMap<SystemProperty, Object> systemProperties = mSystemProperties.build();
             if (systemProperties.containsKey(SystemProperty.PACKAGE)) {
+                // if the package is provided, make it available for placeholder replacement.
                 mPlaceholders.put(PACKAGE_NAME, systemProperties.get(SystemProperty.PACKAGE));
-                mPlaceholders.put(APPLICATION_ID, systemProperties.get(SystemProperty.PACKAGE));
+                // as well as applicationId since package system property overrides everything
+                // but not when output is a library since only the final (application)
+                // application Id should be used to replace libraries "applicationId" placeholders.
+                if (mMergeType != MergeType.LIBRARY) {
+                    mPlaceholders.put(APPLICATION_ID, systemProperties.get(SystemProperty.PACKAGE));
+                }
             }
 
             ManifestMerger2 manifestMerger =
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java
index 9438dda..8935e28 100755
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java
@@ -260,7 +260,7 @@
 
             if (node.getNodeType() == Node.TEXT_NODE) {
                 String text = node.getNodeValue();
-                if (text.length() > 0) {
+                if (!text.isEmpty()) {
                     for (int pos = 0; (pos = text.indexOf('\n', pos)) != -1; pos++) {
                         ++line;
                     }
@@ -427,7 +427,7 @@
                     break;
                 case Node.TEXT_NODE:
                         String txt = node.getNodeValue().trim();
-                         if (txt.length() == 0) {
+                         if (txt.isEmpty()) {
                              // Keep this for debugging. TODO make it a flag
                              // to dump whitespace on debugging. Otherwise ignore it.
                              // txt = "[whitespace]";
@@ -712,7 +712,7 @@
                 // If there are, just dump them as-is into the element representation.
                 // We do trim whitespace and ignore all-whitespace or empty text nodes.
                 String s = child.getNodeValue().trim();
-                if (s.length() > 0) {
+                if (!s.isEmpty()) {
                     sb.append(s);
                     hasText = true;
                 }
@@ -845,7 +845,7 @@
                         for (int kE = iE-1; kE >= 0; kE--) {
                             if (!aE[kE].startsWith(p)) {
                                 sE.insert(0, '\n').insert(0, diffReplaceNs(aE[kE], nsPrefixE)).insert(0, "  ");
-                                if (p.length() == 0) {
+                                if (p.isEmpty()) {
                                     break;
                                 }
                                 p = diffGetPrefix(aE[kE]);
@@ -872,7 +872,7 @@
                             if (!aA[kA].startsWith(p)) {
                                 sA.insert(0, '\n').insert(0, diffReplaceNs(aA[kA], nsPrefixA)).insert(0, "  ");
                                 p = diffGetPrefix(aA[kA]);
-                                if (p.length() == 0) {
+                                if (p.isEmpty()) {
                                     break;
                                 }
                             } else if (aA[kA].contains(keyAttr) || kA == 0) {
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java
index 0b0bb03..2f2d717 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java
@@ -16,10 +16,12 @@
 
 package com.android.manifmerger;
 
-import static com.android.manifmerger.XmlLoader.SourceLocation;
-
 import com.android.annotations.NonNull;
+import com.android.annotations.VisibleForTesting;
 import com.android.annotations.concurrency.Immutable;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.utils.ILogger;
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Optional;
@@ -72,6 +74,11 @@
             }
         }
         mActions.log(logger);
+
+        if (!mResult.isSuccess()) {
+            logger.warning("\nSee http://g.co/androidstudio/manifest-merger for more information"
+                    + " about the manifest merger.\n");
+        }
     }
 
     /**
@@ -149,28 +156,21 @@
     /**
      * Log record. This is used to give users some information about what is happening and
      * what might have gone wrong.
-     *
-     * TODO: need to enhance to add SourceLocation, and make this more machine readable.
      */
     public static class Record {
 
+
         public enum Severity {WARNING, ERROR, INFO }
 
         private final Severity mSeverity;
         private final String mLog;
-        private final SourceLocation mSourceLocation;
-        private final int mLineNumber;
-        private final int mColumnNumber;
+        private final SourceFilePosition mSourceLocation;
 
         private Record(
-                @NonNull SourceLocation sourceLocation,
-                int lineNumber,
-                int columnNumber,
+                @NonNull SourceFilePosition sourceLocation,
                 @NonNull Severity severity,
                 @NonNull String mLog) {
             this.mSourceLocation = sourceLocation;
-            this.mLineNumber = lineNumber;
-            this.mColumnNumber = columnNumber;
             this.mSeverity = severity;
             this.mLog = mLog;
         }
@@ -185,8 +185,8 @@
 
         @Override
         public String toString() {
-            return mSourceLocation.print(false)
-                    + ":" + mLineNumber + ":" + mColumnNumber + " "
+            return mSourceLocation.toString() // needs short string.
+                    + " "
                     + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, mSeverity.toString())
                     + ":\n\t"
                     + mLog;
@@ -221,12 +221,31 @@
             return this;
         }
 
-        Builder addMessage(@NonNull SourceLocation errorLocation,
+        @VisibleForTesting
+        Builder addMessage(@NonNull SourceFile sourceFile,
                 int line,
                 int column,
                 @NonNull Record.Severity severity,
                 @NonNull String message) {
+            // The line and column used are 1-based, but SourcePosition uses zero-based.
+            return addMessage(
+                    new SourceFilePosition(sourceFile, new SourcePosition(line - 1, column -1, -1)),
+                    severity,
+                    message);
+        }
 
+        Builder addMessage(@NonNull SourceFile sourceFile,
+                @NonNull Record.Severity severity,
+                @NonNull String message) {
+            return addMessage(
+                    new SourceFilePosition(sourceFile, SourcePosition.UNKNOWN),
+                    severity,
+                    message);
+        }
+
+        Builder addMessage(@NonNull SourceFilePosition sourceFilePosition,
+                    @NonNull Record.Severity severity,
+                    @NonNull String message) {
             switch (severity) {
                 case ERROR:
                     mHasErrors = true;
@@ -235,8 +254,7 @@
                     mHasWarnings = true;
                     break;
             }
-            mRecordBuilder.add(new Record(
-                    errorLocation, line, column, severity, message));
+            mRecordBuilder.add(new Record(sourceFilePosition,  severity, message));
             return this;
         }
 
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java
index e00008c..0f12930 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java
@@ -21,7 +21,8 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.utils.XmlUtils;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -107,14 +108,14 @@
 
     @NonNull
     @Override
-    public PositionXmlParser.Position getPosition() {
-        return PositionImpl.UNKNOWN;
+    public SourcePosition getPosition() {
+        return SourcePosition.UNKNOWN;
     }
 
     @Override
     @NonNull
-    public XmlLoader.SourceLocation getSourceLocation() {
-        return XmlLoader.UNKNOWN;
+    public SourceFile getSourceFile() {
+        return SourceFile.UNKNOWN;
     }
 }
 
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java
index 97f8b4b..8b0356c 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java
@@ -18,6 +18,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -116,7 +117,7 @@
                         // record the attribute set
                         mergingReportBuilder.getActionRecorder().recordAttributeAction(
                                 xmlAttribute,
-                                PositionImpl.UNKNOWN,
+                                SourcePosition.UNKNOWN,
                                 Actions.ActionType.INJECTED,
                                 null /* attributeOperationType */);
 
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PositionImpl.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PositionImpl.java
deleted file mode 100644
index 3f4ae57..0000000
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PositionImpl.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2014 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.manifmerger;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.utils.PositionXmlParser;
-
-/**
- * Implementation of {@link com.android.utils.PositionXmlParser.Position} capable of initializing
- * from xml definition or with given line, column and offset.
-*/
-final class PositionImpl implements PositionXmlParser.Position {
-
-    /**
-     * Unknown position on an action happens when the action did not originate from any source file
-     * but from environmental factors like placeholder injection, implicit permissions when
-     * upgrading, etc...
-     */
-    static final PositionXmlParser.Position UNKNOWN = new PositionImpl(0, 0, 0);
-
-    private final int mLine;
-    private final int mColumn;
-    private final int mOffset;
-
-    PositionImpl(int line, int column, int offset) {
-        mLine = line;
-        mColumn = column;
-        mOffset = offset;
-    }
-
-    @Nullable
-    @Override
-    public PositionXmlParser.Position getEnd() {
-        return null;
-    }
-
-    @Override
-    public void setEnd(@NonNull PositionXmlParser.Position end) {
-
-    }
-
-    @Override
-    public int getLine() {
-        return mLine;
-    }
-
-    @Override
-    public int getOffset() {
-        return mOffset;
-    }
-
-    @Override
-    public int getColumn() {
-        return mColumn;
-    }
-
-    @Override
-    public String toString() {
-        if (mLine == 0 && mColumn ==0) {
-            return "(unknown)";
-        } else {
-            return mLine + ":" + mColumn;
-        }
-    }
-}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java
index 4522168..59dfed0 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java
@@ -181,8 +181,8 @@
                                     "%1$s was tagged at %2$s:%3$d to replace another declaration "
                                             + "but no other declaration present",
                                     xmlElement.getId(),
-                                    xmlElement.getDocument().getSourceLocation().print(true),
-                                    xmlElement.getPosition().getLine()
+                                    xmlElement.getDocument().getSourceFile().print(true),
+                                    xmlElement.getPosition().getStartLine() + 1
                             ));
                 }
                 break;
@@ -195,8 +195,8 @@
                                     "%1$s was tagged at %2$s:%3$d to remove other declarations "
                                             + "but no other declaration present",
                                     xmlElement.getId(),
-                                    xmlElement.getDocument().getSourceLocation().print(true),
-                                    xmlElement.getPosition().getLine()
+                                    xmlElement.getDocument().getSourceFile().print(true),
+                                    xmlElement.getPosition().getStartLine() + 1
                             ));
                 }
                 break;
@@ -231,8 +231,8 @@
                                                 + " declarations but no other declaration present",
                                         xmlElement.getId(),
                                         attributeOperation.getKey(),
-                                        xmlElement.getDocument().getSourceLocation().print(true),
-                                        xmlElement.getPosition().getLine()
+                                        xmlElement.getDocument().getSourceFile().print(true),
+                                        xmlElement.getPosition().getStartLine() + 1
                                 ));
                     }
                     break;
@@ -245,8 +245,8 @@
                                                 + " declarations but no other declaration present",
                                         xmlElement.getId(),
                                         attributeOperation.getKey(),
-                                        xmlElement.getDocument().getSourceLocation().print(true),
-                                        xmlElement.getPosition().getLine()
+                                        xmlElement.getDocument().getSourceFile().print(true),
+                                        xmlElement.getPosition().getStartLine() + 1
                                 ));
                     }
                     break;
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java
index 7e41e84..6199fde 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java
@@ -253,23 +253,25 @@
                 case REMOVE:
                     // check we are not provided a new value.
                     if (attribute.isPresent()) {
+                        // Add one to startLine so the first line is displayed as 1.
                         xmlElement.addMessage(mergingReport, ERROR, String.format(
                                 "tools:remove specified at line:%d for attribute %s, but "
                                         + "attribute also declared at line:%d, "
                                         + "do you want to use tools:replace instead ?",
-                                xmlElement.getLine(),
+                                xmlElement.getPosition().getStartLine() + 1,
                                 attributeOperationTypeEntry.getKey(),
-                                attribute.get().getPosition().getLine()
+                                attribute.get().getPosition().getStartLine() + 1
                         ));
                     }
                     break;
                 case REPLACE:
                     // check we are provided a new value
                     if (!attribute.isPresent()) {
+                        // Add one to startLine so the first line is displayed as 1.
                         xmlElement.addMessage(mergingReport, ERROR, String.format(
                                 "tools:replace specified at line:%d for attribute %s, but "
                                         + "no new value specified",
-                                xmlElement.getLine(),
+                                xmlElement.getPosition().getStartLine() + 1,
                                 attributeOperationTypeEntry.getKey()
                         ));
                     }
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java
index 7dd00e1..dd457e7 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java
@@ -19,7 +19,9 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -105,11 +107,11 @@
 
     @NonNull
     @Override
-    public PositionXmlParser.Position getPosition() {
+    public SourcePosition getPosition() {
         try {
             return mOwnerElement.getDocument().getNodePosition(this);
         } catch(Exception e) {
-            return PositionImpl.UNKNOWN;
+            return SourcePosition.UNKNOWN;
         }
     }
 
@@ -335,26 +337,41 @@
         Actions.AttributeRecord attributeRecord = report.getActionRecorder()
                 .getAttributeCreationRecord(higherPriority);
 
-        String error = String.format(
-                "Attribute %1$s value=(%2$s) from %3$s\n"
-                        + "\tis also present at %4$s value=(%5$s)\n"
-                        + "\tSuggestion: add 'tools:replace=\"%6$s\"' to <%7$s> element "
-                        + "at %8$s to override",
-                higherPriority.getId(),
-                higherPriority.getValue(),
-                attributeRecord != null
-                        ? attributeRecord.getActionLocation().toString()
-                        : "(unknown)",
-                printPosition(),
-                getValue(),
-                mXml.getName(),
-                getOwnerElement().getType().toXmlName(),
-                higherPriority.getOwnerElement().printPosition(true)
-        );
+        String error;
+        if (getOwnerElement().getType().getMergeType() == MergeType.MERGE_CHILDREN_ONLY) {
+            error = String.format(
+                    "Attribute %1$s value=(%2$s) from %3$s\n"
+                            + "\tis also present at %4$s value=(%5$s).\n"
+                            + "\tAttributes of <%6$s> elements are not merged.",
+                    higherPriority.getId(),
+                    higherPriority.getValue(),
+                    attributeRecord != null
+                            ? attributeRecord.getActionLocation().print(true /*shortFormat*/)
+                            : "(unknown)",
+                    printPosition(),
+                    getValue(),
+                    getOwnerElement().getType().toXmlName());
+        } else {
+            error = String.format(
+                    "Attribute %1$s value=(%2$s) from %3$s\n"
+                            + "\tis also present at %4$s value=(%5$s).\n"
+                            + "\tSuggestion: add 'tools:replace=\"%6$s\"' to <%7$s> element "
+                            + "at %8$s to override.",
+                    higherPriority.getId(),
+                    higherPriority.getValue(),
+                    attributeRecord != null
+                            ? attributeRecord.getActionLocation().print(true /*shortFormat*/)
+                            : "(unknown)",
+                    printPosition(),
+                    getValue(),
+                    mXml.getName(),
+                    getOwnerElement().getType().toXmlName(),
+                    higherPriority.getOwnerElement().printPosition());
+        }
         higherPriority.addMessage(report,
                 attributeRecord != null
                         ? attributeRecord.getActionLocation().getPosition()
-                        : PositionImpl.UNKNOWN,
+                        : SourcePosition.UNKNOWN,
                 MergingReport.Record.Severity.ERROR, error);
     }
 
@@ -365,18 +382,17 @@
     }
 
     void addMessage(MergingReport.Builder report,
-            PositionXmlParser.Position position,
+            SourcePosition position,
             MergingReport.Record.Severity severity,
             String message) {
-        report.addMessage(getOwnerElement().getDocument().getSourceLocation(),
-                position.getLine(),
-                position.getColumn(),
+        report.addMessage(
+                new SourceFilePosition(getOwnerElement().getDocument().getSourceFile(), position),
                 severity, message);
     }
 
     @NonNull
     @Override
-    public XmlLoader.SourceLocation getSourceLocation() {
-        return getOwnerElement().getSourceLocation();
+    public SourceFile getSourceFile() {
+        return getOwnerElement().getSourceFile();
     }
 }
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
index bf46f20..1ad2c65 100755
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
@@ -24,6 +24,9 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.ide.common.xml.XmlFormatPreferences;
 import com.android.ide.common.xml.XmlFormatStyle;
 import com.android.ide.common.xml.XmlPrettyPrinter;
@@ -75,22 +78,20 @@
     private final Element mRootElement;
     // this is initialized lazily to avoid un-necessary early parsing.
     private final AtomicReference<XmlElement> mRootNode = new AtomicReference<XmlElement>(null);
-    private final PositionXmlParser mPositionXmlParser;
-    private final XmlLoader.SourceLocation mSourceLocation;
+    private final SourceFile mSourceFile;
     private final KeyResolver<String> mSelectors;
     private final KeyBasedValueResolver<SystemProperty> mSystemPropertyResolver;
     private final Type mType;
     private final Optional<String> mMainManifestPackageName;
 
-    public XmlDocument(@NonNull PositionXmlParser positionXmlParser,
-            @NonNull XmlLoader.SourceLocation sourceLocation,
+    public XmlDocument(
+            @NonNull SourceFile sourceLocation,
             @NonNull KeyResolver<String> selectors,
             @NonNull KeyBasedValueResolver<SystemProperty> systemPropertyResolver,
             @NonNull Element element,
             @NonNull Type type,
             @NonNull Optional<String> mainManifestPackageName) {
-        this.mPositionXmlParser = Preconditions.checkNotNull(positionXmlParser);
-        this.mSourceLocation = Preconditions.checkNotNull(sourceLocation);
+        this.mSourceFile = Preconditions.checkNotNull(sourceLocation);
         this.mRootElement = Preconditions.checkNotNull(element);
         this.mSelectors = Preconditions.checkNotNull(selectors);
         this.mSystemPropertyResolver = Preconditions.checkNotNull(systemPropertyResolver);
@@ -145,8 +146,8 @@
      * @return a new {@link com.android.manifmerger.XmlDocument} with up to date information.
      */
     public XmlDocument reparse() {
-        return new XmlDocument(mPositionXmlParser,
-                mSourceLocation,
+        return new XmlDocument(
+                mSourceFile,
                 mSelectors,
                 mSystemPropertyResolver,
                 mRootElement,
@@ -183,59 +184,24 @@
     }
 
     /**
-     * Returns a {@link XmlNode} position automatically offsetting the line and number
-     * columns by one (for PositionXmlParser, document starts at line 0, however for the common
-     * understanding, document should start at line 1).
+     * Returns the position of the specified {@link XmlNode}.
      */
     @NonNull
-    PositionXmlParser.Position getNodePosition(XmlNode node) {
+    static SourcePosition getNodePosition(XmlNode node) {
         return getNodePosition(node.getXml());
     }
 
     /**
-     * Returns a {@link org.w3c.dom.Node} position automatically offsetting the line and number
-     * columns by one (for PositionXmlParser, document starts at line 0, however for the common
-     * understanding, document should start at line 1).
+     * Returns the position of the specified {@link org.w3c.dom.Node}.
      */
     @NonNull
-    PositionXmlParser.Position getNodePosition(Node xml) {
-
-        final PositionXmlParser.Position position =  mPositionXmlParser.getPosition(xml);
-        if (position == null) {
-            return PositionImpl.UNKNOWN;
-        }
-        return new PositionXmlParser.Position() {
-            @Nullable
-            @Override
-            public PositionXmlParser.Position getEnd() {
-                return position.getEnd();
-            }
-
-            @Override
-            public void setEnd(@NonNull PositionXmlParser.Position end) {
-                position.setEnd(end);
-            }
-
-            @Override
-            public int getLine() {
-                return position.getLine() + 1;
-            }
-
-            @Override
-            public int getOffset() {
-                return position.getOffset();
-            }
-
-            @Override
-            public int getColumn() {
-                return position.getColumn() +1;
-            }
-        };
+    static SourcePosition getNodePosition(Node xml) {
+        return PositionXmlParser.getPosition(xml);
     }
 
     @NonNull
-    public XmlLoader.SourceLocation getSourceLocation() {
-        return mSourceLocation;
+    public SourceFile getSourceFile() {
+        return mSourceFile;
     }
 
     public synchronized XmlElement getRootNode() {
@@ -388,9 +354,10 @@
             XmlElement usesSdkElement = usesSdk.get();
             if (usesSdkElement.getOperationType() != NodeOperationType.MERGE) {
                 mergingReport
-                        .addMessage(getSourceLocation(),
-                                usesSdkElement.getLine(),
-                                usesSdkElement.getColumn(),
+                        .addMessage(
+                                new SourceFilePosition(
+                                        getSourceFile(),
+                                        usesSdkElement.getPosition()),
                                 MergingReport.Record.Severity.ERROR,
                                 "uses-sdk element cannot have a \"tools:node\" attribute");
                 return;
@@ -412,13 +379,13 @@
         if (!Character.isDigit(libraryTargetSdkVersion.charAt(0))) {
             // this is a code name, ensure this document uses the same code name.
             if (!libraryTargetSdkVersion.equals(getTargetSdkVersion())) {
-                mergingReport.addMessage(getSourceLocation(), 0, 0, MergingReport.Record.Severity.ERROR,
+                mergingReport.addMessage(getSourceFile(), MergingReport.Record.Severity.ERROR,
                         String.format(
                                 "uses-sdk:targetSdkVersion %1$s cannot be different than version "
                                         + "%2$s declared in library %3$s",
                                 getTargetSdkVersion(),
                                 libraryTargetSdkVersion,
-                                lowerPriorityDocument.getSourceLocation().print(false)
+                                lowerPriorityDocument.getSourceFile().print(false)
                         )
                 );
                 return;
@@ -430,13 +397,13 @@
         if (!Character.isDigit(libraryMinSdkVersion.charAt(0))) {
             // this is a code name, ensure this document uses the same code name.
             if (!libraryMinSdkVersion.equals(getMinSdkVersion())) {
-                mergingReport.addMessage(getSourceLocation(), 0, 0, MergingReport.Record.Severity.ERROR,
+                mergingReport.addMessage(getSourceFile(), MergingReport.Record.Severity.ERROR,
                         String.format(
                                 "uses-sdk:minSdkVersion %1$s cannot be different than version "
                                         + "%2$s declared in library %3$s",
                                 getMinSdkVersion(),
                                 libraryMinSdkVersion,
-                                lowerPriorityDocument.getSourceLocation().print(false)
+                                lowerPriorityDocument.getSourceFile().print(false)
                         )
                 );
                 return;
@@ -444,20 +411,23 @@
         }
 
         if (!checkUsesSdkMinVersion(lowerPriorityDocument, mergingReport)) {
-            mergingReport.addMessage(getSourceLocation(),
-                    usesSdk.isPresent() ? usesSdk.get().getLine() : 0,
-                    usesSdk.isPresent() ? usesSdk.get().getColumn() : 0,
-                    MergingReport.Record.Severity.ERROR,
-                    String.format(
+            String error = String.format(
                             "uses-sdk:minSdkVersion %1$s cannot be smaller than version "
                                     + "%2$s declared in library %3$s\n"
                                     + "\tSuggestion: use tools:overrideLibrary=\"%4$s\" to force usage",
                             getMinSdkVersion(),
                             lowerPriorityDocument.getRawMinSdkVersion(),
-                            lowerPriorityDocument.getSourceLocation().print(false),
-                            lowerPriorityDocument.getPackageName()
-                    )
-            );
+                            lowerPriorityDocument.getSourceFile().print(false),
+                            lowerPriorityDocument.getPackageName());
+            if (usesSdk.isPresent()) {
+                mergingReport.addMessage(
+                        new SourceFilePosition(getSourceFile(), usesSdk.get().getPosition()),
+                        MergingReport.Record.Severity.ERROR,
+                        error);
+            } else {
+                mergingReport.addMessage(
+                        getSourceFile(), MergingReport.Record.Severity.ERROR, error);
+            }
             return;
         }
 
@@ -479,9 +449,7 @@
             addIfAbsent(mergingReport.getActionRecorder(),
                     USES_PERMISSION,
                     permission("WRITE_EXTERNAL_STORAGE"),
-                    lowerPriorityDocument.getPackageName() + " has a targetSdkVersion < 4",
-                    Pair.of("maxSdkVersion", "18") // permission became implied at 19.
-            );
+                    lowerPriorityDocument.getPackageName() + " has a targetSdkVersion < 4");
             hasWriteToExternalStoragePermission = true;
 
             addIfAbsent(mergingReport.getActionRecorder(),
@@ -499,10 +467,7 @@
             addIfAbsent(mergingReport.getActionRecorder(),
                     USES_PERMISSION,
                     permission("READ_EXTERNAL_STORAGE"),
-                    lowerPriorityDocument.getPackageName() + " requested WRITE_EXTERNAL_STORAGE",
-                    // NOTE TO @xav, where can we find the list of implied permissions at versions X
-                    Pair.of("maxSdkVersion", "18") // permission became implied at 19, DID IT ???
-            );
+                    lowerPriorityDocument.getPackageName() + " requested WRITE_EXTERNAL_STORAGE");
         }
 
         // Pre-JellyBean call log permission compatibility.
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java
index 3e60364..f14fb94 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java
@@ -19,9 +19,10 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.ide.common.res2.MergingException;
 import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
 import com.android.utils.SdkUtils;
 import com.android.utils.XmlUtils;
 import com.google.common.base.Joiner;
@@ -116,15 +117,17 @@
                             OtherOperationType.valueOf(instruction);
                             break;
                         } catch (IllegalArgumentException e1) {
+
                             String errorMessage =
-                                    String.format("[%1$s:%2$s] Invalid instruction '%3$s', "
-                                                    + "valid instructions are : %4$s",
-                                            mDocument.getSourceLocation().print(false),
-                                            mDocument.getNodePosition(xml).getLine(),
+                                    String.format("Invalid instruction '%1$s', "
+                                                    + "valid instructions are : %2$s",
                                             instruction,
                                             Joiner.on(',').join(AttributeOperationType.values())
                                     );
-                            throw new RuntimeException(new MergingException(errorMessage, e));
+                            throw new RuntimeException(MergingException.wrapException(e)
+                                    .withMessage(errorMessage)
+                                    .withFile(mDocument.getSourceFile())
+                                    .withPosition(mDocument.getNodePosition(xml)).build());
                         }
                     }
                     for (String attributeName : Splitter.on(',').trimResults()
@@ -220,16 +223,17 @@
 
     @NonNull
     @Override
-    public PositionXmlParser.Position getPosition() {
+    public SourcePosition getPosition() {
         return mDocument.getNodePosition(this);
     }
 
     @NonNull
     @Override
-    public XmlLoader.SourceLocation getSourceLocation() {
-        return getDocument().getSourceLocation();
+    public SourceFile getSourceFile() {
+        return mDocument.getSourceFile();
     }
 
+
     /**
      * Merge this xml element with a lower priority node.
      *
@@ -245,9 +249,7 @@
 
 
         if (mSelector != null && !mSelector.isResolvable(getDocument().getSelectors())) {
-            mergingReport.addMessage(getSourceLocation(),
-                    getLine(),
-                    getColumn(),
+            mergingReport.addMessage(getSourceFilePosition(),
                     MergingReport.Record.Severity.ERROR,
                     String.format("'tools:selector=\"%1$s\"' is not a valid library identifier, "
                             + "valid identifiers are : %2$s",
@@ -871,9 +873,7 @@
     void addMessage(MergingReport.Builder mergingReport,
             MergingReport.Record.Severity severity,
             String message) {
-        mergingReport.addMessage(getDocument().getSourceLocation(),
-                getLine(),
-                getColumn(),
+        mergingReport.addMessage(getSourceFilePosition(),
                 severity,
                 message);
     }
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java
index 99194fb..66b08d4 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java
@@ -19,14 +19,11 @@
 import static com.android.manifmerger.ManifestMerger2.SystemProperty;
 import static com.android.manifmerger.PlaceholderHandler.KeyBasedValueResolver;
 
-import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
 import com.android.utils.PositionXmlParser;
 import com.google.common.base.Optional;
-import com.google.common.base.Strings;
 
 import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
 import java.io.BufferedInputStream;
@@ -42,45 +39,6 @@
  */
 public final class XmlLoader {
 
-    /**
-     * Abstraction for the notion of source location. This is useful for logging and records
-     * collection when a origin of an xml declaration is needed.
-     */
-    public static class SourceLocation {
-
-        @Nullable
-        private final File mSource;
-
-        @Nullable
-        private final String mDescription;
-
-        /**
-         * Build a source location, one of the parameter must not be null.
-         */
-        public SourceLocation(@Nullable String description, @Nullable File source) {
-            if (description == null && source == null) {
-                throw new IllegalArgumentException("description and source cannot be both null");
-            }
-            mDescription = description == null ? source.getName() : description;
-            mSource = source;
-        }
-
-        /**
-         * print this source location in a human and machine readable format.
-         *
-         * @param shortFormat whether or not to use the short format. For instance, for a file, a
-         *                    short format is the file name while the long format is its path.
-         * @return the human and machine readable source location.
-         */
-        String print(boolean shortFormat) {
-            return shortFormat
-                    ? mDescription
-                    : mSource == null
-                            ? mDescription :
-                            mSource.getAbsolutePath();
-        }
-    }
-
     private XmlLoader() {}
 
     /**
@@ -100,11 +58,9 @@
             throws IOException, SAXException, ParserConfigurationException {
         InputStream inputStream = new BufferedInputStream(new FileInputStream(xmlFile));
 
-        PositionXmlParser positionXmlParser = new PositionXmlParser();
-        Document domDocument = positionXmlParser.parse(inputStream);
-        return domDocument != null
-                ? new XmlDocument(positionXmlParser,
-                new SourceLocation(displayName, xmlFile),
+        Document domDocument = PositionXmlParser.parse(inputStream);
+        return domDocument != null ? new XmlDocument(
+                new SourceFile(xmlFile, displayName),
                 selectors,
                 systemPropertyResolver,
                 domDocument.getDocumentElement(),
@@ -117,7 +73,7 @@
     /**
      * Loads a xml document from its {@link String} representation without doing xml validation and
      * return a {@link com.android.manifmerger.XmlDocument}
-     * @param sourceLocation the source location to use for logging and record collection.
+     * @param sourceFile the source location to use for logging and record collection.
      * @param xml the persisted xml.
      * @return the initialized {@link com.android.manifmerger.XmlDocument}
      * @throws IOException this should never be thrown.
@@ -127,17 +83,15 @@
     public static XmlDocument load(
             KeyResolver<String> selectors,
             KeyBasedValueResolver<SystemProperty> systemPropertyResolver,
-            SourceLocation sourceLocation,
+            SourceFile sourceFile,
             String xml,
             XmlDocument.Type type,
             Optional<String> mainManifestPackageName)
             throws IOException, SAXException, ParserConfigurationException {
-        PositionXmlParser positionXmlParser = new PositionXmlParser();
-        Document domDocument = positionXmlParser.parse(xml);
+        Document domDocument = PositionXmlParser.parse(xml);
         return domDocument != null
                 ? new XmlDocument(
-                        positionXmlParser,
-                        sourceLocation,
+                        sourceFile,
                         selectors,
                         systemPropertyResolver,
                         domDocument.getDocumentElement(),
@@ -145,7 +99,4 @@
                         mainManifestPackageName)
                 : null;
     }
-
-    public static final SourceLocation UNKNOWN =
-            new SourceLocation("Unknown location", null /* source */);
 }
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java
index 8b37d31..f1e3feb 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java
@@ -19,7 +19,9 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.concurrency.Immutable;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
@@ -32,8 +34,6 @@
  */
 public abstract class XmlNode {
 
-    private static final String UNKNOWN_POSITION = "Unknown position";
-
     protected static final Function<Node, String> NODE_TO_NAME =
             new Function<Node, String>() {
                 @Override
@@ -65,13 +65,20 @@
      * Returns the element's position
      */
     @NonNull
-    public abstract PositionXmlParser.Position getPosition();
+    public abstract SourcePosition getPosition();
 
     /**
      * Returns the element's document xml source file location.
      */
     @NonNull
-    public abstract XmlLoader.SourceLocation getSourceLocation();
+    public abstract SourceFile getSourceFile();
+
+    /**
+     * Returns the element's document xml source file location.
+     */
+    public SourceFilePosition getSourceFilePosition() {
+        return new SourceFilePosition(getSourceFile(), getPosition());
+    }
 
     /**
      * Returns the element's xml
@@ -134,36 +141,13 @@
     }
 
     /**
-     * Return the line number in the original xml file this element or attribute was declared.
-     */
-    public int getLine() {
-        return getPosition().getLine();
-    }
-
-    /**
-     * Return the column number in the original xml file this element or attribute was declared.
-     */
-    public int getColumn() {
-        return getPosition().getColumn();
-    }
-
-    /**
      * Returns the position of this attribute in the original xml file. This may return an invalid
      * location as this xml fragment does not exist in any xml file but is the temporary result
      * of the merging process.
-     * @return a human readable position or {@link #UNKNOWN_POSITION}
+     * @return a human readable position.
      */
     public String printPosition() {
-        return printPosition(true);
-    }
-
-    public String printPosition(boolean shortFormat) {
-        PositionXmlParser.Position position = getPosition();
-        return new StringBuilder()
-                .append(getSourceLocation().print(shortFormat))
-                .append(":").append(position.getLine())
-                .append(":").append(position.getColumn())
-                .toString();
+        return getSourceFilePosition().print(true /*shortFormat*/);
     }
 
     /**
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java
index 5d0ba59..1949366 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java
@@ -72,7 +72,7 @@
             throws ParserConfigurationException, SAXException, IOException {
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         REFEFENCE_DOCUMENT), REFERENCE);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -90,7 +90,7 @@
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append(Actions.HEADER)
             .append(xmlElement.getId()).append("\n");
-        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
+        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
 
         Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
         Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -111,11 +111,11 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         REFEFENCE_DOCUMENT), REFERENCE);
 
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         "other_document"), other);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -141,8 +141,8 @@
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append(Actions.HEADER)
                 .append(xmlElement.getId()).append("\n");
-        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
-        appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", 6, 5);
+        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
+        appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", "6:5-83");
 
         Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
         Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -152,7 +152,7 @@
             throws ParserConfigurationException, SAXException, IOException {
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         REFEFENCE_DOCUMENT), REFERENCE);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -173,13 +173,12 @@
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append(Actions.HEADER)
                 .append(xmlElement.getId()).append("\n");
-        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
+        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
         appendAttribute(stringBuilder,
                 XmlNode.unwrapName(xmlElement.getXml().getAttributeNode("android:name")),
                 Actions.ActionType.ADDED,
                 REFEFENCE_DOCUMENT,
-                6,
-                15);
+                "6:15-41");
 
         Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
         Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -189,7 +188,7 @@
             throws ParserConfigurationException, SAXException, IOException {
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         REFEFENCE_DOCUMENT), REFERENCE);
 
         XmlElement xmlElement = xmlDocument.getRootNode();
@@ -209,13 +208,12 @@
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append(Actions.HEADER)
                 .append(xmlElement.getId()).append("\n");
-        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 1, 1);
+        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "1:1-10:12");
         appendAttribute(stringBuilder,
                 XmlNode.unwrapName(xmlElement.getXml().getAttributeNode("package")),
                 Actions.ActionType.ADDED,
                 REFEFENCE_DOCUMENT,
-                4,
-                5);
+                "4:5-31");
 
         Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
         Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -237,11 +235,11 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         REFEFENCE_DOCUMENT), REFERENCE);
 
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         "other_document"), other);
 
         XmlElement activityElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -272,10 +270,10 @@
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append(Actions.HEADER)
                 .append(activityElement.getId()).append("\n");
-        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
-        appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", 6, 5);
+        appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
+        appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", "6:5-82");
         stringBuilder.append(applicationElement.getId()).append("\n");
-        appendNode(stringBuilder, Actions.ActionType.ADDED, "other_document", 7, 5);
+        appendNode(stringBuilder, Actions.ActionType.ADDED, "other_document", "7:5-49");
 
         Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
         Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -284,29 +282,27 @@
     private void appendNode(StringBuilder out,
             Actions.ActionType actionType,
             String docString,
-            int lineNumber,
-            int columnNumber) {
+            String position) {
 
         out.append(actionType.toString())
                 .append(" from ")
                 .append(getClass().getSimpleName()).append('#').append(docString)
-                .append(":").append(lineNumber).append(":").append(columnNumber).append("\n");
+                .append(':').append(position).append('\n');
     }
 
     private void appendAttribute(StringBuilder out,
             XmlNode.NodeName attributeName,
             Actions.ActionType actionType,
             String docString,
-            int lineNumber,
-            int columnNumber) {
+            String position) {
 
-        out.append("\t")
+        out.append('\t')
                 .append(attributeName.toString())
                 .append("\n\t\t")
                 .append(actionType.toString())
                 .append(" from ")
                 .append(getClass().getSimpleName()).append('#').append(docString)
-                .append(":").append(lineNumber)
-                .append(":").append(columnNumber).append("\n");
+                .append(':').append(position)
+                .append('\n');
     }
 }
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java
index b748f7f..c015573 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java
@@ -16,16 +16,17 @@
 
 package com.android.manifmerger;
 
-import static com.android.manifmerger.Actions.ActionLocation;
 import static com.android.manifmerger.Actions.DecisionTreeRecord;
 import static com.android.manifmerger.XmlNode.NodeKey;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import com.android.SdkConstants;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.sdklib.mock.MockLog;
 import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
 import com.android.utils.StdLogger;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
@@ -73,8 +74,7 @@
         DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord();
         activityDecisionTree.addNodeRecord(new Actions.NodeRecord(
                 Actions.ActionType.ADDED,
-                new ActionLocation(mock(XmlLoader.SourceLocation.class), mock(
-                        PositionXmlParser.Position.class)),
+                new SourceFilePosition(new SourceFile("file"), new SourcePosition(1, 2, -1)),
                 new NodeKey("nodeKey"),
                 null, /* reason */
                 NodeOperationType.MERGE));
@@ -98,8 +98,7 @@
         DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord();
         activityDecisionTree.addNodeRecord(new Actions.NodeRecord(
                 Actions.ActionType.ADDED,
-                new ActionLocation(mock(XmlLoader.SourceLocation.class), mock(
-                        PositionXmlParser.Position.class)),
+                new SourceFilePosition(new SourceFile("file"), new SourcePosition(1, 2, -1)),
                 new NodeKey("nodeKey"),
                 null, /* reason */
                 NodeOperationType.MERGE));
@@ -107,8 +106,8 @@
         activityDecisionTree.mAttributeRecords.put(attributeName,
                 ImmutableList.of(
                     new Actions.AttributeRecord(Actions.ActionType.INJECTED,
-                            new ActionLocation(mock(XmlLoader.SourceLocation.class),
-                                    mock(PositionXmlParser.Position.class)),
+                            new SourceFilePosition(
+                                    new SourceFile("file"), new SourcePosition(1, 2, -1)),
                             new NodeKey("nodeKey"),
                             null, /* reason */
                             AttributeOperationType.STRICT)));
@@ -183,11 +182,11 @@
                 + "</manifest>";                                                            // 13
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+                TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
         XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+                TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -207,25 +206,25 @@
                 + "3    package=\"com.example.lib3\" >\n"
                 + "4\n"
                 + "5    <permission\n"
-                + "5-->ActionsTest#higherPriority:14:5\n"
+                + "5-->ActionsTest#higherPriority:14:5-18:18\n"
                 + "6        android:name=\"permissionThree\"\n"
-                + "6-->ActionsTest#higherPriority:15:14\n"
+                + "6-->ActionsTest#higherPriority:15:14-44\n"
                 + "7        android:protectionLevel=\"signature\" >\n"
-                + "7-->ActionsTest#higherPriority:16:14\n"
+                + "7-->ActionsTest#higherPriority:16:14-49\n"
                 + "8    </permission>\n"
                 + "9    <permission\n"
-                + "9-->ActionsTest#lowerPriorityOne:9:5\n"
+                + "9-->ActionsTest#lowerPriorityOne:9:5-11:18\n"
                 + "10        android:name=\"permissionTwo\"\n"
-                + "10-->ActionsTest#lowerPriorityOne:9:17\n"
+                + "10-->ActionsTest#lowerPriorityOne:9:17-45\n"
                 + "11        android:protectionLevel=\"signature\" >\n"
-                + "11-->ActionsTest#lowerPriorityOne:10:14\n"
+                + "11-->ActionsTest#lowerPriorityOne:10:14-49\n"
                 + "12    </permission>\n"
                 + "13    <permission\n"
-                + "13-->ActionsTest#lowerPriorityTwo:9:5\n"
+                + "13-->ActionsTest#lowerPriorityTwo:9:5-11:18\n"
                 + "14        android:name=\"permissionFour\"\n"
-                + "14-->ActionsTest#lowerPriorityTwo:9:17\n"
+                + "14-->ActionsTest#lowerPriorityTwo:9:17-46\n"
                 + "15        android:protectionLevel=\"normal\" >\n"
-                + "15-->ActionsTest#lowerPriorityTwo:10:14\n"
+                + "15-->ActionsTest#lowerPriorityTwo:10:14-46\n"
                 + "16    </permission>\n"
                 + "17\n"
                 + "18</manifest>\n";
@@ -274,12 +273,7 @@
             Actions.NodeRecord nodeRecord,
             List<Actions.NodeRecord> nodeRecordList) {
         for (Actions.NodeRecord record : nodeRecordList) {
-            if (record.getActionLocation().getPosition().getLine()
-                    == nodeRecord.getActionLocation().getPosition().getLine()
-                    && record.getActionLocation().getPosition().getColumn()
-                    == nodeRecord.getActionLocation().getPosition().getColumn()
-                    && record.getActionLocation().getPosition().getOffset()
-                    == nodeRecord.getActionLocation().getPosition().getOffset()
+            if (record.getActionLocation().equals(nodeRecord.getActionLocation())
                     && record.getActionType() == nodeRecord.getActionType()) {
                 return true;
             }
@@ -294,12 +288,7 @@
         for (Actions.AttributeRecord record : attributeRecordList) {
             if (record.getOperationType() == attributeRecord.getOperationType()
                     && record.getActionType() == attributeRecord.getActionType()
-                    && record.getActionLocation().getPosition().getLine()
-                    == attributeRecord.getActionLocation().getPosition().getLine()
-                    && record.getActionLocation().getPosition().getColumn()
-                    == attributeRecord.getActionLocation().getPosition().getColumn()
-                    && record.getActionLocation().getPosition().getOffset()
-                    == attributeRecord.getActionLocation().getPosition().getOffset()) {
+                    && record.getActionLocation().equals(attributeRecord.getActionLocation())) {
                 return true;
             }
         }
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java
index 83bc78d..9512204 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java
@@ -70,8 +70,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNoUseFeaturesDeclaration"), input);
+                TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -97,8 +96,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNothingToTrim"), input);
+                TestUtils.sourceFile(getClass(), "testNothingToTrim"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -132,8 +130,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMultipleAboveTwoResults"), input);
+                TestUtils.sourceFile(getClass(), "testMultipleAboveTwoResults"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -163,8 +160,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testSingleAboveTwoResults"), input);
+                TestUtils.sourceFile(getClass(), "testSingleAboveTwoResults"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -199,8 +195,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMultipleBelowTwoResults"), input);
+                TestUtils.sourceFile(getClass(), "testMultipleBelowTwoResults"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -230,8 +225,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testSingleBelowTwoResults"), input);
+                TestUtils.sourceFile(getClass(), "testSingleBelowTwoResults"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -277,8 +271,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMultipleAboveAndBelowTwoResults"), input);
+                TestUtils.sourceFile(getClass(), "testMultipleAboveAndBelowTwoResults"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
         ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -312,8 +305,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testUsesFeatureSplit"), input);
+                TestUtils.sourceFile(getClass(), "testUsesFeatureSplit"), input);
 
         ActionRecorder mockActionRecorder = Mockito.mock(ActionRecorder.class);
         MergingReport.Builder mockReport = Mockito.mock(MergingReport.Builder.class);
@@ -354,8 +346,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testUsesFeatureSplit"), input);
+                TestUtils.sourceFile(getClass(), "testUsesFeatureSplit"), input);
 
         ActionRecorder mockActionRecorder = Mockito.mock(ActionRecorder.class);
         MergingReport.Builder mockReport = Mockito.mock(MergingReport.Builder.class);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java
index 0c14292..8e0432d 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java
@@ -173,7 +173,7 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testPackageOverride#xml"), xml);
+                TestUtils.sourceFile(getClass(), "testPackageOverride#xml"), xml);
 
         ManifestMerger2.SystemProperty.PACKAGE.addTo(mActionRecorder, refDocument, "com.bar.new");
         // verify the package value was overriden.
@@ -189,7 +189,7 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testMissingPackageOverride#xml"), xml);
+                TestUtils.sourceFile(getClass(), "testMissingPackageOverride#xml"), xml);
 
         ManifestMerger2.SystemProperty.PACKAGE.addTo(mActionRecorder, refDocument, "com.bar.new");
         // verify the package value was added.
@@ -205,7 +205,7 @@
                 + "</manifest>";
 
         XmlDocument document = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         "testAddingSystemProperties#xml"), xml);
 
         ManifestMerger2.SystemProperty.VERSION_CODE.addTo(mActionRecorder, document, "101");
@@ -241,7 +241,7 @@
                 + "</manifest>";
 
         XmlDocument document = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         "testAddingSystemProperties#xml"), xml
         );
 
@@ -261,7 +261,7 @@
                 + "</manifest>";
 
         XmlDocument document = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),
+                TestUtils.sourceFile(getClass(),
                         "testAddingSystemProperties#xml"), xml);
         // check initial state.
         assertEquals("34", document.getXml().getDocumentElement().getAttribute("versionCode"));
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java
index 9233915..13152a7 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java
@@ -194,7 +194,7 @@
             }
 
             XmlDocument expectedResult = TestUtils.xmlDocumentFromString(
-                    new TestUtils.TestSourceLocation(getClass(), testFiles.getMain().getName()),
+                    TestUtils.sourceFile(getClass(), testFiles.getMain().getName()),
                     testFiles.getExpectedResult());
             Optional<String> comparingMessage =
                     expectedResult.compareTo(actualResult);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java
index 19e84d2..eae307e 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java
@@ -52,8 +52,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNoUseFeaturesDeclaration"), input);
+                TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
         assertEquals("uses-feature",xmlElement.getXml().getNodeName());
@@ -73,8 +72,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNoUseFeaturesDeclaration"), input);
+                TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
         assertEquals("uses-feature",xmlElement.getXml().getNodeName());
@@ -174,8 +172,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNoUseFeaturesDeclaration"), input);
+                TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
         assertEquals("uses-feature",xmlElement.getXml().getNodeName());
@@ -201,8 +198,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNoUseFeaturesDeclaration"), input);
+                TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
 
         XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
         ImmutableList<XmlElement> screenDefinitions = xmlElement.getMergeableElements();
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java
index 9758fda..2545f90 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java
@@ -18,11 +18,9 @@
 
 import static com.android.manifmerger.MergingReport.Record.Severity;
 import static com.android.manifmerger.PlaceholderHandler.KeyBasedValueResolver;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
 
+import com.android.ide.common.blame.SourceFile;
 import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 
@@ -40,7 +38,7 @@
 
     @Mock ILogger mLoggerMock;
     @Mock Element mElement;
-    @Mock XmlLoader.SourceLocation mSourceLocation;
+    SourceFile mSourceLocation = new SourceFile("location");
     @Mock KeyResolver<String> mKeyResolver;
     @Mock KeyBasedValueResolver<ManifestMerger2.SystemProperty> mPropertyResolver;
 
@@ -112,19 +110,20 @@
     }
 
     public void testLogging() {
-        when(mSourceLocation.print(any(boolean.class))).thenReturn("location");
         MergingReport mergingReport = new MergingReport.Builder(mLoggerMock)
-                .addMessage(mSourceLocation,0, 0, Severity.INFO, "merging info")
-                .addMessage(mSourceLocation,0, 0, Severity.WARNING, "something weird happened")
-                .addMessage(mSourceLocation,0, 0, Severity.ERROR, "something bad happened")
+                .addMessage(mSourceLocation,1, 1, Severity.INFO, "merging info")
+                .addMessage(mSourceLocation,1, 1, Severity.WARNING, "something weird happened")
+                .addMessage(mSourceLocation,1, 1, Severity.ERROR, "something bad happened")
                 .build();
 
         mergingReport.log(mLoggerMock);
-        Mockito.verify(mLoggerMock).verbose("location:0:0 Info:\n\tmerging info");
-        Mockito.verify(mLoggerMock).warning("location:0:0 Warning:\n\tsomething weird happened");
+        Mockito.verify(mLoggerMock).verbose("location:1:1 Info:\n\tmerging info");
+        Mockito.verify(mLoggerMock).warning("location:1:1 Warning:\n\tsomething weird happened");
         Mockito.verify(mLoggerMock).error(null /* throwable */,
-                "location:0:0 Error:\n\tsomething bad happened");
+                "location:1:1 Error:\n\tsomething bad happened");
         Mockito.verify(mLoggerMock).verbose(Actions.HEADER);
+        Mockito.verify(mLoggerMock).warning("\nSee http://g.co/androidstudio/manifest-merger "
+                + "for more information about the manifest merger.\n");
         Mockito.verifyNoMoreInteractions(mLoggerMock);
     }
 
@@ -144,7 +143,7 @@
 
     public void testGetMergedDocument() {
         XmlDocument xmlDocument =
-                new XmlDocument(new PositionXmlParser(),
+                new XmlDocument(
                         mSourceLocation,
                         mKeyResolver,
                         mPropertyResolver,
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java
index 11a0e29..ab3869d 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java
@@ -18,7 +18,6 @@
 
 import static com.android.manifmerger.PlaceholderHandler.KeyBasedValueResolver;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.times;
@@ -26,6 +25,8 @@
 import static org.mockito.Mockito.when;
 
 import com.android.annotations.NonNull;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.sdklib.mock.MockLog;
 import com.google.common.base.Optional;
 
@@ -82,7 +83,7 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+                TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
 
         PlaceholderHandler handler = new PlaceholderHandler();
         handler.visit(
@@ -117,7 +118,7 @@
             if (!xmlAttribute.getName().toString().contains("name")) {
                 verify(mActionRecorder).recordAttributeAction(
                         xmlAttribute,
-                        PositionImpl.UNKNOWN,
+                        SourcePosition.UNKNOWN,
                         Actions.ActionType.INJECTED,
                         null);
             }
@@ -137,7 +138,7 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+                TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
 
         PlaceholderHandler handler = new PlaceholderHandler();
         handler.visit(
@@ -180,7 +181,7 @@
             if (!xmlAttribute.getName().toString().contains("name")) {
                 verify(mActionRecorder, times(2)).recordAttributeAction(
                         xmlAttribute,
-                        PositionImpl.UNKNOWN,
+                        SourcePosition.UNKNOWN,
                         Actions.ActionType.INJECTED,
                         null);
 
@@ -198,13 +199,13 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+                TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
 
         PlaceholderHandler handler = new PlaceholderHandler();
         handler.visit(ManifestMerger2.MergeType.APPLICATION, refDocument, nullResolver, mBuilder);
         // verify the error was recorded.
         verify(mBuilder).addMessage(
-                any(XmlLoader.SourceLocation.class), anyInt(), anyInt(),
+                any(SourceFilePosition.class),
                 eq(MergingReport.Record.Severity.ERROR), anyString());
     }
 
@@ -218,13 +219,13 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+                TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
 
         PlaceholderHandler handler = new PlaceholderHandler();
         handler.visit(ManifestMerger2.MergeType.LIBRARY, refDocument, nullResolver, mBuilder);
         // verify the error was recorded.
         verify(mBuilder).addMessage(
-                any(XmlLoader.SourceLocation.class), anyInt(), anyInt(),
+                any(SourceFilePosition.class),
                 eq(MergingReport.Record.Severity.INFO), anyString());
     }
 }
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java
index b53c93c..cb704dd 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java
@@ -18,6 +18,7 @@
 
 import com.android.SdkConstants;
 import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
 
 import junit.framework.TestCase;
 
@@ -68,17 +69,16 @@
                 + "\n"
                 + "    <application android:label=\"@string/lib_name\" />\n"
                 + "\n"
+                + "\n"
                 + "        <activity android:name=\"activityOne\"/>"
                 + "\n"
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemoveMain"), main);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemoveMain"), main);
 
         XmlDocument libraryDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemoveLib"), library);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemoveLib"), library);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -90,7 +90,8 @@
                 return;
             }
         }
-        fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found");
+        fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found in: \n" +
+                Joiner.on("\n    ").join(mergingReportBuilder.build().getLoggingRecords()));
     }
 
     public void testIncorrectReplace()
@@ -123,12 +124,10 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectReplaceMain"), main);
+                TestUtils.sourceFile(getClass(), "testIncorrectReplaceMain"), main);
 
         XmlDocument libraryDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectReplaceLib"), library);
+                TestUtils.sourceFile(getClass(), "testIncorrectReplaceLib"), library);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -140,7 +139,8 @@
                 return;
             }
         }
-        fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found");
+        fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found in: \n" +
+                Joiner.on("\n    ").join(mergingReportBuilder.build().getLoggingRecords()));
     }
 
     public void testApplicationInvalidOrder()
@@ -161,7 +161,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -193,7 +193,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -227,7 +227,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testApplicationValidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testApplicationValidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -258,7 +258,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testUsesSdkInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testUsesSdkInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -290,7 +290,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testUsesSdkInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testUsesSdkInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -324,7 +324,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testUsesSdkValidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testUsesSdkValidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -353,7 +353,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -378,7 +378,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -399,7 +399,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+                TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
         PostValidator.validate(xmlDocument, mergingReportBuilder);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java
index 691bf84..010cfa3 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java
@@ -51,8 +51,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemove"), input);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -78,8 +77,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemove"), input);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -107,8 +105,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemove"), input);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -136,8 +133,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemove"), input);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -166,8 +162,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testIncorrectRemove"), input);
+                TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -200,8 +195,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testScreenMerging"), input);
+                TestUtils.sourceFile(getClass(), "testScreenMerging"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -239,8 +233,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMultipleIntentFilterWithSameKeyValue"), input);
+                TestUtils.sourceFile(getClass(), "testMultipleIntentFilterWithSameKeyValue"), input);
 
         MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
         MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java
index e9c6d2e..d509568 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java
@@ -21,11 +21,9 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
 import com.google.common.base.Optional;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
 import java.io.IOException;
@@ -61,15 +59,12 @@
                 }
             };
 
-    static class TestSourceLocation extends XmlLoader.SourceLocation {
-
-        TestSourceLocation(Class sourceClass, String location) {
-            super(sourceClass.getSimpleName() + "#" + location, null);
-        }
+    static SourceFile sourceFile(Class sourceClass, String location) {
+        return new SourceFile(sourceClass.getSimpleName() + "#" + location);
     }
 
     static XmlDocument xmlDocumentFromString(
-            XmlLoader.SourceLocation location,
+            SourceFile location,
             String input)  throws IOException, SAXException, ParserConfigurationException {
 
         return XmlLoader.load(
@@ -78,7 +73,7 @@
     }
 
     static XmlDocument xmlLibraryFromString(
-            XmlLoader.SourceLocation location,
+            SourceFile location,
             String input)  throws IOException, SAXException, ParserConfigurationException {
 
         return XmlLoader.load(
@@ -87,7 +82,7 @@
     }
 
     static XmlDocument xmlDocumentFromString(
-            XmlLoader.SourceLocation location,
+            SourceFile location,
             String input,
             XmlDocument.Type type,
             Optional<String> mainManifestPackageName)  throws IOException, SAXException, ParserConfigurationException {
@@ -97,7 +92,7 @@
 
     static XmlDocument xmlDocumentFromString(
             @NonNull KeyResolver<String> selectors,
-            @NonNull XmlLoader.SourceLocation location,
+            @NonNull SourceFile location,
             String input)  throws IOException, SAXException, ParserConfigurationException {
 
         return XmlLoader.load(selectors, NO_PROPERTY_RESOLVER, location, input,
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java
index 592bd0c..fd35b92 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java
@@ -53,8 +53,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNodeRemoveOperation"), main);
+                TestUtils.sourceFile(getClass(), "testNodeRemoveOperation"), main);
 
         Element rootElement = mainDocument.getRootNode().getXml();
         ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -87,8 +86,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNodeRemoveWithChildrenOperation"), main);
+                TestUtils.sourceFile(getClass(), "testNodeRemoveWithChildrenOperation"), main);
 
         Element rootElement = mainDocument.getRootNode().getXml();
         ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -117,8 +115,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNodeRemoveOperation"), main);
+                TestUtils.sourceFile(getClass(), "testNodeRemoveOperation"), main);
 
         assertNull(ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog));
     }
@@ -136,8 +133,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNodeRemoveOperation"), main);
+                TestUtils.sourceFile(getClass(), "testNodeRemoveOperation"), main);
 
         assertNull(ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog));
     }
@@ -158,8 +154,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNodeReplaceOperation"), main);
+                TestUtils.sourceFile(getClass(), "testNodeReplaceOperation"), main);
 
         Element rootElement = mainDocument.getRootNode().getXml();
         ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -187,8 +182,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testAttributeRemoveOperation"), main);
+                TestUtils.sourceFile(getClass(), "testAttributeRemoveOperation"), main);
 
         Element rootElement = mainDocument.getRootNode().getXml();
         ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -220,8 +214,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testSelectorRemoval"), main);
+                TestUtils.sourceFile(getClass(), "testSelectorRemoval"), main);
 
         Element rootElement = mainDocument.getRootNode().getXml();
         ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -251,8 +244,7 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testNodeReplaceOperation"), main);
+                TestUtils.sourceFile(getClass(), "testNodeReplaceOperation"), main);
 
         Element rootElement = mainDocument.getRootNode().getXml();
         ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java
index baa38d1..4e9c69c 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java
@@ -116,9 +116,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -149,7 +149,7 @@
         Actions.AttributeRecord attributeRecord = attributeRecords.get(0);
         assertEquals(Actions.ActionType.REJECTED, attributeRecord.getActionType());
         assertEquals(AttributeOperationType.REMOVE, attributeRecord.getOperationType());
-        assertEquals(7, attributeRecord.getActionLocation().getPosition().getLine());
+        assertEquals(6, attributeRecord.getActionLocation().getPosition().getStartLine());
     }
 
     public void testNamespaceAwareAttributeRemoval()
@@ -177,9 +177,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -226,9 +226,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -287,11 +287,11 @@
                 + "</manifest>";
 
         XmlDocument highPriority = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "highPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "highPriority"), higherPriority);
         XmlDocument lowPriority = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowPriority"), lowerPriority);
         XmlDocument lowestPriority = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowestPriority"), evenLowerPriority);
+                TestUtils.sourceFile(getClass(), "lowestPriority"), evenLowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -329,15 +329,15 @@
         assertEquals(Actions.ActionType.REJECTED, attributeRecord.getActionType());
         assertEquals(AttributeOperationType.REMOVE, attributeRecord.getOperationType());
         assertEquals("XmlAttributeTest#lowPriority",
-                attributeRecord.getActionLocation().getSourceLocation().print(true));
-        assertEquals(8, attributeRecord.getActionLocation().getPosition().getLine());
+                attributeRecord.getActionLocation().getFile().print(true));
+        assertEquals(7, attributeRecord.getActionLocation().getPosition().getStartLine());
 
         attributeRecord = attributeRecords.get(1);
         assertEquals(Actions.ActionType.REJECTED, attributeRecord.getActionType());
         assertEquals(AttributeOperationType.REMOVE, attributeRecord.getOperationType());
         assertEquals("XmlAttributeTest#lowestPriority",
-                attributeRecord.getActionLocation().getSourceLocation().print(true));
-        assertEquals(7, attributeRecord.getActionLocation().getPosition().getLine());
+                attributeRecord.getActionLocation().getFile().print(true));
+        assertEquals(6, attributeRecord.getActionLocation().getPosition().getStartLine());
     }
 
     public void testDefaultValueIllegalOverriding()
@@ -367,9 +367,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -404,9 +404,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -456,9 +456,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java
index f7ab6c8..8a0d4b8 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java
@@ -74,8 +74,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMergeableElementsIdentification()"), input);
+                TestUtils.sourceFile(getClass(), "testMergeableElementsIdentification()"), input);
         ImmutableList<XmlElement> mergeableElements = xmlDocument.getRootNode().getMergeableElements();
         assertEquals(2, mergeableElements.size());
     }
@@ -94,8 +93,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMergeableElementsIdentification()"), input);
+                TestUtils.sourceFile(getClass(), "testMergeableElementsIdentification()"), input);
         ImmutableList<XmlElement> mergeableElements = xmlDocument.getRootNode().getMergeableElements();
         assertEquals(2, mergeableElements.size());
         assertEquals(ManifestModel.NodeTypes.APPLICATION, mergeableElements.get(0).getType());
@@ -119,8 +117,7 @@
                 + "</android:manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(
-                        getClass(), "testMergeableElementsIdentification()"), input);
+                TestUtils.sourceFile(getClass(), "testMergeableElementsIdentification()"), input);
         ImmutableList<XmlElement> mergeableElements = xmlDocument.getRootNode().getMergeableElements();
         assertEquals(3, mergeableElements.size());
         assertEquals(ManifestModel.NodeTypes.APPLICATION, mergeableElements.get(0).getType());
@@ -143,7 +140,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testGetXmlNodeByTypeAndKey()"), input);
+                TestUtils.sourceFile(getClass(), "testGetXmlNodeByTypeAndKey()"), input);
         assertTrue(xmlDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").isPresent());
         assertFalse(xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -170,9 +167,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testSimpleMerge()"), main);
+                TestUtils.sourceFile(getClass(), "testSimpleMerge()"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testSimpleMerge()"), library);
+                TestUtils.sourceFile(getClass(), "testSimpleMerge()"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -207,9 +204,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), main);
+                TestUtils.sourceFile(getClass(), "testDiff1()"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), library);
+                TestUtils.sourceFile(getClass(), "testDiff1()"), library);
         assertTrue(mainDocument.compareTo(libraryDocument).isPresent());
     }
 
@@ -236,9 +233,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), main);
+                TestUtils.sourceFile(getClass(), "testDiff2()"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), library);
+                TestUtils.sourceFile(getClass(), "testDiff2()"), library);
         assertFalse(mainDocument.compareTo(libraryDocument).isPresent());
     }
 
@@ -267,9 +264,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), main);
+                TestUtils.sourceFile(getClass(), "testDiff3()"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), library);
+                TestUtils.sourceFile(getClass(), "testDiff3()"), library);
         assertFalse(mainDocument.compareTo(libraryDocument).isPresent());
     }
 
@@ -286,7 +283,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testWriting()"), input);
+                TestUtils.sourceFile(getClass(), "testWriting()"), input);
         assertEquals(input, xmlDocument.prettyPrint());
     }
 
@@ -321,9 +318,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -403,9 +400,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -461,9 +458,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -499,9 +496,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -541,9 +538,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -588,9 +585,9 @@
                 + "</manifest>";
 
         XmlDocument flavorDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "flavor"), flavor);
+                TestUtils.sourceFile(getClass(), "flavor"), flavor);
         XmlDocument mainDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 flavorDocument.merge(mainDocument, mergingReportBuilder);
@@ -631,9 +628,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -663,9 +660,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -707,9 +704,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -761,9 +758,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -808,9 +805,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -871,9 +868,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -917,9 +914,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -963,9 +960,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1010,12 +1007,12 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "overlay"),
+                TestUtils.sourceFile(getClass(), "overlay"),
                 overlay,
                 XmlDocument.Type.OVERLAY,
                 Optional.of("com.example.lib3"));
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1054,9 +1051,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1098,9 +1095,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1141,9 +1138,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1183,9 +1180,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1227,9 +1224,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1273,9 +1270,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1310,9 +1307,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1384,9 +1381,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1448,9 +1445,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1518,9 +1515,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1590,9 +1587,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1660,9 +1657,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1722,9 +1719,9 @@
                 + "</manifest>";
 
         XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "main"), main);
+                TestUtils.sourceFile(getClass(), "main"), main);
         XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "library"), library);
+                TestUtils.sourceFile(getClass(), "library"), library);
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
         Optional<XmlDocument> mergedDocument =
                 mainDocument.merge(libraryDocument, mergingReportBuilder);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java
index b17f79a..d2fe445 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java
@@ -72,7 +72,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testToolsNodeInstructions()"), input);
+                TestUtils.sourceFile(getClass(), "testToolsNodeInstructions()"), input);
         Optional<XmlElement> activity = xmlDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
         assertTrue(activity.isPresent());
@@ -106,8 +106,7 @@
 
         try {
             XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                    new TestUtils.TestSourceLocation(
-                            getClass(), "testInvalidNodeInstruction()"), input);
+                    TestUtils.sourceFile(getClass(), "testInvalidNodeInstruction()"), input);
             xmlDocument.getRootNode();
             fail("Exception not thrown");
         } catch (IllegalArgumentException expected) {
@@ -152,7 +151,7 @@
 
         // ActivityOne, remove operation.
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testAttributeInstructions()"), input);
+                TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
         Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
         assertTrue(activityOptional.isPresent());
@@ -229,7 +228,7 @@
 
         // ActivityOne, remove operation.
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testAttributeInstructions()"), input);
+                TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
         Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
         assertTrue(activityOptional.isPresent());
@@ -254,7 +253,7 @@
 
         // ActivityOne, remove operation.
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testAttributeInstructions()"), input);
+                TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
         Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
         assertTrue(activityOptional.isPresent());
@@ -282,7 +281,7 @@
 
         try {
             XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                    new TestUtils.TestSourceLocation(getClass(), "testDiff6()"), input);
+                    TestUtils.sourceFile(getClass(), "testDiff6()"), input);
             xmlDocument.getRootNode();
             fail("Exception not thrown");
         } catch (RuntimeException expected) {
@@ -305,7 +304,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testOtherToolsInstruction"), input);
+                TestUtils.sourceFile(getClass(), "testOtherToolsInstruction"), input);
         xmlDocument.getRootNode();
     }
 
@@ -335,9 +334,9 @@
                 + "\n"
                 + "</manifest>";
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), reference);
+                TestUtils.sourceFile(getClass(), "testDiff1()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), other);
+                TestUtils.sourceFile(getClass(), "testDiff1()"), other);
 
         assertFalse(refDocument.getRootNode().getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
                 "com.example.lib3.activityOne").get()
@@ -372,9 +371,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), reference);
+                TestUtils.sourceFile(getClass(), "testDiff2()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), other);
+                TestUtils.sourceFile(getClass(), "testDiff2()"), other);
 
         assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
@@ -411,9 +410,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), reference);
+                TestUtils.sourceFile(getClass(), "testDiff3()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), other);
+                TestUtils.sourceFile(getClass(), "testDiff3()"), other);
 
         assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
@@ -450,9 +449,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff4()"), reference);
+                TestUtils.sourceFile(getClass(), "testDiff4()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff4()"), other);
+                TestUtils.sourceFile(getClass(), "testDiff4()"), other);
         assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
                 ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
                 .compareTo(
@@ -489,9 +488,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff5()"), reference);
+                TestUtils.sourceFile(getClass(), "testDiff5()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff5()"), other);
+                TestUtils.sourceFile(getClass(), "testDiff5()"), other);
 
         assertFalse(refDocument.compareTo(otherDocument).isPresent());
     }
@@ -525,9 +524,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff6()"), reference);
+                TestUtils.sourceFile(getClass(), "testDiff6()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testDiff6()"), other);
+                TestUtils.sourceFile(getClass(), "testDiff6()"), other);
         assertTrue(
                 refDocument.getRootNode().getNodeByTypeAndKey(
                         ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
@@ -568,9 +567,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testMerge()"), reference);
+                TestUtils.sourceFile(getClass(), "testMerge()"), reference);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testMerge()"), other);
+                TestUtils.sourceFile(getClass(), "testMerge()"), other);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -618,9 +617,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -669,9 +668,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -717,9 +716,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -772,9 +771,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -827,9 +826,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -875,9 +874,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -929,9 +928,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -976,9 +975,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1029,9 +1028,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1078,9 +1077,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1128,9 +1127,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1172,9 +1171,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1216,9 +1215,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1266,9 +1265,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1309,9 +1308,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1348,9 +1347,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1395,9 +1394,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+                TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1480,11 +1479,11 @@
         KeyResolver<String> keyResolver = (KeyResolver<String>) Mockito.mock(KeyResolver.class);
         when(keyResolver.resolve(any(String.class))).thenReturn("valid");
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(keyResolver,
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument firstLibrary = TestUtils.xmlDocumentFromString(keyResolver,
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+                TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
         XmlDocument secondLibrary = TestUtils.xmlDocumentFromString(keyResolver,
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+                TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1547,11 +1546,11 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+                TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
         XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+                TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1601,9 +1600,9 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+                TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1675,11 +1674,11 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
         XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+                TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
         XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+                TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
@@ -1756,10 +1755,10 @@
                 + "</manifest>";
 
         XmlDocument refDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+                TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
 
         XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
-                new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+                TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
 
         MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
                 new StdLogger(StdLogger.Level.VERBOSE));
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java
index 65032a2..cb5ea4a 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java
@@ -17,10 +17,10 @@
 package com.android.manifmerger;
 
 import com.android.SdkConstants;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.ide.common.xml.XmlFormatPreferences;
 import com.android.ide.common.xml.XmlFormatStyle;
 import com.android.ide.common.xml.XmlPrettyPrinter;
-import com.android.utils.PositionXmlParser;
 import com.google.common.base.Optional;
 
 import junit.framework.TestCase;
@@ -51,7 +51,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),"testToolsPrefix()"), input);
+                TestUtils.sourceFile(getClass(), "testToolsPrefix()"), input);
         Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
                 .getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
         assertTrue(applicationOptional.isPresent());
@@ -87,7 +87,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),"testPrettyPrint()"), input);
+                TestUtils.sourceFile(getClass(), "testPrettyPrint()"), input);
         Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
                 .getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
         assertTrue(applicationOptional.isPresent());
@@ -110,7 +110,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(),"testToolsPrefix()"),input);
+                TestUtils.sourceFile(getClass(), "testToolsPrefix()"),input);
         Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
                 .getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
         assertTrue(applicationOptional.isPresent());
@@ -123,17 +123,17 @@
         assertEquals("replace", tools.getNodeValue());
 
         // check positions.
-        PositionXmlParser.Position applicationPosition = applicationOptional.get().getPosition();
+        SourcePosition applicationPosition = applicationOptional.get().getPosition();
         assertNotNull(applicationPosition);
-        assertEquals(6, applicationPosition.getLine());
-        assertEquals(5, applicationPosition.getColumn());
+        assertEquals(6, applicationPosition.getStartLine() + 1);
+        assertEquals(5, applicationPosition.getStartColumn() + 1);
 
         XmlAttribute xmlAttribute =
                 new XmlAttribute(applicationOptional.get(), tools, null /* AttributeModel */);
-        PositionXmlParser.Position toolsPosition = xmlAttribute.getPosition();
+        SourcePosition toolsPosition = xmlAttribute.getPosition();
         assertNotNull(toolsPosition);
-        assertEquals(6, toolsPosition.getLine());
-        assertEquals(51, toolsPosition.getColumn());
+        assertEquals(6, toolsPosition.getStartLine() + 1);
+        assertEquals(51, toolsPosition.getStartColumn() + 1);
     }
 
     public void testUnusualPrefixes()
@@ -150,7 +150,7 @@
                 + "</manifest>";
 
         XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
-                new TestUtils.TestSourceLocation(getClass(), "testUnusualPrefixes()"), input);
+                TestUtils.sourceFile(getClass(), "testUnusualPrefixes()"), input);
         Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
                 .getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
         assertTrue(applicationOptional.isPresent());
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml
index 4d2b87a..bbaf28b 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml
@@ -205,4 +205,4 @@
 </manifest>
 
 @errors
-WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test1_lib1.xml:1:1
\ No newline at end of file
+WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test1_lib1.xml:1:1-12
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml
index 0e94eae..036c1a2 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml
@@ -143,5 +143,5 @@
 
 @errors
 
-ERROR:Overlay manifest:package attribute declared at ManifestMerger2Test0_overlay1.xml:3:5 value=\(suffix.not.allowed.in.overlay\)
-    has a different value=\(com.example.app1\) declared in main manifest at ManifestMerger2Test1_main.xml:9:5
\ No newline at end of file
+ERROR:Overlay manifest:package attribute declared at ManifestMerger2Test0_overlay1.xml:3:5-44 value=\(suffix.not.allowed.in.overlay\)
+    has a different value=\(com.example.app1\) declared in main manifest at ManifestMerger2Test1_main.xml:9:5-31
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml
index 83adc41..24e80ce 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml
@@ -114,5 +114,5 @@
 </manifest>
 
 @errors
-ERROR:Attribute application@name value=\(com.example.app1.TheApp\) from ManifestMerger2Test0_main.xml:8:13
-    is also present at ManifestMerger2Test1_lib1_widget.xml:6:18 value=\(com.example.lib1.TheApp\)
\ No newline at end of file
+ERROR:Attribute application@name value=\(com.example.app1.TheApp\) from ManifestMerger2Test0_main.xml:8:13-34
+    is also present at ManifestMerger2Test1_lib1_widget.xml:6:18-39 value=\(com.example.lib1.TheApp\).
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml
index bf25232..6850048 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml
@@ -115,4 +115,4 @@
 </manifest>
 
 @errors
-WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:1:1
\ No newline at end of file
+WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:1:1-15:12
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml
index 0a85ae3..eab41c6 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml
@@ -58,6 +58,6 @@
 
 @errors
 
-ERROR:Attribute uses-library#com.example.SomeLibrary2_RequiredTrue@required at ManifestMerger2Test0_main.xml:27:13 has an illegal value=\(booh!\), expected 'true' or 'false'
-ERROR:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9
+ERROR:Attribute uses-library#com.example.SomeLibrary2_RequiredTrue@required at ManifestMerger2Test0_main.xml:27:13-37 has an illegal value=\(booh!\), expected 'true' or 'false'
+ERROR:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9-37:39 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9-32:40
 ERROR:Validation failed, exiting
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml
index 25ff7b8..4cda757 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml
@@ -100,9 +100,9 @@
 
 @errors
 
-ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:10:9
-ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:9:9
-ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:8:9
-WARNING:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9
-ERROR:Attribute uses-library#com.example.SomeLibrary4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:25:13 has an illegal value=\(foo\), expected 'true' or 'false'
+ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:10:9-49
+ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:9:9-50
+ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:8:9-25
+WARNING:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9-37:40 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9-32:40
+ERROR:Attribute uses-library#com.example.SomeLibrary4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:25:13-35 has an illegal value=\(foo\), expected 'true' or 'false'
 ERROR:Validation failed, exiting
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml
index 3109de4..7fdc779 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml
@@ -92,12 +92,10 @@
             />
 
     <android:uses-permission
-            android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-            android:maxSdkVersion="18" />
+            android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <android:uses-permission
-            android:name="android.permission.READ_EXTERNAL_STORAGE"
-            android:maxSdkVersion="18" />
+            android:name="android.permission.READ_EXTERNAL_STORAGE" />
 
     <!-- I should remain last -->
     <application android:name="com.example.TheApp" />
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml
index 3701656..247cc32 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml
@@ -194,10 +194,10 @@
 
 @errors
 
-ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:6:5
-ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:7:5
-ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:8:5
-ERROR:Attribute uses-feature#com.example.SomeFeature3_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:18:9 has an illegal value=\(booh!\), expected 'true' or 'false'
-ERROR:Attribute uses-feature#com.example.SomeFeature4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:23:9 has an illegal value=\(foo\), expected 'true' or 'false'
+ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:6:5-21
+ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:7:5-46
+ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:8:5-45
+ERROR:Attribute uses-feature#com.example.SomeFeature3_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:18:9-33 has an illegal value=\(booh!\), expected 'true' or 'false'
+ERROR:Attribute uses-feature#com.example.SomeFeature4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:23:9-31 has an illegal value=\(foo\), expected 'true' or 'false'
 ERROR:Validation failed, exiting
-WARNING:Element uses-feature#com.example.SomeFeature3_RequiredFalse at ManifestMerger2Test0_main.xml:26:5 duplicated with element declared at ManifestMerger2Test0_main.xml:21:5
+WARNING:Element uses-feature#com.example.SomeFeature3_RequiredFalse at ManifestMerger2Test0_main.xml:26:5-28:36 duplicated with element declared at ManifestMerger2Test0_main.xml:21:5-23:36
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml
index 8b99039..8cd5392 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml
@@ -151,8 +151,8 @@
 
 @errors
 
-ERROR:Attribute uses-feature#0@glEsVersion at ManifestMerger2Test2_lib2.xml:21:9 is not a valid hexadecimal 32 bit value, found 0
-ERROR:Attribute uses-feature#0x0000FFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:27:9 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0x0000FFFF
-ERROR:Attribute uses-feature#0xFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:34:9 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0xFFFFFFFF
-ERROR:Attribute uses-feature#0xFFFFFFFFFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:40:9 is not a valid hexadecimal 32 bit value, found 0xFFFFFFFFFFFFFFFF
+ERROR:Attribute uses-feature#0@glEsVersion at ManifestMerger2Test2_lib2.xml:21:9-32 is not a valid hexadecimal 32 bit value, found 0
+ERROR:Attribute uses-feature#0x0000FFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:27:9-41 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0x0000FFFF
+ERROR:Attribute uses-feature#0xFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:34:9-41 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0xFFFFFFFF
+ERROR:Attribute uses-feature#0xFFFFFFFFFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:40:9-49 is not a valid hexadecimal 32 bit value, found 0xFFFFFFFFFFFFFFFF
 ERROR:Validation failed, exiting
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml
index 39cbf3d..1d777cc 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml
@@ -155,9 +155,9 @@
 
 @errors
 
-ERROR:Attribute uses-configuration@reqFiveWayNav value=\(true\) from ManifestMerger2Test0_main.xml:18:9
-    is also present at ManifestMerger2Test2_lib2.xml:7:9 value=\(false\)
-ERROR:Attribute uses-configuration@reqNavigation value=\(nonav\) from ManifestMerger2Test0_main.xml:21:9
-    is also present at ManifestMerger2Test2_lib2.xml:8:9 value=\(trackball\)
-ERROR:Attribute uses-configuration@reqTouchScreen value=\(stylus\) from ManifestMerger2Test0_main.xml:22:9
-    is also present at ManifestMerger2Test2_lib2.xml:9:9 value=\(finger\)
\ No newline at end of file
+ERROR:Attribute uses-configuration@reqFiveWayNav value=\(true\) from ManifestMerger2Test0_main.xml:18:9-37
+    is also present at ManifestMerger2Test2_lib2.xml:7:9-38 value=\(false\).
+ERROR:Attribute uses-configuration@reqNavigation value=\(nonav\) from ManifestMerger2Test0_main.xml:21:9-38
+    is also present at ManifestMerger2Test2_lib2.xml:8:9-42 value=\(trackball\).
+ERROR:Attribute uses-configuration@reqTouchScreen value=\(stylus\) from ManifestMerger2Test0_main.xml:22:9-40
+    is also present at ManifestMerger2Test2_lib2.xml:9:9-40 value=\(finger\).
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml
index 2ffafe8..3ae1b7b 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml
@@ -190,4 +190,4 @@
 
 @errors
 
-WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:4:1
+WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:4:1-21:12
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml
index db5ac81..ef23160 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml
@@ -86,12 +86,10 @@
        </activity>
    </application>
    <android:uses-permission
-           android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-           android:maxSdkVersion="18" />
+           android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <android:uses-permission
-           android:name="android.permission.READ_EXTERNAL_STORAGE"
-           android:maxSdkVersion="18" />
+           android:name="android.permission.READ_EXTERNAL_STORAGE" />
 
 </manifest>
 
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml
index 81b0a42..74f889e 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml
@@ -122,5 +122,5 @@
 
 @errors
 
-ERROR:Attribute meta-data#name.for.backup.api.key@value value=\(the_apps_backup_api_key\) from ManifestMerger2Test0_main.xml:33:13
-    is also present at ManifestMerger2Test1_lib1.xml:16:13 value=\(the_library1_backup_api_key\)
\ No newline at end of file
+ERROR:Attribute meta-data#name.for.backup.api.key@value value=\(the_apps_backup_api_key\) from ManifestMerger2Test0_main.xml:33:13-52
+    is also present at ManifestMerger2Test1_lib1.xml:16:13-56 value=\(the_library1_backup_api_key\).
\ No newline at end of file
diff --git a/build-system/profile/build.gradle b/build-system/profile/build.gradle
index cfd3bdd..b339a6c 100644
--- a/build-system/profile/build.gradle
+++ b/build-system/profile/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 
 dependencies {
     compile project(':base:annotations')
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java b/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java
index 2b495ae..23330e1 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java
@@ -38,9 +38,12 @@
     VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT(9),
     VARIANT_MANAGER_CREATE_LINT_TASKS(10),
     VARIANT_MANAGER_CREATE_TESTS_TASKS(11),
+    VARIANT_MANAGER_CREATE_VARIANTS(12),
     RESOLVE_DEPENDENCIES(12),
     INITIAL_METADATA(100),
     FINAL_METADATA(101),
+    GENERAL_CONFIG(102),
+    VARIANT_CONFIG(103),
 
     // ApplicationTaskManager per variant tasks.
     APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK(1000),
@@ -55,8 +58,8 @@
     APP_TASK_MANAGER_CREATE_NDK_TASK(1009),
     APP_TASK_MANAGER_CREATE_SPLIT_TASK(1010),
     APP_TASK_MANAGER_CREATE_PACKAGING_TASK(1011),
-    APP_TASK_MANAGER_CREATE_PREPROCESS_RESOURCES_TASK(1012),
-    APP_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(1013),
+    @Deprecated APP_TASK_MANAGER_CREATE_PREPROCESS_RESOURCES_TASK(1012),
+    @Deprecated APP_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(1013),
     APP_TASK_MANAGER_CREATE_LINT_TASK(1014),
 
     // LibraryTaskManager per variant tasks.
@@ -76,7 +79,7 @@
     LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK(2013),
     LIB_TASK_MANAGER_CREATE_PROGUARD_TASK(2014),
     LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR(2015),
-    LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(2016),
+    @Deprecated LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(2016),
     LIB_TASK_MANAGER_CREATE_LINT_TASK(2017),
 
     // TASK_EXECUTION
@@ -105,15 +108,13 @@
     TASK_COPY(3022),
     TASK_LINT(3023);
 
-
     int getId() {
         return id;
     }
 
-
     private final int id;
+
     ExecutionType(int id) {
         this.id = id;
     }
-
 }
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java
index 9c26561..c516726 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java
@@ -19,19 +19,16 @@
 import com.android.annotations.NonNull;
 import com.android.builder.tasks.Job;
 import com.android.builder.tasks.JobContext;
-import com.android.builder.tasks.QueueThreadContext;
+import com.android.builder.tasks.QueueThreadContextAdapter;
 import com.android.builder.tasks.Task;
 import com.android.builder.tasks.WorkQueue;
 import com.android.utils.ILogger;
 import com.google.gson.Gson;
 
-import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.Writer;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Records all the {@link ExecutionRecord} for a process, in order it was received and sends then
@@ -66,21 +63,13 @@
 
 
 
-    private class WorkQueueContext implements QueueThreadContext<ExecutionRecordWriter> {
-        @Override
-        public void creation(@NonNull Thread t) throws IOException {
-        }
-
+    private class WorkQueueContext extends QueueThreadContextAdapter<ExecutionRecordWriter> {
         @Override
         public void runTask(@NonNull Job<ExecutionRecordWriter> job) throws Exception {
             job.runTask(singletonJobContext);
         }
 
         @Override
-        public void destruction(@NonNull Thread t) throws IOException, InterruptedException {
-        }
-
-        @Override
         public void shutdown() {
             try {
                 singletonJobContext.getPayload().close();
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java
index 7ab4269..ca42078 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java
@@ -22,6 +22,8 @@
 import com.android.utils.ILogger;
 import com.android.utils.StdLogger;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 
 import java.io.BufferedInputStream;
@@ -72,7 +74,10 @@
         }
     }
 
-    public static void initialize(ILogger logger, File out) throws IOException {
+    public static void initialize(
+            @NonNull ILogger logger,
+            @NonNull File out,
+            @NonNull List<Recorder.Property> properties) throws IOException {
 
         synchronized (LOCK) {
             if (sINSTANCE.isInitialized() || !isEnabled()) {
@@ -81,19 +86,40 @@
             sINSTANCE.setLogger(logger);
             sINSTANCE.setOutputFile(out);
             sINSTANCE.setRecordWriter(new ProcessRecorder.JsonRecordWriter(new FileWriter(out)));
-            publishInitialRecords();
+            sINSTANCE.get(); // Initialize the ProcessRecorder instance
+            publishInitialRecords(properties);
         }
     }
 
-    public static void publishInitialRecords() {
-        ThreadRecorder.get().record(ExecutionType.INITIAL_METADATA, Recorder.EmptyBlock,
-                new Recorder.Property("build_id", UUID.randomUUID().toString()),
-                new Recorder.Property("os_name", System.getProperty("os.name")),
-                new Recorder.Property("os_version", System.getProperty("os.version")),
-                new Recorder.Property("java_version", System.getProperty("java.version")),
-                new Recorder.Property("java_vm_version", System.getProperty("java.vm.version")),
-                new Recorder.Property("max_memory",
-                        Long.toString(Runtime.getRuntime().maxMemory())));
+    public static void publishInitialRecords(@NonNull List<Recorder.Property> properties) {
+
+        List<Recorder.Property> propertyList = Lists.newArrayListWithExpectedSize(
+                6 + properties.size());
+
+        propertyList.add(new Recorder.Property(
+                "build_id",
+                UUID.randomUUID().toString()));
+        propertyList.add(new Recorder.Property(
+                "os_name",
+                System.getProperty("os.name")));
+        propertyList.add(new Recorder.Property(
+                "os_version",
+                System.getProperty("os.version")));
+        propertyList.add(new Recorder.Property(
+                "java_version",
+                System.getProperty("java.version")));
+        propertyList.add(new Recorder.Property(
+                "java_vm_version",
+                System.getProperty("java.vm.version")));
+        propertyList.add(new Recorder.Property(
+                "max_memory",
+                Long.toString(Runtime.getRuntime().maxMemory())));
+        propertyList.addAll(properties);
+
+        ThreadRecorder.get().record(
+                ExecutionType.INITIAL_METADATA,
+                Recorder.EmptyBlock,
+                propertyList);
     }
 
     private static boolean sENABLED = !Strings.isNullOrEmpty(System.getenv("RECORD_SPANS"));
@@ -115,7 +141,8 @@
         ProcessRecorder.resetForTests();
         setEnabled(true);
         sINSTANCE.setRecordWriter(recordWriter);
-        publishInitialRecords();
+        sINSTANCE.get(); // Initialize the ProcessRecorder instance
+        publishInitialRecords(ImmutableList.<Recorder.Property>of());
     }
 
     static boolean isEnabled() {
@@ -146,7 +173,7 @@
         return sINSTANCE;
     }
 
-    private boolean isInitialized() {
+    boolean isInitialized() {
         return processRecorder != null;
     }
 
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java b/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java
index b83d419..7d3815c 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 import com.google.common.base.Objects;
 
+import java.util.List;
 import java.util.concurrent.Callable;
 
 /**
@@ -113,6 +114,23 @@
     <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
             Property... properties);
 
+
+    /**
+     * Records the time elapsed while executing a {@link Block} and saves the resulting {@link
+     * ExecutionRecord} to {@link ProcessRecorder}.
+     *
+     * @param executionType the task type, so aggregation can be performed.
+     * @param block         the block of code to execution and measure.
+     * @param properties    optional list of free formed properties to save in the {@link
+     *                      ExecutionRecord}
+     * @param <T>           the type of the returned value from the block.
+     * @return the value returned from the block (including null) or null if the block execution
+     * raised an exception which was subsequently swallowed by {@link Block#handleException(Exception)}
+     */
+    @Nullable
+    <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
+            @NonNull List<Property> properties);
+
     /**
      * Allocate a new recordId that can be used to create a {@link ExecutionRecord} and record
      * an execution span. This method is useful when the code span to measure cannot be expressed
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java b/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java
index 94c8baa..60b8fee 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java
@@ -21,9 +21,9 @@
 import com.google.common.collect.ImmutableList;
 
 import java.util.ArrayDeque;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -44,6 +44,13 @@
         @Override
         public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
                 Property... properties) {
+            return record(executionType, block, Collections.<Property>emptyList());
+        }
+
+        @Nullable
+        @Override
+        public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
+                @NonNull List<Property> properties) {
             try {
                 return block.call();
             } catch (Exception e) {
@@ -66,7 +73,7 @@
 
 
     public static Recorder get() {
-        return ProcessRecorderFactory.isEnabled() ? recorder : dummyRecorder;
+        return ProcessRecorderFactory.getFactory().isInitialized() ? recorder : dummyRecorder;
     }
 
     private static  class PartialRecord {
@@ -118,26 +125,33 @@
         ProcessRecorder.get().writeRecord(executionRecord);
     }
 
-
     @Nullable
     @Override
     public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
             Property... properties) {
 
+        List<Recorder.Property> propertyList = properties == null
+                ? ImmutableList.<Recorder.Property>of()
+                : ImmutableList.copyOf(properties);
+
+        return record(executionType, block, propertyList);
+    }
+
+    @Nullable
+    @Override
+    public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
+            @NonNull List<Property> properties) {
+
         long thisRecordId = ProcessRecorder.allocateRecordId();
 
         // am I a child ?
         Long parentId = recordStacks.get().peek();
 
-        List<Recorder.Property> propertyList = properties == null
-                ? ImmutableList.<Recorder.Property>of()
-                : ImmutableList.copyOf(properties);
-
         long startTimeInMs = System.currentTimeMillis();
 
         final PartialRecord currentRecord = new PartialRecord(executionType,
                 thisRecordId, parentId == null ? 0 : parentId,
-                startTimeInMs, propertyList);
+                startTimeInMs, properties);
 
         recordStacks.get().push(thisRecordId);
         try {
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/Job.java b/build-system/profile/src/main/java/com/android/builder/tasks/Job.java
index a10ae8d..61c8897 100644
--- a/build-system/profile/src/main/java/com/android/builder/tasks/Job.java
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/Job.java
@@ -16,6 +16,7 @@
 
 package com.android.builder.tasks;
 
+import com.android.annotations.NonNull;
 import com.google.common.base.Objects;
 
 import java.io.IOException;
@@ -42,7 +43,7 @@
         return mJobTitle;
     }
 
-    public void runTask(JobContext<T> jobContext) throws IOException {
+    public void runTask(@NonNull JobContext<T> jobContext) throws IOException {
         mTask.run(this, jobContext);
     }
 
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java b/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java
index 969fb77..0bd9ac2 100644
--- a/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java
@@ -16,6 +16,8 @@
 
 package com.android.builder.tasks;
 
+import com.android.annotations.Nullable;
+
 /**
  * Contains contextual (and usually mutable) job data.
  */
@@ -24,10 +26,11 @@
     private final T payload;
     private final long creationTime = System.currentTimeMillis();
 
-    public JobContext(T payload) {
+    public JobContext(@Nullable T payload) {
         this.payload = payload;
     }
 
+    @Nullable
     public T getPayload() {
         return payload;
     }
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/QueueThreadContextAdapter.java b/build-system/profile/src/main/java/com/android/builder/tasks/QueueThreadContextAdapter.java
new file mode 100644
index 0000000..7a913d4
--- /dev/null
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/QueueThreadContextAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.tasks;
+
+import com.android.annotations.NonNull;
+
+import java.io.IOException;
+
+/**
+ * Adapter for {@link QueueThreadContext} with empty methods implementations.
+ */
+public abstract class QueueThreadContextAdapter<T> implements QueueThreadContext<T> {
+
+    @Override
+    public void creation(@NonNull Thread t) throws IOException {
+    }
+
+    @Override
+    public void runTask(@NonNull Job<T> job) throws Exception {
+    }
+
+    @Override
+    public void destruction(@NonNull Thread t) throws IOException, InterruptedException {
+    }
+
+    @Override
+    public void shutdown() {
+    }
+}
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java b/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java
index 14a64b8..2a09c13 100644
--- a/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java
@@ -23,7 +23,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -35,6 +34,8 @@
  */
 public class WorkQueue<T> implements Runnable {
 
+    private static final boolean VERBOSE = System.getenv("GRADLE_WORK_QUEUE_VERBOSE") != null;
+
     private final ILogger mLogger;
 
     // queue name as human would understand.
@@ -127,9 +128,9 @@
     private synchronized void checkWorkforce() {
         if (mWorkThreads.isEmpty()
                 || (mPendingJobs.size() / mWorkThreads.size() > mGrowthTriggerRation)) {
-            mLogger.verbose("Request to incrementing workforce from %1$d", mWorkThreads.size());
+            verbose("Request to incrementing workforce from %1$d", mWorkThreads.size());
             if (mWorkThreads.size() >= MAX_WORKFORCE_SIZE) {
-                mLogger.verbose("Already at max workforce %1$d, denied.", MAX_WORKFORCE_SIZE);
+                verbose("Already at max workforce %1$d, denied.", MAX_WORKFORCE_SIZE);
                 return;
             }
             for (int i = 0; i < mMWorkforceIncrement; i++) {
@@ -138,12 +139,12 @@
                 mWorkThreads.add(t);
                 t.start();
             }
-            mLogger.verbose("thread-pool size=%1$d", mWorkThreads.size());
+            verbose("thread-pool size=%1$d", mWorkThreads.size());
         }
     }
 
     private synchronized void reduceWorkforce() throws InterruptedException {
-        mLogger.verbose("Decrementing workforce from " + mWorkThreads.size());
+        verbose("Decrementing workforce from " + mWorkThreads.size());
         // push a the right number of kiss of death tasks to shutdown threads.
         for (int i = 0; i < mMWorkforceIncrement; i++) {
            _push(new QueueTask<T>(QueueTask.ActionType.Death, null));
@@ -200,7 +201,7 @@
         // this
         try {
             try {
-                mLogger.verbose("Creating a new working thread %1$s", threadName);
+                verbose("Creating a new working thread %1$s", threadName);
                 mQueueThreadContext.creation(Thread.currentThread());
             } catch (IOException e) {
                 e.printStackTrace();
@@ -208,7 +209,7 @@
             while(true) {
                 final QueueTask<T> queueTask = mPendingJobs.take();
                 if (queueTask.actionType== QueueTask.ActionType.Death) {
-                    mLogger.verbose("Thread(%1$s): Death requested", threadName);
+                    verbose("Thread(%1$s): Death requested", threadName);
                     // we are done.
                     return;
                 }
@@ -219,7 +220,7 @@
                             "I got a null pending job out of the priority queue");
                     return;
                 }
-                mLogger.verbose("Thread(%1$s): scheduling %2$s", threadName, job.getJobTitle());
+                verbose("Thread(%1$s): scheduling %2$s", threadName, job.getJobTitle());
 
                 try {
                     mQueueThreadContext.runTask(job);
@@ -230,18 +231,18 @@
                 }
                 // wait for the job completion.
                 job.await();
-                mLogger.verbose("Thread(%1$s): job %2$s finished", threadName, job.getJobTitle());
+                verbose("Thread(%1$s): job %2$s finished", threadName, job.getJobTitle());
                 // we could potentially reduce the workforce at this point if we have little
                 // queuing comparatively to the number of worker threads but at this point, the
                 // overall process (gradle activity) is fairly short lived so skipping at this
                 // point.
-                mLogger.verbose("Thread(%1$s): queue size %2$d", threadName, mPendingJobs.size());
+                verbose("Thread(%1$s): queue size %2$d", threadName, mPendingJobs.size());
             }
         } catch (InterruptedException e) {
             mLogger.error(e, "Thread(%1$s): Interrupted", threadName);
         } finally {
             try {
-                mLogger.verbose("Thread(%1$s): destruction", threadName);
+                verbose("Thread(%1$s): destruction", threadName);
                 mQueueThreadContext.destruction(Thread.currentThread());
             } catch (IOException e) {
                 mLogger.error(e, "Thread(%1$s): %2$s", threadName, e.getMessage());
@@ -250,4 +251,10 @@
             }
         }
     }
+
+    private void verbose(String format, Object...args) {
+        if (VERBOSE) {
+            mLogger.verbose(format, args);
+        }
+    }
 }
diff --git a/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java b/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java
index ceed139..2608ae7 100644
--- a/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java
+++ b/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java
@@ -22,9 +22,9 @@
 
 import com.android.annotations.NonNull;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -46,12 +46,17 @@
         ProcessRecorderFactory.sINSTANCE = new ProcessRecorderFactory();
     }
 
+    @After
+    public void shutdown() throws InterruptedException {
+        ProcessRecorderFactory.shutdown();
+    }
+
     @Test
     public void testBasicRecord() throws InterruptedException {
         StringWriter stringWriter = new StringWriter();
         ProcessRecorder.JsonRecordWriter jsonRecordWriter =
                 new ProcessRecorder.JsonRecordWriter(stringWriter);
-        ProcessRecorderFactory.sINSTANCE.setRecordWriter(jsonRecordWriter);
+        ProcessRecorderFactory.initializeForTests(jsonRecordWriter);
         ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
                 new Recorder.Block<Integer>() {
                     @Override
@@ -74,7 +79,7 @@
         StringWriter stringWriter = new StringWriter();
         ProcessRecorder.JsonRecordWriter jsonRecordWriter =
                 new ProcessRecorder.JsonRecordWriter(stringWriter);
-        ProcessRecorderFactory.sINSTANCE.setRecordWriter(jsonRecordWriter);
+        ProcessRecorderFactory.initializeForTests(jsonRecordWriter);
         ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
                 new Recorder.Block<Integer>() {
                     @Override
@@ -105,7 +110,7 @@
             }
         };
 
-        ProcessRecorderFactory.sINSTANCE.setRecordWriter(recorderWriter);
+        ProcessRecorderFactory.initializeForTests(recorderWriter);
         ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
                 new Recorder.Block<Integer>() {
                     @Override
@@ -122,6 +127,8 @@
 
         ProcessRecorder.get().finish();
         setExecutionRecords(records);
+        // delete the initial metadata record.
+        assertEquals(ExecutionType.INITIAL_METADATA, records.remove(0).type);
         assertEquals(2, records.size());
         assertTrue(records.get(1).parentId == records.get(0).id);
     }
@@ -142,7 +149,8 @@
 
             }
         };
-        ProcessRecorderFactory.sINSTANCE.setRecordWriter(recorderWriter);
+        ProcessRecorderFactory.initializeForTests(recorderWriter);
+
         Integer value = ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
                 new Recorder.Block<Integer>() {
                     @Override
@@ -197,6 +205,8 @@
         assertNotNull(value);
         assertEquals(16, value.intValue());
         ProcessRecorder.get().finish();
+        // delete the initial metadata record.
+        assertEquals(ExecutionType.INITIAL_METADATA, records.remove(0).type);
         assertEquals(6, records.size());
         // re-order by event id.
         setExecutionRecords(records);
diff --git a/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java b/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java
index f77b5ed..71a9164 100644
--- a/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java
+++ b/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java
@@ -18,7 +18,7 @@
 
 import com.android.annotations.NonNull;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/chartlib/build.gradle b/chartlib/build.gradle
index 44557c7..24c58d6 100644
--- a/chartlib/build.gradle
+++ b/chartlib/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 
 group = 'com.android.tools.chartlib'
 archivesBaseName = 'chartlib'
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java b/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java
index 3476c59..483e8ad 100644
--- a/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java
+++ b/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java
@@ -23,13 +23,22 @@
 import java.awt.event.ActionListener;
 import java.awt.event.HierarchyEvent;
 import java.awt.event.HierarchyListener;
+import java.awt.geom.Path2D;
 import java.util.LinkedList;
 import java.util.List;
 
 import javax.swing.JComponent;
 import javax.swing.Timer;
 
-public class AnimatedComponent extends JComponent implements ActionListener, HierarchyListener {
+/**
+ * Base class for components that should change their look over time.
+ *
+ * At a minimum, child classes should override {@link #updateData()} and {@link #draw(Graphics2D)},
+ * as well as pay attention to the field {@link #mFrameLength} as it controls the behavior of timed
+ * animations.
+ */
+public abstract class AnimatedComponent extends JComponent
+        implements ActionListener, HierarchyListener {
 
     protected static final Font DEFAULT_FONT = new Font("Sans", Font.PLAIN, 10);
 
@@ -67,16 +76,16 @@
      * @param fraction the interpolation fraction.
      * @return the interpolated value.
      */
-    protected float lerp(float from, float to, float fraction) {
+    protected final float lerp(float from, float to, float fraction) {
         float q = (float) Math.pow(1.0f - fraction, mFrameLength);
         return from * q + to * (1.0f - q);
     }
 
-    public boolean isDrawDebugInfo() {
+    public final boolean isDrawDebugInfo() {
         return mDrawDebugInfo;
     }
 
-    public void setDrawDebugInfo(boolean drawDebugInfo) {
+    public final void setDrawDebugInfo(boolean drawDebugInfo) {
         mDrawDebugInfo = drawDebugInfo;
     }
 
@@ -103,7 +112,7 @@
         g2d.dispose();
     }
 
-    protected void addDebugInfo(String format, Object... values) {
+    protected final void addDebugInfo(String format, Object... values) {
         if (mDrawDebugInfo) {
             mDebugInfo.add(String.format(format, values));
         }
@@ -124,17 +133,15 @@
     }
 
     /**
-     * First step of the animation, this is where the data is read and the current animation
-     * values are fixed.
+     * First step of the animation, this is where the data is read and the current animation values
+     * are fixed.
      */
-    protected void updateData() {
-    }
+    protected abstract void updateData();
 
     /**
      * Renders the data constructed in the update phase to the given graphics context.
      */
-    protected void draw(Graphics2D g) {
-    }
+    protected abstract void draw(Graphics2D g);
 
     /**
      * Draws visual debug information.
@@ -144,12 +151,12 @@
 
 
     @Override
-    public void actionPerformed(ActionEvent actionEvent) {
+    public final void actionPerformed(ActionEvent actionEvent) {
         repaint();
     }
 
     @Override
-    public void hierarchyChanged(HierarchyEvent hierarchyEvent) {
+    public final void hierarchyChanged(HierarchyEvent hierarchyEvent) {
         if (mTimer.isRunning() && !isShowing()) {
             mTimer.stop();
         } else if (!mTimer.isRunning() && isShowing()) {
@@ -157,11 +164,39 @@
         }
     }
 
-    public void setUpdateData(boolean updateData) {
+    /**
+     * If true, this component will animate normally.
+     */
+    public final void setUpdateData(boolean updateData) {
         mUpdateData = updateData;
     }
 
-    public void step() {
+    /**
+     * Animate this component for a single frame and then pause. Note that this call will have no
+     * effect unless {@link #setUpdateData(boolean)} is set to {@code false} first.
+     */
+    public final void step() {
         mStep = true;
     }
+
+    protected static void drawArrow(Graphics2D g, float x, float y, float dx, float dy, float len,
+            Color color) {
+        Path2D.Float path = new Path2D.Float();
+        path.moveTo(x, y);
+        path.lineTo(x + dx * len, y + dy * len);
+        path.lineTo(x + dx * (len - 10) + dy * 10, y + dy * (len - 10) - dx * 10);
+        path.lineTo(x + dx * (len - 10) - dy * 10, y + dy * (len - 10) + dx * 10);
+        g.setColor(color);
+        g.draw(path);
+    }
+
+    protected static void drawMarker(Graphics2D g, float x, float y, Color color) {
+        Path2D.Float path = new Path2D.Float();
+        path.moveTo(x - 10, y);
+        path.lineTo(x + 10, y);
+        path.moveTo(x, y - 10);
+        path.lineTo(x, y + 10);
+        g.setColor(color);
+        g.draw(path);
+    }
 }
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/SunburstComponent.java b/chartlib/src/main/java/com/android/tools/chartlib/SunburstComponent.java
new file mode 100644
index 0000000..36e71a6
--- /dev/null
+++ b/chartlib/src/main/java/com/android/tools/chartlib/SunburstComponent.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2015 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.tools.chartlib;
+
+import com.android.annotations.NonNull;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.tree.TreeNode;
+
+/**
+ * Component which renders a
+ * <a href="https://en.wikipedia.org/wiki/Pie_chart#Ring_chart_.2F_Sunburst_chart_.2F_Multilevel_pie_chart">
+ * sunburst chart</a> that can be unrolled by setting its angle.
+ */
+public final class SunburstComponent extends AnimatedComponent {
+
+    private static final Color[] COLORS = {
+            new Color(0x6baed6),
+            new Color(0xc6dbef),
+            new Color(0xfd8d3c),
+            new Color(0xfdd0a2),
+            new Color(0x74c476),
+            new Color(0xc7e9c0),
+            new Color(0x9e9ac8),
+            new Color(0xdadaeb),
+            new Color(0x969696),
+            new Color(0xd9d9d9),
+    };
+
+    private static final Color[] HIGHLIGHTS = {
+            new Color(0x3182bd),
+            new Color(0x9ecae1),
+            new Color(0xe6550d),
+            new Color(0xfdae6b),
+            new Color(0x31a354),
+            new Color(0xa1d99b),
+            new Color(0x756bb1),
+            new Color(0xbcbddc),
+            new Color(0x636363),
+            new Color(0xbdbdbd),
+    };
+
+    private ValuedTreeNode mData;
+
+    private Slice mSlice;
+
+    private float mGap;
+
+    private float mStart;
+
+    private float mFixed;
+
+    private float mAngle;
+
+    private float mCurrentAngle;
+
+    private float mSeparator;
+
+    private boolean mAutoSize;
+
+    private float mSliceWidth;
+
+    private boolean myUseCount;
+
+    private int mySelectionLevel;
+
+    private int myZoomLevel;
+
+    private Slice mySelection;
+
+    private Slice myZoom;
+
+    private boolean myLockSelection;
+
+    private final List<SliceSelectionListener> mListeners;
+
+    // Calculated values
+    private float mX;
+
+    private float mY;
+
+    private float mMaxDepth;
+
+    private float mMaxSide;
+
+    private float mCenterX;
+
+    private float mCenterY;
+
+    private float mDelta;
+
+    private Point2D.Float mDirection;
+
+    private Map<Color, Path2D.Float> mPaths;
+
+    public SunburstComponent(@NonNull ValuedTreeNode data) {
+        super(30);
+        mData = data;
+        mSlice = new Slice(0.0f);
+        mSliceWidth = 50;
+        mGap = 50;
+        mX = -1;
+        mY = -1;
+        mCurrentAngle = mAngle = 360.0f;
+        mDelta = 0.0f;
+        mFixed = 60.0f;
+        mStart = 180.0f;
+        mSeparator = 1.0f;
+        mySelectionLevel = -1;
+        myLockSelection = false;
+        mPaths = new HashMap<Color, Path2D.Float>();
+        addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) {
+                    myZoom = mySelection;
+                    myZoomLevel = mySelectionLevel;
+                    myLockSelection = false;
+                } else {
+                    myLockSelection = !myLockSelection && mySelection != null;
+                }
+            }
+        });
+        mListeners = new LinkedList<SliceSelectionListener>();
+    }
+
+    @Override
+    protected void draw(Graphics2D g) {
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        Dimension dim = getSize();
+
+        g.setColor(getBackground());
+        g.fillRect(0, 0, dim.width, dim.height);
+
+        mPaths.clear();
+        drawSlice(g, mSlice, 0.0f, 0.0f, 1.0f);
+        for (Map.Entry<Color, Path2D.Float> entry : mPaths.entrySet()) {
+            g.setColor(entry.getKey());
+            g.fill(entry.getValue());
+        }
+    }
+
+    @Override
+    protected void updateData() {
+
+        Dimension dim = getSize();
+        mX = dim.width * 0.5f;
+        mY = dim.height * 0.5f;
+
+        updateArea();
+        updateStructure(mSlice, mData, false);
+
+        mCurrentAngle = Math.abs(mCurrentAngle - mAngle) < 0.1f ? mAngle
+                : lerp(mCurrentAngle, mAngle, 0.999f);
+        if (mAutoSize) {
+            float full = Math.min(mMaxDepth, mMaxSide) - mGap - 20;
+            float none = mMaxDepth * 2 - mGap - 20;
+            float factor = mCurrentAngle / 360.0f;
+            float depth = full * factor + none * (1 - factor);
+            mFixed = lerp(mFixed, (float) ((mMaxSide - 20) / Math.PI), 0.999f);
+            float width = depth / getMaxDepth(mSlice);
+            mSliceWidth = lerp(mSliceWidth, width, 0.999f);
+        }
+
+        // mDelta is the extra radius needed to keep the same length at the fixes radius
+        // (mDelta + mFixed * Rad(mCurrentAngle) == 2PI * mFixed =>
+        // mDelta + mFixed == 2PI * mFixed / Rad(mCurrentAngle) =>
+        mDelta = 360.0f * mFixed / mCurrentAngle - mFixed;
+        mDirection = new Point2D.Float((float) Math.cos(mStart * Math.PI / 180.0f),
+                -(float) Math.sin(mStart * Math.PI / 180.0f));
+
+        mCenterX = mX + (mDelta + mMaxDepth * (360.0f - mCurrentAngle) / 360.0f) * mDirection.x;
+        mCenterY = mY + (mDelta + mMaxDepth * (360.0f - mCurrentAngle) / 360.0f) * mDirection.y;
+
+        updateSelection();
+        updateSlice(mSlice, 0, myZoom == null);
+    }
+
+    private void updateSelection() {
+        Point mouse = getMousePosition();
+        if (!myLockSelection) {
+            boolean selection = false;
+            if (mouse != null) {
+                float value = 0;
+                float depth = 0;
+                if (mCurrentAngle > 0) {
+                    float distance = (float) mouse.distance(mCenterX, mCenterY);
+                    depth = (distance - mGap - mDelta) / mSliceWidth;
+                    float angle = -(float) Math
+                            .toDegrees(Math.atan2(mouse.y - mCenterY, mouse.x - mCenterX));
+                    angle = (angle - mStart - 180.0f + mCurrentAngle * 0.5f) % 360.0f;
+                    angle = angle < 0 ? angle + 360.0f : angle;
+                    value = angle / mCurrentAngle;
+                } else {
+                    float length = (float) (Math.PI * 2.0f * mFixed);
+                    // Do the dot product of the vector to the mouse with the unit vector to project to:
+                    depth = (mouse.x - mX) * mDirection.x + (mouse.y - mY) * mDirection.y - (mMaxDepth - mGap);
+                    value = (mouse.x - mX) * mDirection.y + (mouse.y - mY) * -mDirection.x;
+                    // Normalize:
+                    depth = -depth / mSliceWidth;
+                    value = -value / length + 0.5f;
+                }
+                selection = updateSelectedSlice(mSlice, depth, value, 0);
+            }
+            if (!selection) {
+                mySelectionLevel = -1;
+                if (mySelection != null) {
+                    mySelection = null;
+                    fireSliceSelected(new SliceSelectionEvent(null));
+                }
+            }
+        } else {
+            if (mySelection != null && mySelection.node == null) {
+                myLockSelection = false;
+                mySelectionLevel = -1;
+                mySelection = null;
+                fireSliceSelected(new SliceSelectionEvent(null));
+            }
+        }
+        if (myZoom != null && myZoom.node == null) {
+            resetZoom();
+        }
+    }
+
+    public void resetZoom() {
+        myZoom = null;
+        myZoomLevel = -1;
+    }
+
+    private boolean updateSlice(Slice slice, int level, boolean zoom) {
+        zoom = zoom || slice == myZoom;
+        boolean children = false;
+        for (int i = 0; i < slice.getChildrenCount(); i++) {
+            Slice child = slice.getChild(i);
+            children = updateSlice(child, level + 1, zoom) || children;
+        }
+        zoom = zoom || children;
+
+        slice.selected = lerp(slice.selected, slice == mySelection ? 1.0f : 0.0f, 0.99f);
+        slice.visible = lerp(slice.visible, zoom ? 1.0f : 0.0f, 0.99f);
+        slice.zoom = lerp(slice.zoom,
+                level < myZoomLevel ? (level == myZoomLevel - 1) ? 0.5f : 0.0f : 1.0f, 0.99f);
+
+        return zoom;
+    }
+
+    private boolean updateSelectedSlice(Slice slice, float depth, float value, int level) {
+        if (depth < 0.0f || value < 0.0f || value > 1.0f) {
+            return false;
+        } else if (depth < slice.getDepth()) {
+            mySelectionLevel = level;
+            if (mySelection != slice) {
+                mySelection = slice;
+                fireSliceSelected(new SliceSelectionEvent(slice.node));
+            }
+            return true;
+        } else {
+            depth -= slice.getDepth();
+            float total = 0.0f;
+            for (Slice child : slice.getChildren()) {
+                total += child.getValue();
+            }
+            float current = 0.0f;
+            for (int i = 0; i < slice.getChildrenCount(); i++) {
+                Slice child = slice.getChild(i);
+                float val = child.getValue() / total;
+                if (value < current + val || i == slice.getChildrenCount() - 1) {
+                    value = (value - current) / val;
+                    return updateSelectedSlice(child, depth, value, level + 1);
+                }
+                current += val;
+
+            }
+        }
+        return false;
+    }
+
+    private void fireSliceSelected(SliceSelectionEvent event) {
+        for (SliceSelectionListener listener : mListeners) {
+            listener.valueChanged(event);
+        }
+    }
+
+    @Override
+    protected void debugDraw(Graphics2D g2d) {
+        addDebugInfo("Total slices: %d", mData.getCount());
+        addDebugInfo("Paths %d", mPaths.size());
+        g2d.setColor(Color.GREEN);
+        drawArrow(g2d, mX, mY, mDirection.x, mDirection.y, mMaxDepth, Color.MAGENTA);
+        drawArrow(g2d, mX, mY, mDirection.y, -mDirection.x, mMaxSide, Color.MAGENTA);
+        if (mCurrentAngle == 0) {
+            Path2D.Float fixed = new Path2D.Float();
+            float length = (float) (Math.PI * 2.0f * mFixed) * 0.5f;
+            fixed.moveTo(
+                    mX + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.x
+                            - mDirection.y * length,
+                    mY + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.y
+                            + mDirection.x * length);
+            fixed.lineTo(
+                    mX + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.x
+                            + mDirection.y * length,
+                    mY + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.y
+                            - mDirection.x * length);
+
+            g2d.draw(fixed);
+        } else {
+            drawMarker(g2d, mCenterX, mCenterY, Color.BLUE);
+            Arc2D.Float fixed = new Arc2D.Float();
+            fixed.setArcByCenter(mCenterX, mCenterY, mDelta + mFixed,
+                    mStart + (360.0f - mCurrentAngle) * 0.5f, mCurrentAngle, Arc2D.OPEN);
+            g2d.draw(fixed);
+        }
+    }
+
+    private void updateArea() {
+        float angle = (float) Math.toRadians(mStart);
+        float a = (float) Math.cos(angle) * mY;
+        float b = (float) Math.sin(angle) * mX;
+        mMaxDepth = mX * mY / (float) Math.sqrt((a * a) + (b * b));
+        a = (float) Math.cos(angle + Math.PI * 0.5) * mY;
+        b = (float) Math.sin(angle + Math.PI * 0.5) * mX;
+        mMaxSide = mX * mY / (float) Math.sqrt((a * a) + (b * b));
+    }
+
+    float getFraction(ValuedTreeNode node) {
+        TreeNode parent = node.getParent();
+        assert parent == null || parent instanceof ValuedTreeNode;
+        if (myUseCount) {
+            return parent == null ? 1.0f
+                    : (float) node.getCount() / ((ValuedTreeNode) parent).getCount();
+        } else {
+            return parent == null ? 1.0f
+                    : (float) node.getValue() / ((ValuedTreeNode) parent).getValue();
+        }
+    }
+
+    static ValuedTreeNode getChildAt(ValuedTreeNode node, int i) {
+        TreeNode child = node.getChildAt(i);
+        assert child instanceof ValuedTreeNode;
+        return (ValuedTreeNode) child;
+    }
+
+    private float getMaxDepth(Slice slice) {
+        float depth = 0.0f;
+        for (Slice child : slice.getChildren()) {
+            depth = Math.max(depth, getMaxDepth(child));
+        }
+        return depth + slice.depth;
+    }
+
+    private boolean updateStructure(Slice slice, ValuedTreeNode node, boolean hasSiblings) {
+        if (node == null) {
+            slice.depth = lerp(slice.depth, hasSiblings ? slice.depth : 0.0f, 0.99f);
+            slice.value = lerp(slice.value, hasSiblings ? 0.0f : slice.value, 0.99f);
+        } else {
+            slice.depth = lerp(slice.depth, node.getParent() == null ? 0.0f : 1.0f, 0.99f);
+            slice.value = lerp(slice.value, getFraction(node), 0.99f);
+        }
+        slice.node = node;
+
+        int last = -1;
+        int slices = slice.getChildrenCount();
+        int nodes = node == null ? 0 : node.getChildCount();
+        for (int i = 0; i < slices; i++) {
+            Slice childSlice = slice.getChild(i);
+            ValuedTreeNode childNode = i < nodes ? getChildAt(node, i) : null;
+            if (updateStructure(childSlice, childNode, nodes > 0)) {
+                last = i;
+            }
+        }
+        // Test neighbours with the same color:
+        int c = slices > 0 ? slice.getChild(0).color
+                : ((slice.color + (int) (Math.random() * COLORS.length - 1) + 1) % COLORS.length);
+        for (int i = slices; i < nodes; i++) {
+            ValuedTreeNode childNode = getChildAt(node, i);
+            Slice childSlice = new Slice(slices > 0 ? 0.0f : getFraction(childNode));
+            childSlice.color = c;
+            childSlice.depth = slices > 0 ? 1.0f : 0.0f;
+            slice.addChild(childSlice);
+            if (updateStructure(childSlice, childNode, nodes > 0)) {
+                last = i;
+            }
+        }
+
+        if (last + 1 < slice.getChildrenCount()) {
+            slice.clearSublist(last + 1, slice.getChildrenCount());
+        }
+
+        return node != null || (slice.depth > 0.00001f && slice.value > 0.00001f) || last >= 0;
+    }
+
+    Path2D.Float getPath(Color color) {
+        Path2D.Float path = mPaths.get(color);
+        if (path == null) {
+            path = new Path2D.Float();
+            mPaths.put(color, path);
+        }
+        return path;
+    }
+
+    private void drawSlice(Graphics2D g, Slice slice, float depth, float from, float to) {
+        if (slice.getDepth() > 0.0f) { // Optimization for zero width slices
+            Color c = COLORS[slice.color];
+            float s = slice.selected;
+            Color b = HIGHLIGHTS[slice.color];
+            c = new Color((int) (b.getRed() * s + c.getRed() * (1 - s)),
+                    (int) (b.getGreen() * s + c.getGreen() * (1 - s)),
+                    (int) (b.getBlue() * s + c.getBlue() * (1 - s)));
+            Path2D.Float path = getPath(c);
+            if (mCurrentAngle == 0) {
+                float length = (float) (Math.PI * 2.0f * mFixed);
+                float delta = mGap + depth * mSliceWidth - mMaxDepth + mSeparator * 0.5f
+                        + slice.getBorder() * mSliceWidth;
+                float up = length * (0.5f - from) - mSeparator * 0.5f;
+                float down = length * (0.5f - to) + mSeparator * 0.5f;
+                float size = mSliceWidth * slice.getDepth() - mSeparator
+                        - slice.getBorder() * mSliceWidth * 2.0f;
+
+                float deltaX = mDirection.x * delta;
+                float deltaY = mDirection.y * delta;
+                float upX = mDirection.y * up;
+                float upY = -mDirection.x * up;
+                float downX = mDirection.y * down;
+                float downY = -mDirection.x * down;
+                float sizeX = mDirection.x * size;
+                float sizeY = mDirection.y * size;
+
+                if (up > down) {
+                    path.moveTo(mX - deltaX + upX, mY - deltaY + upY);
+                    path.lineTo(mX - deltaX + upX - sizeX, mY - deltaY + upY - sizeY);
+                    path.lineTo(mX - deltaX + downX - sizeX, mY - deltaY + downY - sizeY);
+                    path.lineTo(mX - deltaX + downX, mY - deltaY + downY);
+                    path.closePath();
+                }
+            } else {
+                float angle = (360.0f - mCurrentAngle) * 0.5f + mCurrentAngle * from + mStart;
+                float arc = mCurrentAngle * (to - from);
+
+                float radius = mSliceWidth * depth + mGap + mDelta;
+
+                float outerLen = (radius + mSliceWidth * slice.getDepth()) * (float) Math.toRadians(
+                        arc);
+                if (outerLen < 1) {
+                    path = getPath(Color.RED);
+                }
+                float outerRadius = radius + mSliceWidth * slice.getDepth() - mSeparator * 0.5f
+                        - slice.getBorder() * mSliceWidth;
+                float innerRadius = radius + mSeparator * 0.5f + slice.getBorder() * mSliceWidth;
+                float outerAngle = (float) Math.toDegrees(Math.asin(mSeparator / outerRadius));
+                if (outerAngle < arc && outerRadius > innerRadius) {
+                    Arc2D.Float outer = new Arc2D.Float();
+                    outer.setArcByCenter(mCenterX, mCenterY, outerRadius,
+                            angle + outerAngle * 0.5f, arc - outerAngle, Arc2D.OPEN);
+                    path.append(outer, false);
+
+                    float innerAngle = (float) Math.toDegrees(Math.asin(mSeparator / innerRadius));
+                    if (innerAngle < arc) {
+                        Arc2D.Float inner = new Arc2D.Float();
+                        inner.setArcByCenter(mCenterX, mCenterY, innerRadius,
+                                angle + innerAngle * 0.5f + arc - innerAngle, -(arc - innerAngle),
+                                Arc2D.OPEN);
+                        path.append(inner, true);
+                    } else {
+                        float r = (float) (mSeparator * 0.5f / Math
+                                .sin(Math.toRadians(arc * 0.5f)));
+                        float dx = (float) (Math.cos(Math.toRadians(angle + arc * 0.5f)) * r);
+                        float dy = (float) (Math.sin(Math.toRadians(angle + arc * 0.5f)) * r);
+                        path.lineTo(mCenterX + dx, mCenterY - dy);
+                    }
+                    path.lineTo(outer.getStartPoint().getX(), outer.getStartPoint().getY());
+                }
+            }
+        }
+
+        float total = 0.0f;
+        for (Slice child : slice.getChildren()) {
+            total += child.getValue();
+        }
+        float value = 0.0f;
+        for (Slice child : slice.getChildren()) {
+            float childFrom = from + (value / total) * (to - from);
+            float childTo = from + ((value + child.getValue()) / total) * (to - from);
+
+            drawSlice(g, child, depth + slice.getDepth(), childFrom, childTo);
+
+            value += child.getValue();
+        }
+    }
+
+    public void setGap(float gap) {
+        mGap = gap;
+    }
+
+    public void setSliceWidth(float sliceWidth) {
+        mSliceWidth = sliceWidth;
+    }
+
+    public void setAngle(float angle) {
+        mAngle = angle;
+    }
+
+    public void setStart(float start) {
+        mStart = start;
+        updateArea();
+    }
+
+    public void setFixed(int fixed) {
+        mFixed = fixed;
+    }
+
+    public float getGap() {
+        return mGap;
+    }
+
+    public float getSliceWidth() {
+        return mSliceWidth;
+    }
+
+    public float getStart() {
+        return mStart;
+    }
+
+    public float getFixed() {
+        return mFixed;
+    }
+
+    public float getAngle() {
+        return mAngle;
+    }
+
+    public float getSeparator() {
+        return mSeparator;
+    }
+
+    public void setSeparator(float separator) {
+        mSeparator = separator;
+    }
+
+    public void setData(ValuedTreeNode data) {
+        mData = data;
+    }
+
+    public ValuedTreeNode getData() {
+        return mData;
+    }
+
+    public void setAutoSize(boolean autoSize) {
+        mAutoSize = autoSize;
+    }
+
+    public void setUseCount(boolean useCount) {
+        myUseCount = useCount;
+    }
+
+    public void addSelectionListener(SliceSelectionListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeSelectionListener(SliceSelectionListener listener) {
+        mListeners.remove(listener);
+    }
+
+    static class Slice {
+
+        private ArrayList<Slice> children = new ArrayList<Slice>();
+
+        Slice parent;
+
+        float value;
+
+        float depth;
+
+        int color;
+
+        float hover;
+
+        float zoom;
+
+        float selected;
+
+        float visible;
+
+        ValuedTreeNode node;
+
+        public float getValue() {
+            return value * visible;
+        }
+
+        public float getBorder() {
+            return selected * depth * 0.05f;
+        }
+
+        public float getDepth() {
+            return zoom * depth + getBorder() * 2.0f;
+        }
+
+        public Slice(float value) {
+            this.value = value;
+            this.depth = 1.0f;
+            this.hover = 0.0f;
+            this.selected = 0.0f;
+            this.zoom = 1.0f;
+            this.visible = 1.0f;
+        }
+
+        public int getChildrenCount() {
+            return children.size();
+        }
+
+        public void addChild(Slice child) {
+            children.add(child);
+            child.parent = this;
+        }
+
+        public Slice getChild(int i) {
+            return children.get(i);
+        }
+
+        public void clearSublist(int from, int to) {
+            for (int i = from; i < to; i++) {
+                children.get(i).parent = null;
+            }
+            children.subList(from, to).clear();
+        }
+
+        public ArrayList<Slice> getChildren() {
+            return children;
+        }
+    }
+
+    public static class SliceSelectionEvent {
+
+        private final ValuedTreeNode mNode;
+
+        public SliceSelectionEvent(ValuedTreeNode node) {
+            mNode = node;
+        }
+
+        public ValuedTreeNode getNode() {
+            return mNode;
+        }
+    }
+
+    public interface SliceSelectionListener {
+        void valueChanged(SliceSelectionEvent e);
+    }
+}
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java b/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
index 57f0e31..d90b1b8 100644
--- a/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
+++ b/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
@@ -43,7 +43,7 @@
  * modifications to it while it's begin rendered, but objects of this class should not be accessed
  * from different threads.
  */
-public class TimelineComponent extends AnimatedComponent
+public final class TimelineComponent extends AnimatedComponent
         implements ActionListener, HierarchyListener {
 
     private static final Color TEXT_COLOR = new Color(128, 128, 128);
@@ -660,7 +660,7 @@
         }
     }
 
-    public static enum Style {
+    public enum Style {
         NONE,
         SOLID,
         DASHED
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/ValuedTreeNode.java b/chartlib/src/main/java/com/android/tools/chartlib/ValuedTreeNode.java
new file mode 100644
index 0000000..1838916
--- /dev/null
+++ b/chartlib/src/main/java/com/android/tools/chartlib/ValuedTreeNode.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.tools.chartlib;
+
+import javax.swing.tree.TreeNode;
+
+public interface ValuedTreeNode extends TreeNode {
+    int getCount();
+
+    int getValue();
+}
diff --git a/chartlib/src/test/java/AnimatedComponentVisualTests.java b/chartlib/src/test/java/AnimatedComponentVisualTests.java
index 68cb79b..c16089d 100644
--- a/chartlib/src/test/java/AnimatedComponentVisualTests.java
+++ b/chartlib/src/test/java/AnimatedComponentVisualTests.java
@@ -16,18 +16,24 @@
 
 import com.android.tools.chartlib.AnimatedComponent;
 import com.android.tools.chartlib.EventData;
+import com.android.tools.chartlib.SunburstComponent;
 import com.android.tools.chartlib.TimelineComponent;
 import com.android.tools.chartlib.TimelineData;
+import com.android.tools.chartlib.ValuedTreeNode;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
+import java.awt.LayoutManager;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.swing.Box;
@@ -42,8 +48,7 @@
 import javax.swing.UIManager;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
-
-import sun.awt.VerticalBagLayout;
+import javax.swing.tree.DefaultMutableTreeNode;
 
 public class AnimatedComponentVisualTests extends JDialog {
 
@@ -53,6 +58,7 @@
         JPanel contentPane = new JPanel(new BorderLayout());
         JButton close = new JButton("Close");
         JTabbedPane tabs = new JTabbedPane();
+        tabs.addTab("PieChart", getPieChartExample());
         tabs.addTab("Timeline", getTimelineExample());
 
         contentPane.setPreferredSize(new Dimension(1280, 1024));
@@ -76,18 +82,6 @@
         });
         controls.add(debug);
 
-        final JCheckBox update = new JCheckBox("Update");
-        update.setSelected(true);
-        update.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent actionEvent) {
-                for (AnimatedComponent component : mComponents) {
-                    component.setUpdateData(update.isSelected());
-                }
-            }
-        });
-        controls.add(update);
-
         final JButton step = new JButton("Step");
         step.addActionListener(new ActionListener() {
             @Override
@@ -97,10 +91,31 @@
                 }
             }
         });
+        final JCheckBox update = new JCheckBox("Update");
+        update.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent) {
+                for (AnimatedComponent component : mComponents) {
+                    component.setUpdateData(update.isSelected());
+                }
+                step.setEnabled(!update.isSelected());
+            }
+        });
+        update.setSelected(true);
+        step.setEnabled(false);
+        final JCheckBox dark = new JCheckBox("Dark");
+        dark.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent) {
+                setDarkMode(dark.isSelected());
+            }
+        });
+        controls.add(dark);
+        controls.add(update);
         controls.add(step);
-
         contentPane.add(controls, BorderLayout.WEST);
 
+        setDarkMode(false);
         setContentPane(contentPane);
         setModal(true);
         getRootPane().setDefaultButton(close);
@@ -113,10 +128,14 @@
         });
     }
 
-    static interface Value {
+    private void setDarkMode(boolean dark) {
+        for (AnimatedComponent c : mComponents) {
+            c.setBackground(dark ? new Color(60, 63, 65) : new Color(244, 244, 244));
+        }
+    }
 
+    interface Value {
         void set(int v);
-
         int get();
     }
 
@@ -138,6 +157,7 @@
         panel.add(slider, BorderLayout.CENTER);
         panel.add(new JLabel(name + ": "), BorderLayout.WEST);
         panel.add(text, BorderLayout.EAST);
+        panel.setAlignmentX(Component.LEFT_ALIGNMENT);
         return panel;
     }
 
@@ -145,19 +165,231 @@
         panel.setLayout(new BorderLayout());
         mComponents.add(animated);
         panel.add(animated, BorderLayout.CENTER);
-        JPanel controls = new JPanel(new VerticalBagLayout());
+
+        JPanel controls = new JPanel();
+        LayoutManager manager = new BoxLayout(controls, BoxLayout.Y_AXIS);
+        controls.setLayout(manager);
         controls.setPreferredSize(new Dimension(300, 800));
         panel.add(controls, BorderLayout.WEST);
         return controls;
     }
 
+    static class DataNode extends DefaultMutableTreeNode implements ValuedTreeNode {
+
+        private int mCount;
+        private int mValue;
+
+        public DataNode() {
+            this(0, 0);
+        }
+
+        public DataNode(int count, int value) {
+            mCount = count;
+            mValue = value;
+        }
+
+        @Override
+        public int getCount() {
+            return mCount;
+        }
+
+        @Override
+        public int getValue() {
+            return mValue;
+        }
+
+        public void add(int count, int value) {
+            mCount += count;
+            mValue += value;
+            if (parent instanceof DataNode) {
+                ((DataNode)parent).add(count, value);
+            }
+        }
+
+        public void addDataNode(DataNode dataNode) {
+            super.add(dataNode);
+            add(dataNode.getCount(), dataNode.getValue());
+        }
+    }
+
+    private JPanel getPieChartExample() {
+
+        final DataNode data = new DataNode();
+        data.addDataNode(new DataNode(1, 10));
+
+        final SunburstComponent layout = new SunburstComponent(data);
+
+        JPanel panel = new JPanel();
+        JPanel controls = createControlledPane(panel, layout);
+        final JLabel info = new JLabel("<No information yet>");
+        panel.add(info, BorderLayout.SOUTH);
+
+        controls.add(createVaribleSlider("Gap", 0, 200, new Value() {
+            @Override
+            public void set(int v) {
+                layout.setGap(v);
+            }
+
+            @Override
+            public int get() {
+                return (int) layout.getGap();
+            }
+        }));
+        controls.add(createVaribleSlider("Size", 0, 200, new Value() {
+            @Override
+            public void set(int v) {
+                layout.setSliceWidth(v);
+            }
+
+            @Override
+            public int get() {
+                return (int) layout.getSliceWidth();
+            }
+        }));
+        controls.add(createVaribleSlider("Angle", 0, 360, new Value() {
+            @Override
+            public void set(int v) {
+                layout.setAngle(v);
+            }
+
+            @Override
+            public int get() {
+                return (int) layout.getAngle();
+            }
+        }));
+        controls.add(createVaribleSlider("Start", 0, 360, new Value() {
+            @Override
+            public void set(int v) {
+                layout.setStart(v);
+            }
+
+            @Override
+            public int get() {
+                return (int) layout.getStart();
+            }
+        }));
+        controls.add(createVaribleSlider("Fixed", 1, 100, new Value() {
+            @Override
+            public void set(int v) {
+                layout.setFixed(v);
+            }
+
+            @Override
+            public int get() {
+                return (int) layout.getFixed();
+            }
+        }));
+        controls.add(createVaribleSlider("Separator", 0, 100, new Value() {
+            @Override
+            public void set(int v) {
+                layout.setSeparator(v);
+            }
+
+            @Override
+            public int get() {
+                return (int) layout.getSeparator();
+            }
+        }));
+        controls.add(createButton("Generate", new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent) {
+                generateLayoutData((DataNode) layout.getData(), 5);
+            }
+        }));
+        controls.add(createButton("Tree A", new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent) {
+                DataNode g = new DataNode();
+                g.addDataNode(createTree(1));
+                g.addDataNode(createValue());
+                g.addDataNode(createTree(1));
+                g.addDataNode(createValue());
+                g.addDataNode(createTree(0));
+                layout.setData(g);
+            }
+        }));
+        controls.add(createButton("Tree B", new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent) {
+                DataNode g = new DataNode();
+                g.addDataNode(createValue());
+                g.addDataNode(createValue());
+                g.addDataNode(createTree(0));
+                layout.setData(g);
+            }
+        }));
+        controls.add(createButton("Value", new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent actionEvent) {
+                DataNode g = new DataNode();
+                g.addDataNode(new DataNode(1, (int) (Math.random() * 50)));
+                layout.setData(g);
+            }
+        }));
+        controls.add(createCheckbox("Auto size", new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent itemEvent) {
+                layout.setAutoSize(itemEvent.getStateChange() == ItemEvent.SELECTED);
+            }
+        }));
+        controls.add(
+                new Box.Filler(new Dimension(0, 0), new Dimension(300, Integer.MAX_VALUE),
+                        new Dimension(300, Integer.MAX_VALUE)));
+
+        layout.addSelectionListener(new SunburstComponent.SliceSelectionListener() {
+            @Override
+            public void valueChanged(SunburstComponent.SliceSelectionEvent e) {
+                ValuedTreeNode node = e.getNode();
+                info.setText(node == null ? "<No selection>" : String.format("Value %d Count %d",
+                        node.getValue(), node.getCount()));
+            }
+        });
+        return panel;
+    }
+
+    private static DataNode createValue() {
+        return new DataNode(1, (int)(Math.random() * 50));
+    }
+
+    private static DataNode createTree(int depth) {
+        DataNode b = depth == 0 ? createValue() : createTree(depth - 1);
+        DataNode c = depth == 0 ? createValue() : createTree(depth - 1);
+        DataNode a = new DataNode();
+        a.addDataNode(b);
+        a.addDataNode(c);
+        return a;
+    }
+
     private static Component createButton(String label, ActionListener action) {
         JButton button = new JButton(label);
-        button.setPreferredSize(button.getPreferredSize());
         button.addActionListener(action);
+        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMaximumSize().height));
         return button;
     }
 
+    private static Component createCheckbox(String label, ItemListener action) {
+        JCheckBox button = new JCheckBox(label);
+        button.addItemListener(action);
+        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMaximumSize().height));
+        return button;
+    }
+
+    private static void generateLayoutData(DataNode data, int maxDepth) {
+        Random random = new Random();
+        int branch = random.nextInt(9) + 1;
+        for (int i = 0; i < branch; i++) {
+            int value = random.nextInt(1024);
+            if (maxDepth > 0 && random.nextInt(4) == 0) {
+                DataNode group = new DataNode();
+                group.add(new DataNode(1, value));
+                generateLayoutData(group, maxDepth - 1);
+                data.addDataNode(group);
+            } else {
+                data.addDataNode(new DataNode(1, value));
+            }
+        }
+    }
+
     private JPanel getTimelineExample() {
         final TimelineData data = new TimelineData(2, 2000);
         final EventData events = new EventData();
@@ -241,6 +473,7 @@
         controls.add(createEventButton(1, events, null));
         controls.add(createEventButton(2, events, variance));
 
+        controls.add(new Box.Filler(new Dimension(0, 0), new Dimension(300, Integer.MAX_VALUE), new Dimension(300, Integer.MAX_VALUE)));
         panel.add(timeline, BorderLayout.CENTER);
         return panel;
     }
diff --git a/common/build.gradle b/common/build.gradle
index 231a25e..904c8f9 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 dependencies {
diff --git a/common/src/main/java/com/android/SdkConstants.java b/common/src/main/java/com/android/SdkConstants.java
index cca12ec..933f652 100644
--- a/common/src/main/java/com/android/SdkConstants.java
+++ b/common/src/main/java/com/android/SdkConstants.java
@@ -34,7 +34,7 @@
  * <li><code>EXT_</code> File name extension, without the dot </li>
  * </ul>
  */
-@SuppressWarnings("javadoc") // Not documenting all the fields here
+@SuppressWarnings({"javadoc", "unused"}) // Not documenting all the fields here
 public final class SdkConstants {
     public static final int PLATFORM_UNKNOWN = 0;
     public static final int PLATFORM_LINUX = 1;
@@ -273,6 +273,8 @@
     public static final String FD_TEST = "androidTest";                 //$NON-NLS-1$
     /** Default java code folder name, i.e. "java" */
     public static final String FD_JAVA = "java";                        //$NON-NLS-1$
+    /** Default native code folder name, i.e. "jni" */
+    public static final String FD_JNI = "jni";                          //$NON-NLS-1$
     /** Default gradle folder name, i.e. "gradle" */
     public static final String FD_GRADLE = "gradle";                    //$NON-NLS-1$
     /** Default gradle wrapper folder name, i.e. "gradle/wrapper" */
@@ -358,6 +360,7 @@
     /** Name of the SDK extras folder. */
     public static final String FD_EXTRAS = "extras";                    //$NON-NLS-1$
     public static final String FD_M2_REPOSITORY = "m2repository";       //$NON-NLS-1$
+    public static final String FD_NDK = "ndk-bundle";                   //$NON-NLS-1$
     /**
      * Name of an extra's sample folder.
      * Ideally extras should have one {@link #FD_SAMPLES} folder containing
@@ -408,7 +411,7 @@
      * Note: if you need an URI specifically for the "android" namespace, consider using
      * {@link SdkConstants#NS_RESOURCES} instead.
      */
-    public final static String NS_CUSTOM_RESOURCES_S = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$
+    public static final String NS_CUSTOM_RESOURCES_S = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$
 
 
     /** The name of the uses-library that provides "android.test.runner" */
@@ -452,7 +455,7 @@
     /** Path of the bin folder of proguard folder relative to the sdk folder.
      *  This is an OS path, ending with a separator. */
     public static final String OS_SDK_TOOLS_PROGUARD_BIN_FOLDER =
-        SdkConstants.OS_SDK_TOOLS_FOLDER +
+        OS_SDK_TOOLS_FOLDER +
         "proguard" + File.separator +                                   //$NON-NLS-1$
         "bin" + File.separator;                                         //$NON-NLS-1$
 
@@ -582,6 +585,19 @@
     /** MockView is part of the layoutlib bridge and used to display classes that have
      * no rendering in the graphical layout editor. */
     public static final String CLASS_MOCK_VIEW = "com.android.layoutlib.bridge.MockView"; //$NON-NLS-1$
+    public static final String CLASS_LAYOUT_INFLATER = "android.view.LayoutInflater"; //$NON-NLS-1$
+
+    /* Android Design Support Class Constants */
+    public static final String CLASS_COORDINATOR_LAYOUT = "android.support.design.widget.CoordinatorLayout"; //$NON-NLS-1$
+    public static final String CLASS_APP_BAR_LAYOUT = "android.support.design.widget.AppBarLayout"; //$NON-NLS-1$
+    public static final String CLASS_FLOATING_ACTION_BUTTON = "android.support.design.widget.FloatingActionButton"; //$NON-NLS-1$
+    public static final String CLASS_COLLAPSING_TOOLBAR_LAYOUT = "android.support.design.widget.CollapsingToolbarLayout"; //$NON-NLS-1$
+    public static final String CLASS_NAVIGATION_VIEW = "android.support.design.widget.NavigationView"; //$NON-NLS-1$
+    public static final String CLASS_SNACKBAR = "android.support.design.widget.Snackbar"; //$NON-NLS-1$
+    public static final String CLASS_TAB_LAYOUT = "android.support.design.widget.TabLayout"; //$NON-NLS-1$
+    public static final String CLASS_TEXT_INPUT_LAYOUT = "android.support.design.widget.TextInputLayout"; //$NON-NLS-1$
+    public static final String CLASS_NESTED_SCROLL_VIEW = "android.support.v4.widget.NestedScrollView";  //$NON-NLS-1$
+
 
     /** Returns the appropriate name for the 'android' command, which is 'android.exe' for
      * Windows and 'android' for all other platforms. */
@@ -759,6 +775,7 @@
     public static final String TAG_DECLARE_STYLEABLE = "declare-styleable"; //$NON-NLS-1$
     public static final String TAG_EAT_COMMENT = "eat-comment";        //$NON-NLS-1$
     public static final String TAG_SKIP = "skip";                      //$NON-NLS-1$
+    public static final String TAG_SELECTOR = "selector";              //$NON-NLS-1$
 
     // Tags: XML
     public static final String TAG_HEADER = "header";                  //$NON-NLS-1$
@@ -817,10 +834,27 @@
     public static final String MULTI_AUTO_COMPLETE_TEXT_VIEW = "MultiAutoCompleteTextView"; //$NON-NLS-1$
     public static final String AUTO_COMPLETE_TEXT_VIEW = "AutoCompleteTextView"; //$NON-NLS-1$
     public static final String CHECKABLE = "Checkable";                //$NON-NLS-1$
+    public static final String TEXTURE_VIEW = "TextureView";           //$NON-NLS-1$
+
+    /* Android Design Support Tag Constants */
+    public static final String COORDINATOR_LAYOUT = CLASS_COORDINATOR_LAYOUT;
+    public static final String APP_BAR_LAYOUT = CLASS_APP_BAR_LAYOUT;
+    public static final String FLOATING_ACTION_BUTTON = CLASS_FLOATING_ACTION_BUTTON;
+    public static final String COLLAPSING_TOOLBAR_LAYOUT = CLASS_COLLAPSING_TOOLBAR_LAYOUT;
+    public static final String NAVIGATION_VIEW = CLASS_NAVIGATION_VIEW;
+    public static final String SNACKBAR = CLASS_SNACKBAR;
+    public static final String TAB_LAYOUT = CLASS_TAB_LAYOUT;
+    public static final String TEXT_INPUT_LAYOUT = CLASS_TEXT_INPUT_LAYOUT;
 
     // Tags: Drawables
     public static final String TAG_BITMAP = "bitmap";                  //$NON-NLS-1$
 
+    // Tags: Data-Binding
+    public static final String TAG_LAYOUT = "layout";                  //$NON-NLS-1$
+    public static final String TAG_DATA = "data";                      //$NON-NLS-1$
+    public static final String TAG_VARIABLE = "variable";              //$NON-NLS-1$
+    public static final String TAG_IMPORT = "import";                  //$NON-NLS-1$
+
     // Attributes: Manifest
     public static final String ATTR_EXPORTED = "exported";             //$NON-NLS-1$
     public static final String ATTR_PERMISSION = "permission";         //$NON-NLS-1$
@@ -830,6 +864,8 @@
     public static final String ATTR_PACKAGE = "package";               //$NON-NLS-1$
     public static final String ATTR_CORE_APP = "coreApp";              //$NON-NLS-1$
     public static final String ATTR_THEME = "theme";                   //$NON-NLS-1$
+    public static final String ATTR_SCHEME = "scheme";                 //$NON_NLS-1$
+    public static final String ATTR_HOST = "host";                     //$NON_NLS-1$
     public static final String ATTR_PATH = "path";                     //$NON-NLS-1$
     public static final String ATTR_PATH_PREFIX = "pathPrefix";        //$NON-NLS-1$
     public static final String ATTR_PATH_PATTERN = "pathPattern";      //$NON-NLS-1$
@@ -851,6 +887,10 @@
     public static final String ATTR_VALUE = "value";                   //$NON-NLS-1$
     public static final String ATTR_QUANTITY = "quantity";             //$NON-NLS-1$
     public static final String ATTR_FORMAT = "format";                 //$NON-NLS-1$
+    public static final String ATTR_PREPROCESSING = "preprocessing";   //$NON-NLS-1$
+
+    // Attributes: Data-Binding
+    public static final String ATTR_ALIAS = "alias";                  //$NON-NLS-1$
 
     // Attributes: Layout
     public static final String ATTR_LAYOUT_RESOURCE_PREFIX = "layout_";//$NON-NLS-1$
@@ -971,6 +1011,12 @@
     // Attributes: Drawables
     public static final String ATTR_TILE_MODE = "tileMode";            //$NON-NLS-1$
 
+    // Attributes: CoordinatorLayout
+    public static final String ATTR_LAYOUT_ANCHOR = "layout_anchor";                //$NON-NLS-1$
+    public static final String ATTR_LAYOUT_ANCHOR_GRAVITY = "layout_anchorGravity"; //$NON-NLS-1$
+    public static final String ATTR_LAYOUT_BEHAVIOR = "layout_behavior";            //$NON-NLS-1$
+    public static final String ATTR_LAYOUT_KEYLINE = "layout_keyline";              //$NON-NLS-1$
+
     // Values: Manifest
     public static final String VALUE_SPLIT_ACTION_BAR_WHEN_NARROW = "splitActionBarWhenNarrow"; // NON-NLS-$1
 
@@ -984,7 +1030,6 @@
     public static final String VALUE_SELECTABLE_ITEM_BACKGROUND =
             "?android:attr/selectableItemBackground";                   //$NON-NLS-1$
 
-
     // Values: Resources
     public static final String VALUE_ID = "id";                        //$NON-NLS-1$
 
@@ -1020,6 +1065,7 @@
 
     public static final String RES_FOLDER = "res";                     //$NON-NLS-1$
     public static final String DOT_XML = ".xml";                       //$NON-NLS-1$
+    public static final String DOT_XSD = ".xsd";                       //$NON-NLS-1$
     public static final String DOT_GIF = ".gif";                       //$NON-NLS-1$
     public static final String DOT_JPG = ".jpg";                       //$NON-NLS-1$
     public static final String DOT_JPEG = ".jpeg";                     //$NON-NLS-1$
@@ -1134,6 +1180,7 @@
     // Resources
     public static final String PREFIX_RESOURCE_REF = "@";               //$NON-NLS-1$
     public static final String PREFIX_THEME_REF = "?";                  //$NON-NLS-1$
+    public static final String PREFIX_BINDING_EXPR = "@{";              //$NON-NLS-1$
     public static final String ANDROID_PREFIX = "@android:";            //$NON-NLS-1$
     public static final String ANDROID_THEME_PREFIX = "?android:";      //$NON-NLS-1$
     public static final String LAYOUT_RESOURCE_PREFIX = "@layout/";     //$NON-NLS-1$
@@ -1148,6 +1195,7 @@
 
     public static final String ANDROID_LAYOUT_RESOURCE_PREFIX = "@android:layout/"; //$NON-NLS-1$
     public static final String ANDROID_STYLE_RESOURCE_PREFIX = "@android:style/";   //$NON-NLS-1$
+    public static final String ANDROID_COLOR_RESOURCE_PREFIX = "@android:color/";   //$NON-NLS-1$
     public static final String ANDROID_NEW_ID_PREFIX = "@android:+id/";             //$NON-NLS-1$
     public static final String ANDROID_ID_PREFIX = "@android:id/";                  //$NON-NLS-1$
     public static final String ANDROID_DRAWABLE_PREFIX = "@android:drawable/";      //$NON-NLS-1$
@@ -1373,9 +1421,9 @@
 
     public static final String GRADLE_PLUGIN_NAME = "com.android.tools.build:gradle:";
     public static final String GRADLE_MINIMUM_VERSION = "2.2.1";
-    public static final String GRADLE_LATEST_VERSION = "2.2.1";
+    public static final String GRADLE_LATEST_VERSION = "2.4";
     public static final String GRADLE_PLUGIN_MINIMUM_VERSION = "1.0.0";
-    public static final String GRADLE_PLUGIN_RECOMMENDED_VERSION = "1.2.0";
+    public static final String GRADLE_PLUGIN_RECOMMENDED_VERSION = "1.3.0";
     public static final String GRADLE_PLUGIN_LATEST_VERSION = GRADLE_PLUGIN_RECOMMENDED_VERSION;
     public static final String MIN_BUILD_TOOLS_VERSION = "19.1.0";
     public static final String SUPPORT_LIB_ARTIFACT = "com.android.support:support-v4";
@@ -1388,4 +1436,11 @@
     public static final String TYPE_DEF_VALUE_ATTRIBUTE = "value";
     public static final String TYPE_DEF_FLAG_ATTRIBUTE = "flag";
     public static final String FN_ANNOTATIONS_ZIP = "annotations.zip";
+
+    // Data Binding MISC
+    public static final String DATA_BINDING_LIB_ARTIFACT = "com.android.databinding:library";
+    public static final String[] TAGS_DATA_BINDING = new String[]{TAG_VARIABLE,
+        TAG_IMPORT, TAG_LAYOUT, TAG_DATA};
+    public static final String[] ATTRS_DATA_BINDING = new String[]{ATTR_NAME,
+        ATTR_TYPE, ATTR_CLASS, ATTR_ALIAS};
 }
diff --git a/common/src/main/java/com/android/ide/common/blame/Message.java b/common/src/main/java/com/android/ide/common/blame/Message.java
new file mode 100644
index 0000000..08f109e
--- /dev/null
+++ b/common/src/main/java/com/android/ide/common/blame/Message.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.Immutable;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+import java.util.List;
+
+@Immutable
+public final class Message {
+
+    @NonNull
+    private final Kind mKind;
+
+    @NonNull
+    private final String mText;
+
+    @NonNull
+    private final List<SourceFilePosition> mSourceFilePositions;
+
+    @NonNull
+    private final String mRawMessage;
+
+    /**
+     * Create a new message, which has a {@link Kind}, a String which will be shown to the user and
+     * at least one {@link SourceFilePosition}.
+     *
+     * @param kind the message type.
+     * @param text the text of the message.
+     * @param sourceFilePosition the first source file position the message .
+     * @param sourceFilePositions any additional source file positions, may be empty.
+     */
+    public Message(@NonNull Kind kind,
+            @NonNull String text,
+            @NonNull SourceFilePosition sourceFilePosition,
+            @NonNull SourceFilePosition... sourceFilePositions) {
+        mKind = kind;
+        mText = text;
+        mRawMessage = text;
+        mSourceFilePositions = ImmutableList.<SourceFilePosition>builder()
+                .add(sourceFilePosition).add(sourceFilePositions).build();
+    }
+
+    /**
+     * Create a new message, which has a {@link Kind}, a String which will be shown to the user and
+     * at least one {@link SourceFilePosition}.
+     *
+     * It also has a rawMessage, to store the original string for cases when the message is
+     * constructed by parsing the output from another tool.
+     *
+     * @param kind the message kind.
+     * @param text a human-readable string explaining the issue.
+     * @param rawMessage the original text of the message, usually from an external tool.
+     * @param sourceFilePosition the first source file position.
+     * @param sourceFilePositions any additional source file positions, may be empty.
+     */
+    public Message(@NonNull Kind kind,
+            @NonNull String text,
+            @NonNull String rawMessage,
+            @NonNull SourceFilePosition sourceFilePosition,
+            @NonNull SourceFilePosition... sourceFilePositions) {
+        mKind = kind;
+        mText = text;
+        mRawMessage = rawMessage;
+        mSourceFilePositions = ImmutableList.<SourceFilePosition>builder()
+                .add(sourceFilePosition).add(sourceFilePositions).build();
+    }
+
+    public Message(@NonNull Kind kind,
+            @NonNull String text,
+            @NonNull String rawMessage,
+            @NonNull ImmutableList<SourceFilePosition> positions) {
+        mKind = kind;
+        mText = text;
+        mRawMessage = rawMessage;
+
+        if (positions.isEmpty()) {
+            mSourceFilePositions = ImmutableList.of(SourceFilePosition.UNKNOWN);
+        } else {
+            mSourceFilePositions = positions;
+        }
+    }
+
+    @NonNull
+    public Kind getKind() {
+        return mKind;
+    }
+
+    @NonNull
+    public String getText() {
+        return mText;
+    }
+
+    /**
+     * Returns a list of source positions. Will always contain at least one item.
+     */
+    @NonNull
+    public List<SourceFilePosition> getSourceFilePositions() {
+        return mSourceFilePositions;
+    }
+
+    @NonNull
+    public String getRawMessage() {
+        return mRawMessage;
+    }
+
+    @Nullable
+    public String getSourcePath() {
+        File file = mSourceFilePositions.get(0).getFile().getSourceFile();
+        if (file == null) {
+            return null;
+        }
+        return file.getAbsolutePath();
+    }
+
+    /**
+     * Returns a legacy 1-based line number.
+     */
+    @Deprecated
+    public int getLineNumber() {
+        return mSourceFilePositions.get(0).getPosition().getStartLine() + 1;
+    }
+
+    /**
+     * @return a legacy 1-based column number.
+     */
+    @Deprecated
+    public int getColumn() {
+        return mSourceFilePositions.get(0).getPosition().getStartColumn() + 1;
+    }
+
+    public enum Kind {
+        ERROR, WARNING, INFO, STATISTICS, UNKNOWN, SIMPLE;
+
+        public static Kind findIgnoringCase(String s, Kind defaultKind) {
+            for (Kind kind : values()) {
+                if (kind.toString().equalsIgnoreCase(s)) {
+                    return kind;
+                }
+            }
+            return defaultKind;
+        }
+
+        @Nullable
+        public static Kind findIgnoringCase(String s) {
+            return findIgnoringCase(s, null);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Message)) {
+            return false;
+        }
+        Message that = (Message) o;
+        return Objects.equal(mKind, that.mKind) &&
+                Objects.equal(mText, that.mText) &&
+                Objects.equal(mSourceFilePositions, that.mSourceFilePositions);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mKind, mText, mSourceFilePositions);
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("kind", mKind).add("text", mText).add("sources",
+                mSourceFilePositions).toString();
+    }
+}
diff --git a/common/src/main/java/com/android/ide/common/blame/SourceFile.java b/common/src/main/java/com/android/ide/common/blame/SourceFile.java
new file mode 100644
index 0000000..be1264b
--- /dev/null
+++ b/common/src/main/java/com/android/ide/common/blame/SourceFile.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.Immutable;
+import com.google.common.base.Objects;
+
+import java.io.File;
+
+/**
+ * Represents a source file.
+ */
+@Immutable
+public final class SourceFile {
+
+    public static final SourceFile UNKNOWN = new SourceFile();
+
+    @Nullable
+    private final File mSourceFile;
+
+    /**
+     * A human readable description
+     *
+     * Usually the file name is OK for the short output, but for the manifest merger,
+     * where all of the files will be named AndroidManifest.xml the variant name is more useful.
+     */
+    @Nullable
+    private final String mDescription;
+
+    @SuppressWarnings("NullableProblems")
+    public SourceFile(
+            @NonNull File sourceFile,
+            @NonNull String description) {
+        mSourceFile = sourceFile;
+        mDescription = description;
+    }
+
+    public SourceFile(
+            @SuppressWarnings("NullableProblems") @NonNull File sourceFile) {
+        mSourceFile = sourceFile;
+        mDescription = null;
+    }
+
+    public SourceFile(
+            @SuppressWarnings("NullableProblems") @NonNull String description) {
+        mSourceFile = null;
+        mDescription = description;
+    }
+
+    private SourceFile() {
+        mSourceFile = null;
+        mDescription = null;
+    }
+
+    @Nullable
+    public File getSourceFile() {
+        return mSourceFile;
+    }
+
+    @Nullable
+    public String getDescription() {
+        return mDescription;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SourceFile)) {
+            return false;
+        }
+        SourceFile other = (SourceFile) obj;
+
+        return Objects.equal(mDescription, other.mDescription) &&
+                Objects.equal(mSourceFile, other.mSourceFile);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mSourceFile, mDescription);
+    }
+
+    @Override
+    public String toString() {
+        return print(false /* shortFormat */);
+    }
+
+    public String print(boolean shortFormat) {
+        if (mSourceFile == null) {
+            if (mDescription == null) {
+                return "Unknown source file";
+            }
+            return mDescription;
+        }
+        String fileName = mSourceFile.getName();
+        String fileDisplayName = shortFormat ? fileName : mSourceFile.getAbsolutePath();
+        if (mDescription == null || mDescription.equals(fileName)) {
+            return fileDisplayName;
+        } else {
+            return String.format("[%1$s] %2$s", mDescription, fileDisplayName);
+        }
+    }
+
+}
diff --git a/common/src/main/java/com/android/ide/common/blame/SourceFilePosition.java b/common/src/main/java/com/android/ide/common/blame/SourceFilePosition.java
new file mode 100644
index 0000000..e5ce048
--- /dev/null
+++ b/common/src/main/java/com/android/ide/common/blame/SourceFilePosition.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.concurrency.Immutable;
+import com.google.common.base.Objects;
+
+import java.io.File;
+
+@Immutable
+public final class SourceFilePosition {
+
+    public static final com.android.ide.common.blame.SourceFilePosition UNKNOWN =
+            new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN);
+
+    @NonNull
+    private final SourceFile mSourceFile;
+
+    @NonNull
+    private final SourcePosition mSourcePosition;
+
+    public SourceFilePosition(@NonNull SourceFile sourceFile,
+            @NonNull SourcePosition sourcePosition) {
+        mSourceFile = sourceFile;
+        mSourcePosition = sourcePosition;
+    }
+
+    public SourceFilePosition(@NonNull File file,
+            @NonNull SourcePosition sourcePosition) {
+        this(new SourceFile(file), sourcePosition);
+    }
+
+    @NonNull
+    public SourcePosition getPosition() {
+        return mSourcePosition;
+    }
+
+    @NonNull
+    public SourceFile getFile() {
+        return mSourceFile;
+    }
+
+    @Override
+    public String toString() {
+        return print(false);
+    }
+
+    public String print(boolean shortFormat) {
+        if (mSourcePosition.equals(SourcePosition.UNKNOWN)) {
+            return mSourceFile.print(shortFormat);
+        } else {
+            return mSourceFile.print(shortFormat) + ':' + mSourcePosition.toString();
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mSourceFile, mSourcePosition);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SourceFilePosition)) {
+            return false;
+        }
+        SourceFilePosition other = (SourceFilePosition) obj;
+        return Objects.equal(mSourceFile, other.mSourceFile) &&
+                Objects.equal(mSourcePosition, other.mSourcePosition);
+    }
+}
diff --git a/common/src/main/java/com/android/ide/common/blame/SourcePosition.java b/common/src/main/java/com/android/ide/common/blame/SourcePosition.java
index 5790673..de62180 100644
--- a/common/src/main/java/com/android/ide/common/blame/SourcePosition.java
+++ b/common/src/main/java/com/android/ide/common/blame/SourcePosition.java
@@ -16,6 +16,9 @@
 
 package com.android.ide.common.blame;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.Immutable;
 import com.google.common.base.Objects;
 
 /**
@@ -23,25 +26,15 @@
  *
  * Positions that are unknown are represented by -1.
  */
-public class SourcePosition {
+@Immutable
+public final class SourcePosition {
 
     public static final SourcePosition UNKNOWN = new SourcePosition();
 
-    private final int mStartLine;
+    private final int mStartLine, mStartColumn, mStartOffset, mEndLine, mEndColumn, mEndOffset;
 
-    private final int mStartColumn;
-
-    private final int mStartOffset;
-
-    private final int mEndLine;
-
-    private final int mEndColumn;
-
-    private final int mEndOffset;
-
-    public SourcePosition(int startLine, int startColumn, int startOffset, int endLine,
-            int endColumn,
-            int endOffset) {
+    public SourcePosition(int startLine, int startColumn, int startOffset,
+            int endLine, int endColumn, int endOffset) {
         mStartLine = startLine;
         mStartColumn = startColumn;
         mStartOffset = startOffset;
@@ -83,25 +76,28 @@
      */
     @Override
     public String toString() {
-        StringBuilder sB = new StringBuilder();
-        sB.append(mStartLine);
+        if (mStartLine == -1) {
+            return "?";
+        }
+        StringBuilder sB = new StringBuilder(15);
+        sB.append(mStartLine + 1); // Humans think that the first line is line 1.
         if (mStartColumn != -1) {
             sB.append(':');
-            sB.append(mStartColumn);
+            sB.append(mStartColumn + 1);
         }
         if (mEndLine != -1) {
 
             if (mEndLine == mStartLine) {
                 if (mEndColumn != -1 && mEndColumn != mStartColumn) {
                     sB.append('-');
-                    sB.append(mEndColumn);
+                    sB.append(mEndColumn + 1);
                 }
             } else {
                 sB.append('-');
-                sB.append(mEndLine);
+                sB.append(mEndLine + 1);
                 if (mEndColumn != -1) {
                     sB.append(':');
-                    sB.append(mEndColumn);
+                    sB.append(mEndColumn + 1);
                 }
             }
         }
@@ -110,17 +106,20 @@
 
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof SourcePosition) {
-            SourcePosition other = (SourcePosition) obj;
-
-            return other.mStartLine == mStartLine &&
-                    other.mStartColumn == mStartColumn &&
-                    other.mStartOffset == mStartOffset &&
-                    other.mEndLine == mEndLine &&
-                    other.mEndColumn == mEndColumn &&
-                    other.mEndOffset == mEndOffset;
+        if (this == obj) {
+            return true;
         }
-        return false;
+        if (!(obj instanceof SourcePosition)) {
+            return false;
+        }
+        SourcePosition other = (SourcePosition) obj;
+
+        return other.mStartLine == mStartLine &&
+                other.mStartColumn == mStartColumn &&
+                other.mStartOffset == mStartOffset &&
+                other.mEndLine == mEndLine &&
+                other.mEndColumn == mEndColumn &&
+                other.mEndOffset == mEndOffset;
     }
 
     @Override
diff --git a/common/src/main/java/com/android/utils/FileUtils.java b/common/src/main/java/com/android/utils/FileUtils.java
index 52bd87d..897300a 100644
--- a/common/src/main/java/com/android/utils/FileUtils.java
+++ b/common/src/main/java/com/android/utils/FileUtils.java
@@ -16,34 +16,48 @@
 
 package com.android.utils;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.android.annotations.NonNull;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import com.google.common.hash.Hashing;
 import com.google.common.io.Files;
 
 import java.io.File;
 import java.io.IOException;
 
 public class FileUtils {
-    public static boolean deleteFolder(final File folder) {
+    public static void deleteFolder(final File folder) throws IOException {
         if (!folder.exists()) {
-            return true;
+            return;
         }
         File[] files = folder.listFiles();
-        if (files != null) {
+        if (files != null) { // i.e. is a directory.
             for (final File file : files) {
-                if (file.isDirectory()) {
-                    deleteFolder(file);
-                } else {
-                    file.delete();
-                }
+                deleteFolder(file);
             }
         }
-        return folder.delete();
+        if (!folder.delete()) {
+            throw new IOException(String.format("Could not delete folder %s", folder));
+        }
+    }
+
+    public static void emptyFolder(final File folder) throws IOException {
+        deleteFolder(folder);
+        if (!folder.mkdirs()) {
+            throw new IOException(String.format("Could not create empty folder %s", folder));
+        }
     }
 
     public static void copyFile(File from, File to) throws IOException {
         to = new File(to, from.getName());
         if (from.isDirectory()) {
             if (!to.exists()) {
-                to.mkdirs();
+                if (!to.mkdirs()) {
+                    throw new IOException(String.format("Could not create directory %s", to));
+                }
             }
 
             File[] children = from.listFiles();
@@ -56,4 +70,30 @@
             Files.copy(from, to);
         }
     }
+
+    public static File join(File dir, String... paths) {
+        return new File(dir, Joiner.on(File.separatorChar).join(paths));
+    }
+
+    public static String relativePath(@NonNull File file, @NonNull File dir) {
+        checkArgument(file.isFile(), "%s is not a file.", file.getPath());
+        checkArgument(dir.isDirectory(), "%s is not a directory.", dir.getPath());
+
+        return dir.toURI().relativize(file.toURI()).getPath();
+    }
+
+    public static String sha1(@NonNull File file) throws IOException {
+        return Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
+    }
+
+    public static String getNamesAsCommaSeparatedList(Iterable<File> files) {
+        return Joiner.on(", ").join(Iterables.transform(files, GET_NAME));
+    }
+
+    private static final Function<File, String> GET_NAME = new Function<File, String>() {
+        @Override
+        public String apply(File file) {
+            return file.getName();
+        }
+    };
 }
diff --git a/common/src/main/java/com/android/utils/HtmlBuilder.java b/common/src/main/java/com/android/utils/HtmlBuilder.java
index f799425..085d7e3 100644
--- a/common/src/main/java/com/android/utils/HtmlBuilder.java
+++ b/common/src/main/java/com/android/utils/HtmlBuilder.java
@@ -65,14 +65,14 @@
     }
 
     public HtmlBuilder newline() {
-        mStringBuilder.append("<BR/>\n");
+        mStringBuilder.append("<BR/>");
 
         return this;
     }
 
     public HtmlBuilder newlineIfNecessary() {
-        if (!SdkUtils.endsWith(mStringBuilder, "<BR/>\n")) {
-            mStringBuilder.append("<BR/>\n");
+        if (!SdkUtils.endsWith(mStringBuilder, "<BR/>")) {
+            mStringBuilder.append("<BR/>");
         }
 
         return this;
@@ -223,9 +223,9 @@
 
     public HtmlBuilder endList() {
         if (USE_DD_LISTS) {
-            mStringBuilder.append("\n</DL>");
+            mStringBuilder.append("</DL>");
         } else {
-            mStringBuilder.append("\n</UL>");
+            mStringBuilder.append("</UL>");
         }
 
         return this;
@@ -233,10 +233,10 @@
 
     public HtmlBuilder listItem() {
         if (USE_DD_LISTS) {
-            mStringBuilder.append("\n<DD>");
+            mStringBuilder.append("<DD>");
             mStringBuilder.append("-&NBSP;");
         } else {
-            mStringBuilder.append("\n<LI>");
+            mStringBuilder.append("<LI>");
         }
 
         return this;
diff --git a/common/src/main/java/com/android/utils/PositionXmlParser.java b/common/src/main/java/com/android/utils/PositionXmlParser.java
index f0b8767..86ce844 100644
--- a/common/src/main/java/com/android/utils/PositionXmlParser.java
+++ b/common/src/main/java/com/android/utils/PositionXmlParser.java
@@ -18,9 +18,9 @@
 
 import static com.android.SdkConstants.UTF_8;
 
-import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Comment;
@@ -40,7 +40,6 @@
 import java.io.InputStream;
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -69,12 +68,15 @@
             "http://xml.org/sax/features/xmlns-uris";            //$NON-NLS-1$
     /** See http://www.w3.org/TR/REC-xml/#NT-EncodingDecl */
     private static final Pattern ENCODING_PATTERN =
-            Pattern.compile("encoding=['\"](\\S*)['\"]");//$NON-NLS-1$
+            Pattern.compile("encoding=['\"](\\S*)['\"]");        //$NON-NLS-1$
+    private static final String LOAD_EXTERNAL_DTD =
+            "http://apache.org/xml/features/nonvalidating/load-external-dtd";; //$NON-NLS-1$
 
     /**
      * Parses the XML content from the given input stream.
      *
      * @param input the input stream containing the XML to be parsed
+     * @param checkDtd whether or not download the DTD and validate it
      * @return the corresponding document
      * @throws ParserConfigurationException if a SAX parser is not available
      * @throws SAXException if the document contains a parsing error
@@ -82,8 +84,8 @@
      *             happen since the input source is known to be constructed from
      *             a string.
      */
-    @Nullable
-    public Document parse(@NonNull InputStream input)
+    @NonNull
+    public static Document parse(@NonNull InputStream input, boolean checkDtd)
             throws ParserConfigurationException, SAXException, IOException {
         // Read in all the data
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -96,13 +98,32 @@
           out.write(buf, 0, r);
         }
         input.close();
-        return parse(out.toByteArray());
+        return parse(out.toByteArray(), checkDtd);
+    }
+
+    /**
+     * @see PositionXmlParser#parse(InputStream, boolean)
+     */
+    @NonNull
+    public static Document parse(@NonNull InputStream input)
+            throws IOException, SAXException, ParserConfigurationException {
+        return parse(input, true);
+    }
+
+    /**
+     * @see PositionXmlParser#parse(byte[], boolean)
+     */
+    @NonNull
+    public static Document parse(@NonNull byte[] data)
+            throws ParserConfigurationException, SAXException, IOException {
+        return parse(data, true);
     }
 
     /**
      * Parses the XML content from the given byte array
      *
      * @param data the raw XML data (with unknown encoding)
+     * @param checkDtd whether or not download the DTD and validate it
      * @return the corresponding document
      * @throws ParserConfigurationException if a SAX parser is not available
      * @throws SAXException if the document contains a parsing error
@@ -110,12 +131,12 @@
      *             happen since the input source is known to be constructed from
      *             a string.
      */
-    @Nullable
-    public Document parse(@NonNull byte[] data)
-            throws ParserConfigurationException, SAXException, IOException {
+    @NonNull
+    public static Document parse(@NonNull byte[] data, boolean checkDtd)
+      throws ParserConfigurationException, SAXException, IOException {
         String xml = getXmlString(data);
         xml = XmlUtils.stripBom(xml);
-        return parse(xml, new InputSource(new StringReader(xml)), true);
+        return parse(xml, new InputSource(new StringReader(xml)), true, checkDtd);
     }
 
     /**
@@ -130,21 +151,26 @@
      *             happen since the input source is known to be constructed from
      *             a string.
      */
-    @Nullable
-    public Document parse(@NonNull String xml)
+    @NonNull
+    public static Document parse(@NonNull String xml)
             throws ParserConfigurationException, SAXException, IOException {
         xml = XmlUtils.stripBom(xml);
-        return parse(xml, new InputSource(new StringReader(xml)), true);
+        return parse(xml, new InputSource(new StringReader(xml)), true, true);
     }
 
     @NonNull
-    private Document parse(@NonNull String xml, @NonNull InputSource input, boolean checkBom)
+    private static Document parse(@NonNull String xml, @NonNull InputSource input, boolean checkBom,
+                                  boolean checkDtd)
             throws ParserConfigurationException, SAXException, IOException {
         try {
             SAXParserFactory factory = SAXParserFactory.newInstance();
-            factory.setFeature(NAMESPACE_FEATURE, true);
-            factory.setFeature(NAMESPACE_PREFIX_FEATURE, true);
-            factory.setFeature(PROVIDE_XMLNS_URIS, true);
+            if (checkDtd) {
+                factory.setFeature(NAMESPACE_FEATURE, true);
+                factory.setFeature(NAMESPACE_PREFIX_FEATURE, true);
+                factory.setFeature(PROVIDE_XMLNS_URIS, true);
+            } else {
+                factory.setFeature(LOAD_EXTERNAL_DTD, false);
+            }
             SAXParser parser = factory.newSAXParser();
             DomBuilder handler = new DomBuilder(xml);
             XMLReader xmlReader = parser.getXMLReader();
@@ -160,7 +186,7 @@
                 // (see http://en.wikipedia.org/wiki/Byte_order_mark) so here we'll
                 // just skip those up to the XML prolog beginning character, <
                 xml = xml.replaceFirst("^([\\W]+)<","<");  //$NON-NLS-1$ //$NON-NLS-2$
-                return parse(xml, new InputSource(new StringReader(xml)), false);
+                return parse(xml, new InputSource(new StringReader(xml)), false, checkDtd);
             }
             throw e;
         }
@@ -313,8 +339,8 @@
      * @return the position, or null if the node type is not supported for
      *         position info
      */
-    @Nullable
-    public Position getPosition(@NonNull Node node) {
+    @NonNull
+    public static SourcePosition getPosition(@NonNull Node node) {
         return getPosition(node, -1, -1);
     }
 
@@ -332,8 +358,15 @@
      * @return the position, or null if the node type is not supported for
      *         position info
      */
+
+    @NonNull
+    public static SourcePosition getPosition(@NonNull Node node, int start, int end) {
+        Position p = getPositionHelper(node, start, end);
+        return p == null ? SourcePosition.UNKNOWN : p.toSourcePosition();
+    }
+
     @Nullable
-    public Position getPosition(@NonNull Node node, int start, int end) {
+    private static Position getPositionHelper(@NonNull Node node, int start, int end) {
         // Look up the position information stored while parsing for the given node.
         // Note however that we only store position information for elements (because
         // there is no SAX callback for individual attributes).
@@ -384,10 +417,10 @@
                         }
                     }
 
-                    Position attributePosition = createPosition(line, column, index);
+                    Position attributePosition = new Position(line, column, index);
                     // Also set end range for retrieval in getLocation
-                    attributePosition.setEnd(createPosition(line, column + matcher.end(1) - index,
-                            matcher.end(1)));
+                    attributePosition.setEnd(
+                            new Position(line, column + matcher.end(1) - index, matcher.end(1)));
                     return attributePosition;
                 } else {
                     // No regexp match either: just fall back to element position
@@ -465,15 +498,13 @@
                             column = newColumn;
                         }
 
-                        Position attributePosition = createPosition(line, column,
-                                offset + textIndex);
+                        Position attributePosition = new Position(line, column, offset + textIndex);
                         // Also set end range for retrieval in getLocation
                         if (end != -1) {
-                            attributePosition.setEnd(createPosition(line, column,
-                                    offset + end));
+                            attributePosition.setEnd(new Position(line, column, offset + end));
                         } else {
-                            attributePosition.setEnd(createPosition(line, column,
-                                    offset + textLength));
+                            attributePosition.setEnd(
+                                    new Position(line, column, offset + textLength));
                         }
                         return attributePosition;
                     } else if (c == '"') {
@@ -498,7 +529,7 @@
      * information is attached to the DOM nodes by setting user data with the
      * {@link #POS_KEY} key.
      */
-    private final class DomBuilder extends DefaultHandler2 {
+    private static final class DomBuilder extends DefaultHandler2 {
         private final String mXml;
         private final Document mDocument;
         private Locator mLocator;
@@ -640,7 +671,7 @@
                         }
                     }
 
-                    return createPosition(line, column, offset);
+                    return new Position(line, column, offset);
                 }
             }
             // we did not find it, approximate.
@@ -685,7 +716,7 @@
             }
             mCurrentColumn = column;
 
-            return createPosition(mCurrentLine, mCurrentColumn, mCurrentOffset);
+            return new Position(mCurrentLine, mCurrentColumn, mCurrentOffset);
         }
 
         @Override
@@ -703,50 +734,7 @@
         }
     }
 
-    /**
-     * Creates a position while constructing the DOM document. This method
-     * allows a subclass to create a custom implementation of the position
-     * class.
-     *
-     * @param line the line number for the position
-     * @param column the column number for the position
-     * @param offset the character offset
-     * @return a new position
-     */
-    @NonNull
-    protected Position createPosition(int line, int column, int offset) {
-        return new DefaultPosition(line, column, offset);
-    }
-
-    public interface Position {
-        /**
-         * Linked position: for a begin position this will point to the
-         * corresponding end position. For an end position this will be null.
-         *
-         * @return the end position, or null
-         */
-        @Nullable
-        public Position getEnd();
-
-        /**
-         * Linked position: for a begin position this will point to the
-         * corresponding end position. For an end position this will be null.
-         *
-         * @param end the end position
-         */
-        public void setEnd(@NonNull Position end);
-
-        /** @return the line number, 0-based */
-        public int getLine();
-
-        /** @return the offset number, 0-based */
-        public int getOffset();
-
-        /** @return the column number, 0-based, and -1 if the column number if not known */
-        public int getColumn();
-    }
-
-    protected static class DefaultPosition implements Position {
+    private static class Position {
         /** The line number (0-based where the first line is line 0) */
         private final int mLine;
         private final int mColumn;
@@ -760,35 +748,44 @@
          * @param column the 0-based column number, or -1 if unknown
          * @param offset the offset, or -1 if unknown
          */
-        public DefaultPosition(int line, int column, int offset) {
+        public Position(int line, int column, int offset) {
             this.mLine = line;
             this.mColumn = column;
             this.mOffset = offset;
         }
 
-        @Override
         public int getLine() {
             return mLine;
         }
 
-        @Override
         public int getOffset() {
             return mOffset;
         }
 
-        @Override
         public int getColumn() {
             return mColumn;
         }
 
-        @Override
         public Position getEnd() {
             return mEnd;
         }
 
-        @Override
         public void setEnd(@NonNull Position end) {
             mEnd = end;
         }
+
+        public SourcePosition toSourcePosition() {
+           int endLine = mLine, endColumn = mColumn, endOffset = mOffset;
+
+            if (mEnd != null) {
+                endLine = mEnd.getLine();
+                endColumn = mEnd.getColumn();
+                endOffset = mEnd.getOffset();
+            }
+
+            return new SourcePosition(mLine, mColumn, mOffset, endLine, endColumn, endOffset);
+        }
     }
+
+    private PositionXmlParser() { }
 }
diff --git a/common/src/main/java/com/android/utils/SdkUtils.java b/common/src/main/java/com/android/utils/SdkUtils.java
index 51b0464..68e7354 100644
--- a/common/src/main/java/com/android/utils/SdkUtils.java
+++ b/common/src/main/java/com/android/utils/SdkUtils.java
@@ -18,10 +18,9 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
 import com.google.common.base.CaseFormat;
 import com.google.common.base.Charsets;
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closeables;
 
@@ -37,6 +36,7 @@
 import java.text.ParseException;
 import java.util.List;
 
+import static com.android.SdkConstants.DOT_WEBP;
 import static com.android.SdkConstants.DOT_XML;
 import static com.android.SdkConstants.DOT_PNG;
 import static com.android.SdkConstants.DOT_GIF;
@@ -489,8 +489,8 @@
         return resourceName;
     }
 
-    public static final List<String> IMAGE_EXTENSIONS = Lists.newArrayList(
-            DOT_PNG, DOT_9PNG, DOT_GIF, DOT_JPEG, DOT_JPG, DOT_BMP);
+    public static final List<String> IMAGE_EXTENSIONS = ImmutableList.of(
+            DOT_PNG, DOT_9PNG, DOT_GIF, DOT_JPEG, DOT_JPG, DOT_BMP, DOT_WEBP);
 
     /**
      * Returns true if the given file path points to an image file recognized by
diff --git a/common/src/main/java/com/android/utils/XmlUtils.java b/common/src/main/java/com/android/utils/XmlUtils.java
index d8374de..1159c89 100644
--- a/common/src/main/java/com/android/utils/XmlUtils.java
+++ b/common/src/main/java/com/android/utils/XmlUtils.java
@@ -33,8 +33,6 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Charsets;
 import com.google.common.io.Files;
 
 import org.w3c.dom.Attr;
@@ -48,14 +46,10 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
-import java.nio.charset.Charset;
 import java.util.HashSet;
 import java.util.Locale;
 
@@ -493,7 +487,7 @@
      * To perform pretty printing, use {@code XmlPrettyPrinter.prettyPrint(node)} in
      * {@code sdk-common}.
      */
-    public static String toXml(Node node, boolean preserveWhitespace) {
+    public static String toXml(Node node) {
         StringBuilder sb = new StringBuilder(1000);
         append(sb, node, 0);
         return sb.toString();
diff --git a/common/src/main/java/com/android/xml/AndroidManifest.java b/common/src/main/java/com/android/xml/AndroidManifest.java
index b53566b..b06daa1 100644
--- a/common/src/main/java/com/android/xml/AndroidManifest.java
+++ b/common/src/main/java/com/android/xml/AndroidManifest.java
@@ -70,6 +70,8 @@
     public static final String ATTRIBUTE_PACKAGE = "package";
     public static final String ATTRIBUTE_VERSIONCODE = "versionCode";
     public static final String ATTRIBUTE_NAME = "name";
+    public static final String ATTRIBUTE_MIME_TYPE = "mimeType";
+    public static final String ATTRIBUTE_PORT = "port";
     public static final String ATTRIBUTE_REQUIRED = "required";
     public static final String ATTRIBUTE_GLESVERSION = "glEsVersion";
     public static final String ATTRIBUTE_PROCESS = "process";
diff --git a/common/src/test/java/com/android/utils/HtmlBuilderTest.java b/common/src/test/java/com/android/utils/HtmlBuilderTest.java
index f04f3c3..82701df 100644
--- a/common/src/test/java/com/android/utils/HtmlBuilderTest.java
+++ b/common/src/test/java/com/android/utils/HtmlBuilderTest.java
@@ -59,10 +59,10 @@
         builder.add("Plain").newline();
         builder.beginList().listItem().add("item 1").listItem().add("item 2").endList();
 
-        assertEquals("Plain<BR/>\n" +
-                     "<DL>\n" +
-                     "<DD>-&NBSP;item 1\n" +
-                     "<DD>-&NBSP;item 2\n" +
+        assertEquals("Plain<BR/>" +
+                     "<DL>" +
+                     "<DD>-&NBSP;item 1" +
+                     "<DD>-&NBSP;item 2" +
                      "</DL>", builder.getHtml());
     }
 
@@ -124,16 +124,16 @@
     public void testNewlineIfNecessary() {
         HtmlBuilder builder = new HtmlBuilder();
         builder.newlineIfNecessary();
-        assertEquals("<BR/>\n", builder.getHtml());
+        assertEquals("<BR/>", builder.getHtml());
         builder.newlineIfNecessary();
-        assertEquals("<BR/>\n", builder.getHtml());
+        assertEquals("<BR/>", builder.getHtml());
         builder.add("a");
         builder.newlineIfNecessary();
-        assertEquals("<BR/>\na<BR/>\n", builder.getHtml());
+        assertEquals("<BR/>a<BR/>", builder.getHtml());
         builder.newline();
         builder.newlineIfNecessary();
         builder.newlineIfNecessary();
         builder.newlineIfNecessary();
-        assertEquals("<BR/>\na<BR/>\n<BR/>\n", builder.getHtml());
+        assertEquals("<BR/>a<BR/><BR/>", builder.getHtml());
     }
 }
diff --git a/common/src/test/java/com/android/utils/PositionXmlParserTest.java b/common/src/test/java/com/android/utils/PositionXmlParserTest.java
index 9c1130f..859cf36 100644
--- a/common/src/test/java/com/android/utils/PositionXmlParserTest.java
+++ b/common/src/test/java/com/android/utils/PositionXmlParserTest.java
@@ -16,7 +16,7 @@
 
 package com.android.utils;
 
-import com.android.utils.PositionXmlParser.Position;
+import com.android.ide.common.blame.SourcePosition;
 
 import junit.framework.TestCase;
 
@@ -36,7 +36,7 @@
 import java.io.OutputStreamWriter;
 import java.io.Writer;
 
-@SuppressWarnings("javadoc")
+@SuppressWarnings({"javadoc", "IOResourceOpenedButNotSafelyClosed"})
 public class PositionXmlParserTest extends TestCase {
     public void test() throws Exception {
         String xml =
@@ -59,12 +59,11 @@
                 "        android:text=\"Button\" />\n" +
                 "\n" +
                 "</LinearLayout>\n";
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest", ".xml");
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(xml);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
 
         // Basic parsing heart beat tests
@@ -79,40 +78,36 @@
         // Check attribute positions
         Attr attr = linearLayout.getAttributeNodeNS(ANDROID_URI, "layout_width");
         assertNotNull(attr);
-        Position start = parser.getPosition(attr);
-        Position end = start.getEnd();
-        assertEquals(2, start.getLine());
-        assertEquals(4, start.getColumn());
-        assertEquals(xml.indexOf("android:layout_width"), start.getOffset());
-        assertEquals(2, end.getLine());
+        SourcePosition position = PositionXmlParser.getPosition(attr);
+        assertEquals(2, position.getStartLine());
+        assertEquals(4, position.getStartColumn());
+        assertEquals(xml.indexOf("android:layout_width"), position.getStartOffset());
+        assertEquals(2, position.getEndLine());
         String target = "android:layout_width=\"match_parent\"";
-        assertEquals(xml.indexOf(target) + target.length(), end.getOffset());
+        assertEquals(xml.indexOf(target) + target.length(), position.getEndOffset());
 
         // Check element positions
         Element button = (Element) buttons.item(0);
-        start = parser.getPosition(button);
-        end = start.getEnd();
-        assertNull(end.getEnd());
-        assertEquals(6, start.getLine());
-        assertEquals(4, start.getColumn());
-        assertEquals(xml.indexOf("<Button"), start.getOffset());
-        assertEquals(xml.indexOf("/>") + 2, end.getOffset());
-        assertEquals(10, end.getLine());
-        int button1End = end.getOffset();
+        position = PositionXmlParser.getPosition(button);
+        assertEquals(6, position.getStartLine());
+        assertEquals(4, position.getStartColumn());
+        assertEquals(xml.indexOf("<Button"), position.getStartOffset());
+        assertEquals(xml.indexOf("/>") + 2, position.getEndOffset());
+        assertEquals(10, position.getEndLine());
+        int button1End = position.getEndOffset();
 
         Element button2 = (Element) buttons.item(1);
-        start = parser.getPosition(button2);
-        end = start.getEnd();
-        assertEquals(12, start.getLine());
-        assertEquals(4, start.getColumn());
-        assertEquals(xml.indexOf("<Button", button1End), start.getOffset());
-        assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset());
-        assertEquals(16, end.getLine());
+        position = PositionXmlParser.getPosition(button2);
+        assertEquals(12, position.getStartLine());
+        assertEquals(4, position.getStartColumn());
+        assertEquals(xml.indexOf("<Button", button1End), position.getStartOffset());
+        assertEquals(xml.indexOf("/>", position.getStartOffset()) + 2, position.getEndOffset());
+        assertEquals(16, position.getEndLine());
 
         file.delete();
     }
 
-    public void testText() throws Exception {
+    public static void testText() throws Exception {
         String xml =
                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                 "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
@@ -128,13 +123,12 @@
                 "          some text\n" +
                 "\n" +
                 "</LinearLayout>\n";
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest", ".xml");
         file.deleteOnExit();
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(xml);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
 
         // Basic parsing heart beat tests
@@ -152,16 +146,16 @@
         assertNotNull(text);
 
         // Check attribute positions
-        Position start = parser.getPosition(text);
-        assertEquals(11, start.getLine());
-        assertEquals(10, start.getColumn());
-        assertEquals(xml.indexOf("some text"), start.getOffset());
+        SourcePosition start = PositionXmlParser.getPosition(text);
+        assertEquals(11, start.getStartLine());
+        assertEquals(10, start.getStartColumn());
+        assertEquals(xml.indexOf("some text"), start.getStartOffset());
 
         // Check attribute positions with text node offsets
-        start = parser.getPosition(text, 13, 15);
-        assertEquals(11, start.getLine());
-        assertEquals(12, start.getColumn());
-        assertEquals(xml.indexOf("me"), start.getOffset());
+        start = PositionXmlParser.getPosition(text, 13, 15);
+        assertEquals(11, start.getStartLine());
+        assertEquals(12, start.getStartColumn());
+        assertEquals(xml.indexOf("me"), start.getStartOffset());
     }
 
     public void testLineEndings() throws Exception {
@@ -172,12 +166,11 @@
                 "\r" +
                 "<LinearLayout></LinearLayout>\r\n" +
                 "</LinearLayout>\r\n";
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest2", ".xml");
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(xml);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
 
         file.delete();
@@ -211,7 +204,6 @@
                 lineEnding +
                 "<bar></bar>" + lineEnding +
                 "</foo>" + lineEnding);
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest" + encoding + writeBom + writeEncoding,
                 ".xml");
         BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
@@ -247,18 +239,18 @@
         writer.write(sb.toString());
         writer.close();
 
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
         Element root = document.getDocumentElement();
         assertEquals(file.getPath(), value, root.getAttribute("attr"));
-        assertEquals(4, parser.getPosition(root).getLine());
+        assertEquals(4, PositionXmlParser.getPosition(root).getStartLine());
 
         Attr attribute = root.getAttributeNode("attr");
         assertNotNull(attribute);
-        Position position = parser.getPosition(attribute);
+        SourcePosition position = PositionXmlParser.getPosition(attribute);
         assertNotNull(position);
-        assertEquals(4, position.getLine());
-        assertEquals(startAttrOffset, position.getOffset());
+        assertEquals(4, position.getStartLine());
+        assertEquals(startAttrOffset, position.getStartOffset());
 
         file.delete();
     }
@@ -338,13 +330,12 @@
                         "          some text\n" +
                         "\n" +
                         "</LinearLayout>\n";
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest", ".xml");
         file.deleteOnExit();
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(xml);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
 
         // Basic parsing heart beat tests
@@ -354,18 +345,18 @@
         // first child is a comment.
         org.w3c.dom.Node commentNode = linearLayout.getFirstChild().getNextSibling();
         assertEquals(Node.COMMENT_NODE, commentNode.getNodeType());
-        Position position = parser.getPosition(commentNode);
+        SourcePosition position = PositionXmlParser.getPosition(commentNode);
         assertNotNull(position);
-        assertEquals(6, position.getLine());
-        assertEquals(4, position.getColumn());
-        assertEquals(xml.indexOf("<!--"), position.getOffset());
+        assertEquals(6, position.getStartLine());
+        assertEquals(4, position.getStartColumn());
+        assertEquals(xml.indexOf("<!--"), position.getStartOffset());
 
         // ensure that the next siblings' position start at the right location.
         Element button = (Element) document.getElementsByTagName("Button").item(0);
-        Position buttonPosition = parser.getPosition(button);
+        SourcePosition buttonPosition = PositionXmlParser.getPosition(button);
         assertNotNull(buttonPosition);
-        assertEquals(7, buttonPosition.getLine());
-        assertEquals(4, buttonPosition.getColumn());
+        assertEquals(7, buttonPosition.getStartLine());
+        assertEquals(4, buttonPosition.getStartColumn());
     }
 
     public void testMultipleLineComment() throws Exception {
@@ -387,13 +378,12 @@
                         "          some text\n" +
                         "\n" +
                         "</LinearLayout>\n";
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest", ".xml");
         file.deleteOnExit();
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(xml);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
 
         // Basic parsing heart beat tests
@@ -403,18 +393,18 @@
         // first child is a comment.
         Node commentNode = linearLayout.getFirstChild().getNextSibling();
         assertEquals(Node.COMMENT_NODE, commentNode.getNodeType());
-        Position position = parser.getPosition(commentNode);
+        SourcePosition position = PositionXmlParser.getPosition(commentNode);
         assertNotNull(position);
-        assertEquals(6, position.getLine());
-        assertEquals(4, position.getColumn());
+        assertEquals(6, position.getStartLine());
+        assertEquals(4, position.getStartColumn());
 
 
         // ensure that the next siblings' position start at the right location.
         Element button = (Element) document.getElementsByTagName("Button").item(0);
-        Position buttonPosition = parser.getPosition(button);
+        SourcePosition buttonPosition = PositionXmlParser.getPosition(button);
         assertNotNull(buttonPosition);
-        assertEquals(9, buttonPosition.getLine());
-        assertEquals(4, buttonPosition.getColumn());
+        assertEquals(9, buttonPosition.getStartLine());
+        assertEquals(4, buttonPosition.getStartColumn());
     }
 
     public void testAttributeWithoutNamespace() throws Exception {
@@ -444,13 +434,12 @@
                 + "</LinearLayout>\n"
                 + "\n";
 
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest", ".xml");
         file.deleteOnExit();
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(xml);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
 
         // Basic parsing heart beat tests
@@ -463,33 +452,26 @@
         for (int i = 0, n = buttons.getLength(); i < n; i++) {
             Element button = (Element)buttons.item(i);
             Attr attr;
-            Position start;
-            Position end;
+            SourcePosition position;
 
             attr = button.getAttributeNode("orientation");
-            start = parser.getPosition(attr);
-            assertNotNull(start);
-            end = start.getEnd();
-            assertNotNull(end);
+            position = PositionXmlParser.getPosition(attr);
+            assertNotNull(position);
             assertEquals(" orientation=\"true\"",
-                    xml.substring(start.getOffset() - 1, end.getOffset()));
+                    xml.substring(position.getStartOffset() - 1, position.getEndOffset()));
 
             attr = button.getAttributeNodeNS("http://schemas.android.com/apk/res/android",
                     "orientation");
-            start = parser.getPosition(attr);
-            assertNotNull(start);
-            end = start.getEnd();
-            assertNotNull(end);
+            position = PositionXmlParser.getPosition(attr);
+            assertNotNull(position);
             assertEquals("android:orientation=\"vertical\"",
-                    xml.substring(start.getOffset(), end.getOffset()));
+                    xml.substring(position.getStartOffset(), position.getEndOffset()));
 
             attr = button.getAttributeNodeNS("http://foo.bar", "orientation");
-            start = parser.getPosition(attr);
-            assertNotNull(start);
-            end = start.getEnd();
-            assertNotNull(end);
+            position = PositionXmlParser.getPosition(attr);
+            assertNotNull(position);
             assertEquals("other:orientation=\"vertical\"",
-                    xml.substring(start.getOffset(), end.getOffset()));
+                    xml.substring(position.getStartOffset(), position.getEndOffset()));
         }
     }
 
@@ -503,12 +485,11 @@
                         "    <ns:SubTag\n" +
                         "        ns:text=\"Button\" />\n" +
                         "</ns:SomeTag>\n";
-        PositionXmlParser parser = new PositionXmlParser();
         File file = File.createTempFile("parsertest", ".xml");
         Writer fw = new BufferedWriter(new FileWriter(file));
         fw.write(XML);
         fw.close();
-        Document document = parser.parse(new FileInputStream(file));
+        Document document = PositionXmlParser.parse(new FileInputStream(file));
         assertNotNull(document);
         Element e = document.getDocumentElement();
         assertNotNull(e);
@@ -526,11 +507,10 @@
         assertEquals("SubTag", subTag.getLocalName());
         Attr attr = subTag.getAttributeNodeNS(NAMESPACE_URL, "text");
         assertNotNull(attr);
-        Position start = parser.getPosition(attr);
-        assertNotNull(start);
-        Position end = start.getEnd();
-        assertNotNull(end);
-        assertEquals("ns:text=\"Button\"", XML.substring(start.getOffset(), end.getOffset()));
+        SourcePosition position = PositionXmlParser.getPosition(attr);
+        assertNotNull(position);
+        assertEquals("ns:text=\"Button\"",
+                XML.substring(position.getStartOffset(), position.getEndOffset()));
         assertEquals("Button", subTag.getAttributeNS(NAMESPACE_URL, "text"));
         assertEquals(NAMESPACE_URL, subTag.getNamespaceURI());
     }
diff --git a/common/src/test/java/com/android/utils/SdkUtilsTest.java b/common/src/test/java/com/android/utils/SdkUtilsTest.java
index adb6c9b..53e01e2 100755
--- a/common/src/test/java/com/android/utils/SdkUtilsTest.java
+++ b/common/src/test/java/com/android/utils/SdkUtilsTest.java
@@ -38,6 +38,15 @@
 
 @SuppressWarnings("javadoc")
 public class SdkUtilsTest extends TestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        // TODO: Use Files.createTempDir() to avoid this.
+        if (new File("/tmp/foo").isDirectory()) {
+            fail("This test will fail if /tmp/foo exists and is a directory. Please remove it.");
+        }
+    }
+
     public void testEndsWithIgnoreCase() {
         assertTrue(SdkUtils.endsWithIgnoreCase("foo", "foo"));
         assertTrue(SdkUtils.endsWithIgnoreCase("foo", "Foo"));
@@ -319,7 +328,7 @@
         Element root = document.getDocumentElement();
         assertNotNull(root);
         root.appendChild(document.createComment(comment));
-        String xml = XmlUtils.toXml(document, false);
+        String xml = XmlUtils.toXml(document);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<root>"
diff --git a/common/src/test/java/com/android/utils/XmlUtilsTest.java b/common/src/test/java/com/android/utils/XmlUtilsTest.java
index 3f97a51..5d3073c 100644
--- a/common/src/test/java/com/android/utils/XmlUtilsTest.java
+++ b/common/src/test/java/com/android/utils/XmlUtilsTest.java
@@ -176,7 +176,7 @@
         Node text = doc.createTextNode("  This is my text  ");
         child3.appendChild(text);
 
-        String xml = XmlUtils.toXml(doc, true);
+        String xml = XmlUtils.toXml(doc);
         assertEquals(
                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                         "<myroot baz=\"baz\" foo=\"bar\"><mychild/><hasComment><!--This is my comment--></hasComment><hasText>  This is my text  </hasText></myroot>",
@@ -199,7 +199,7 @@
 
         Document doc = parse(xml);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<resources>\n"
@@ -221,7 +221,7 @@
                 + "</root>";
         Document doc = parse(xml);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(xml, formatted);
     }
 
@@ -236,7 +236,7 @@
                 + "</resources>";
         Document doc = parse(xml);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<resources>\n"
@@ -258,7 +258,7 @@
                 + "<root/>";
         Document doc = parse(xml);
 
-        xml = XmlUtils.toXml(doc, true);
+        xml = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<!-- ============== --><!-- Generic styles --><!-- ============== --><root/>",
@@ -273,7 +273,7 @@
                 + "</root>";
         Document doc = parse(xml);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(xml, formatted);
     }
 
@@ -293,7 +293,7 @@
 
         Document doc = parse(xml);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<resources>\n"
diff --git a/ddmlib/build.gradle b/ddmlib/build.gradle
index 824196f..fd300c1 100644
--- a/ddmlib/build.gradle
+++ b/ddmlib/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools.ddms'
diff --git a/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java b/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java
index d49544e..b390f93 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java
@@ -191,11 +191,9 @@
     /**
      * Create an ASCII string preceded by four hex digits. The opening "####"
      * is the length of the rest of the string, encoded as ASCII hex (case
-     * doesn't matter). "port" and "host" are what we want to forward to. If
-     * we're on the host side connecting into the device, "addrStr" should be
-     * null.
+     * doesn't matter).
      */
-    static byte[] formAdbRequest(String req) {
+    public static byte[] formAdbRequest(String req) {
         String resultStr = String.format("%04X%s", req.length(), req); //$NON-NLS-1$
         byte[] result;
         try {
diff --git a/ddmlib/src/main/java/com/android/ddmlib/AdbVersion.java b/ddmlib/src/main/java/com/android/ddmlib/AdbVersion.java
new file mode 100644
index 0000000..92de6ee
--- /dev/null
+++ b/ddmlib/src/main/java/com/android/ddmlib/AdbVersion.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 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.ddmlib;
+
+import com.android.annotations.NonNull;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AdbVersion implements Comparable<AdbVersion> {
+    public static final AdbVersion UNKNOWN = new AdbVersion(-1, -1, -1);
+
+    /** Matches e.g. ".... 1.0.32" */
+    private static final Pattern ADB_VERSION_PATTERN = Pattern.compile(
+            "^.*(\\d+)\\.(\\d+)\\.(\\d+).*");
+
+    public final int major;
+    public final int minor;
+    public final int micro;
+
+    private AdbVersion(int major, int minor, int micro) {
+        this.major = major;
+        this.minor = minor;
+        this.micro = micro;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US, "%1$d.%2$d.%3$d", major, minor, micro);
+    }
+
+    @Override
+    public int compareTo(AdbVersion o) {
+        if (major != o.major) {
+            return major - o.major;
+        }
+
+        if (minor != o.minor) {
+            return minor - o.minor;
+        }
+
+        return micro - o.micro;
+    }
+
+    @NonNull
+    public static AdbVersion parseFrom(@NonNull String input) {
+        Matcher matcher = ADB_VERSION_PATTERN.matcher(input);
+        if (matcher.matches()) {
+            int major = Integer.parseInt(matcher.group(1));
+            int minor = Integer.parseInt(matcher.group(2));
+            int micro = Integer.parseInt(matcher.group(3));
+            return new AdbVersion(major, minor, micro);
+        } else {
+            return UNKNOWN;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        AdbVersion version = (AdbVersion) o;
+
+        if (major != version.major) {
+            return false;
+        }
+        if (minor != version.minor) {
+            return false;
+        }
+        return micro == version.micro;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = major;
+        result = 31 * result + minor;
+        result = 31 * result + micro;
+        return result;
+    }
+}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java b/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java
index ebbcd90..13dec70 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java
@@ -19,6 +19,9 @@
 import com.android.annotations.NonNull;
 import com.android.ddmlib.Log.LogLevel;
 import com.google.common.base.Joiner;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -32,8 +35,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A connection to the host-side android debug bridge (adb)
@@ -47,12 +50,7 @@
      * Minimum and maximum version of adb supported. This correspond to
      * ADB_SERVER_VERSION found in //device/tools/adb/adb.h
      */
-
-    private static final int ADB_VERSION_MICRO_MIN = 20;
-    private static final int ADB_VERSION_MICRO_MAX = -1;
-
-    private static final Pattern sAdbVersion = Pattern.compile(
-            "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"); //$NON-NLS-1$
+    private static final AdbVersion MIN_ADB_VERSION = AdbVersion.parseFrom("1.0.20");
 
     private static final String ADB = "adb"; //$NON-NLS-1$
     private static final String DDMS = "ddms"; //$NON-NLS-1$
@@ -480,6 +478,7 @@
      * Returns the devices.
      * @see #hasInitialDeviceList()
      */
+    @NonNull
     public IDevice[] getDevices() {
         synchronized (sLock) {
             if (mDeviceMonitor != null) {
@@ -562,7 +561,11 @@
         }
         mAdbOsLocation = osLocation;
 
-        checkAdbVersion();
+        try {
+            checkAdbVersion();
+        } catch (IOException e) {
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -572,10 +575,9 @@
     }
 
     /**
-     * Queries adb for its version number and checks it against {@link #ADB_VERSION_MICRO_MIN} and
-     * {@link #ADB_VERSION_MICRO_MAX}
+     * Queries adb for its version number and checks that it is atleast {@link #MIN_ADB_VERSION}.
      */
-    private void checkAdbVersion() {
+    private void checkAdbVersion() throws IOException {
         // default is bad check
         mVersionCheck = false;
 
@@ -583,127 +585,78 @@
             return;
         }
 
-        String[] command = new String[2];
-        command[0] = mAdbOsLocation;
-        command[1] = "version"; //$NON-NLS-1$
-        Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation));
-        Process process = null;
+        File adb = new File(mAdbOsLocation);
+        ListenableFuture<AdbVersion> future = getAdbVersion(adb);
+        AdbVersion version;
         try {
-            process = Runtime.getRuntime().exec(command);
-        } catch (IOException e) {
-            boolean exists = new File(mAdbOsLocation).exists();
-            String msg;
-            if (exists) {
-                msg = String.format(
-                        "Unexpected exception '%1$s' while attempting to get adb version from '%2$s'",
-                        e.getMessage(), mAdbOsLocation);
-            } else {
-                msg = "Unable to locate adb: '" + mAdbOsLocation + "'.\n" +
-                      "Please use SDK Manager and check if Android SDK platform-tools are installed.";
-            }
-            Log.logAndDisplay(LogLevel.ERROR, ADB, msg);
-            return;
-        }
-
-        ArrayList<String> errorOutput = new ArrayList<String>();
-        ArrayList<String> stdOutput = new ArrayList<String>();
-        int status;
-        try {
-            status = grabProcessOutput(process, errorOutput, stdOutput,
-                    true /* waitForReaders */);
+            version = future.get(5, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
             return;
+        } catch (java.util.concurrent.TimeoutException e) {
+            String msg = "Unable to obtain result of 'adb version'";
+            Log.logAndDisplay(LogLevel.ERROR, ADB, msg);
+            return;
+        } catch (ExecutionException e) {
+            Log.logAndDisplay(LogLevel.ERROR, ADB, e.getCause().getMessage());
+            Throwables.propagateIfInstanceOf(e.getCause(), IOException.class);
+            return;
         }
 
-        if (status != 0) {
-            StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$
-            for (String error : errorOutput) {
-                builder.append('\n');
-                builder.append(error);
-            }
-            Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString());
-        }
-
-        // check both stdout and stderr
-        boolean versionFound = false;
-        for (String line : stdOutput) {
-            versionFound = scanVersionLine(line);
-            if (versionFound) {
-                break;
-            }
-        }
-        if (!versionFound) {
-            for (String line : errorOutput) {
-                versionFound = scanVersionLine(line);
-                if (versionFound) {
-                    break;
-                }
-            }
-        }
-
-        if (!versionFound) {
-            // if we get here, we failed to parse the output.
-            StringBuilder builder = new StringBuilder(
-                    "Failed to parse the output of 'adb version':\n"); //$NON-NLS-1$
-            builder.append("Standard Output was:\n"); //$NON-NLS-1$
-            for (String line : stdOutput) {
-                builder.append(line);
-                builder.append('\n');
-            }
-            builder.append("\nError Output was:\n"); //$NON-NLS-1$
-            for (String line : errorOutput) {
-                builder.append(line);
-                builder.append('\n');
-            }
-            Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString());
+        if (version.compareTo(MIN_ADB_VERSION) > 0) {
+            mVersionCheck = true;
+        } else {
+            String message = String.format(
+                    "Required minimum version of adb: %1$s."
+                            + "Current version is %2$s", MIN_ADB_VERSION, version);
+            Log.logAndDisplay(LogLevel.ERROR, ADB, message);
         }
     }
 
-    /**
-     * Scans a line resulting from 'adb version' for a potential version number.
-     * <p/>
-     * If a version number is found, it checks the version number against what is expected
-     * by this version of ddms.
-     * <p/>
-     * Returns true when a version number has been found so that we can stop scanning,
-     * whether the version number is in the acceptable range or not.
-     *
-     * @param line The line to scan.
-     * @return True if a version number was found (whether it is acceptable or not).
-     */
-    @SuppressWarnings("all") // With Eclipse 3.6, replace by @SuppressWarnings("unused")
-    private boolean scanVersionLine(String line) {
-        if (line != null) {
-            Matcher matcher = sAdbVersion.matcher(line);
-            if (matcher.matches()) {
-                int majorVersion = Integer.parseInt(matcher.group(1));
-                int minorVersion = Integer.parseInt(matcher.group(2));
-                int microVersion = Integer.parseInt(matcher.group(3));
+    public static ListenableFuture<AdbVersion> getAdbVersion(@NonNull final File adb) {
+        final SettableFuture<AdbVersion> future = SettableFuture.create();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                ProcessBuilder pb = new ProcessBuilder(adb.getPath(), "version");
+                pb.redirectErrorStream(true);
 
-                // check only the micro version for now.
-                if (microVersion < ADB_VERSION_MICRO_MIN) {
-                    String message = String.format(
-                            "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
-                            + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
-                            majorVersion, minorVersion, ADB_VERSION_MICRO_MIN,
-                            microVersion);
-                    Log.logAndDisplay(LogLevel.ERROR, ADB, message);
-                } else if (ADB_VERSION_MICRO_MAX != -1 &&
-                        microVersion > ADB_VERSION_MICRO_MAX) {
-                    String message = String.format(
-                            "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
-                            + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
-                            majorVersion, minorVersion, ADB_VERSION_MICRO_MAX,
-                            microVersion);
-                    Log.logAndDisplay(LogLevel.ERROR, ADB, message);
-                } else {
-                    mVersionCheck = true;
+                Process p = null;
+                try {
+                    p = pb.start();
+                } catch (IOException e) {
+                    future.setException(e);
+                    return;
                 }
 
-                return true;
+                StringBuilder sb = new StringBuilder();
+                BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                try {
+                    String line;
+                    while ((line = br.readLine()) != null) {
+                        AdbVersion version = AdbVersion.parseFrom(line);
+                        if (version != AdbVersion.UNKNOWN) {
+                            future.set(version);
+                            return;
+                        }
+                        sb.append(line);
+                        sb.append('\n');
+                    }
+                } catch (IOException e) {
+                    future.setException(e);
+                    return;
+                } finally {
+                    try {
+                        br.close();
+                    } catch (IOException e) {
+                        future.setException(e);
+                    }
+                }
+
+                future.setException(new RuntimeException(
+                        "Unable to detect adb version, adb output: " + sb.toString()));
             }
-        }
-        return false;
+        }, "Obtaining adb version").start();
+        return future;
     }
 
     /**
@@ -1134,7 +1087,7 @@
      * This includes adding/removing listeners, but also notifying listeners of new bridges,
      * devices, and clients.
      */
-    static Object getLock() {
+    private static Object getLock() {
         return sLock;
     }
 
diff --git a/ddmlib/src/main/java/com/android/ddmlib/Client.java b/ddmlib/src/main/java/com/android/ddmlib/Client.java
index a5dc0dc..d2b1488 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/Client.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/Client.java
@@ -657,7 +657,10 @@
     void sendAndConsume(JdwpPacket packet, ChunkHandler replyHandler)
         throws IOException {
 
-        if (mChan == null) {
+        // Fix to avoid a race condition on mChan. This should be better synchronized
+        // but just capturing the channel here, avoids a NPE.
+        SocketChannel chan = mChan;
+        if (chan == null) {
             // can happen for e.g. THST packets
             Log.v("ddms", "Not sending packet -- client is closed");
             return;
@@ -672,9 +675,13 @@
             addRequestId(packet.getId(), replyHandler);
         }
 
-        synchronized (mChan) {
+        // Synchronizing on this variable is still useful as we do not want to threads
+        // reading at the same time from the same channel, and the only change that
+        // can happen to this channel is to be closed and mChan become null.
+        //noinspection SynchronizationOnLocalVariableOrMethodParameter
+        synchronized (chan) {
             try {
-                packet.writeAndConsume(mChan);
+                packet.writeAndConsume(chan);
             }
             catch (IOException ioe) {
                 removeRequestId(packet.getId());
diff --git a/ddmlib/src/main/java/com/android/ddmlib/ClientData.java b/ddmlib/src/main/java/com/android/ddmlib/ClientData.java
index f453353..2362a44 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/ClientData.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/ClientData.java
@@ -332,7 +332,7 @@
     }
 
     public static class HprofData {
-        public static enum Type {
+        public enum Type {
             FILE,
             DATA
         }
diff --git a/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java b/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java
index 6aec91e..f998ef7 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java
@@ -29,10 +29,11 @@
      */
     public static final int CURRENT_PLATFORM = currentPlatform();
 
+    public static final String EXTENSION = "trace";
     /**
      * Extension for Traceview files.
      */
-    public static final String DOT_TRACE = ".trace";
+    public static final String DOT_TRACE = "." + EXTENSION;
 
     /** hprof-conv executable (with extension for the current OS)  */
     public static final String FN_HPROF_CONVERTER = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
diff --git a/ddmlib/src/main/java/com/android/ddmlib/Device.java b/ddmlib/src/main/java/com/android/ddmlib/Device.java
index bd35ded..1cb1a4c 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/Device.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/Device.java
@@ -21,12 +21,14 @@
 import com.android.annotations.VisibleForTesting;
 import com.android.annotations.concurrency.GuardedBy;
 import com.android.ddmlib.log.LogReceiver;
+import com.google.common.base.CharMatcher;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Atomics;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -46,6 +48,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -372,7 +375,7 @@
     }
 
     // The full list of features can be obtained from /etc/permissions/features*
-    // However, since we only support the "watch" feature, we can determine that by simply
+    // However, the smaller set of features we are interested in can be obtained by
     // reading the build characteristics property.
     @Override
     public boolean supportsFeature(@NonNull HardwareFeature feature) {
@@ -425,11 +428,47 @@
         return !value.endsWith("No such file or directory");
     }
 
+    @Nullable
     @Override
-    public String getMountPoint(String name) {
-        return mMountPoints.get(name);
+    public String getMountPoint(@NonNull String name) {
+        String mount = mMountPoints.get(name);
+        if (mount == null) {
+            try {
+                mount = queryMountPoint(name);
+                mMountPoints.put(name, mount);
+            } catch (TimeoutException ignored) {
+            } catch (AdbCommandRejectedException ignored) {
+            } catch (ShellCommandUnresponsiveException ignored) {
+            } catch (IOException ignored) {
+            }
+        }
+        return mount;
     }
 
+    @Nullable
+    private String queryMountPoint(@NonNull final String name)
+            throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+            IOException {
+
+        final AtomicReference<String> ref = Atomics.newReference();
+        executeShellCommand("echo $" + name, new MultiLineReceiver() { //$NON-NLS-1$
+            @Override
+            public boolean isCancelled() {
+                return false;
+            }
+
+            @Override
+            public void processNewLines(String[] lines) {
+                for (String line : lines) {
+                    if (!line.isEmpty()) {
+                        // this should be the only one.
+                        ref.set(line);
+                    }
+                }
+            }
+        });
+        return ref.get();
+    }
 
     @Override
     public String toString() {
@@ -698,17 +737,17 @@
         removeClientInfo(client);
     }
 
-    /**
-     * Sets the client monitoring socket.
-     * @param socketChannel the sockets
-     */
-    void setClientMonitoringSocket(SocketChannel socketChannel) {
+    /** Sets the socket channel on which a track-jdwp command for this device has been sent. */
+    void setClientMonitoringSocket(@NonNull SocketChannel socketChannel) {
         mSocketChannel = socketChannel;
     }
 
     /**
-     * Returns the client monitoring socket.
+     * Returns the channel on which responses to the track-jdwp command will be available if it
+     * has been set, null otherwise. The channel is set via {@link #setClientMonitoringSocket(SocketChannel)},
+     * which is usually invoked when the device goes online.
      */
+    @Nullable
     SocketChannel getClientMonitoringSocket() {
         return mSocketChannel;
     }
@@ -1001,6 +1040,9 @@
         return receiver.getSessionId();
     }
 
+    private static final CharMatcher UNSAFE_PM_INSTALL_SESSION_SPLIT_NAME_CHARS =
+            CharMatcher.inRange('a','z').or(CharMatcher.inRange('A','Z'))
+                    .or(CharMatcher.anyOf("_-")).negate();
 
     private boolean uploadAPK(final String sessionId, String apkFilePath, int uniqueId) {
         Log.d(sessionId, String.format("Uploading APK %1$s ", apkFilePath));
@@ -1017,6 +1059,8 @@
                 ? fileToUpload.getName().substring(0, fileToUpload.getName().lastIndexOf('.'))
                 : fileToUpload.getName();
 
+        baseName = UNSAFE_PM_INSTALL_SESSION_SPLIT_NAME_CHARS.replaceFrom(baseName, '_');
+
         String command = String.format("pm install-write -S %d %s %d_%s -",
                 fileToUpload.length(), sessionId, uniqueId, baseName);
 
diff --git a/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java b/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java
index 0545a2f..4aa0daa 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java
@@ -16,10 +16,19 @@
 
 package com.android.ddmlib;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
 import com.android.ddmlib.AdbHelper.AdbResponse;
 import com.android.ddmlib.ClientData.DebuggerStatus;
 import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
 import com.android.ddmlib.IDevice.DeviceState;
+import com.android.ddmlib.utils.DebuggerPorts;
+import com.android.utils.Pair;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.util.concurrent.Uninterruptibles;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -29,63 +38,64 @@
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
- * A Device monitor. This connects to the Android Debug Bridge and get device and
- * debuggable process information from it.
+ * The {@link DeviceMonitor} monitors devices attached to adb.
+ *
+ * On one thread, it runs the {@link com.android.ddmlib.DeviceMonitor.DeviceListMonitorTask}.
+ * This  establishes a socket connection to the adb host, and issues a
+ * {@link #ADB_TRACK_DEVICES_COMMAND}. It then monitors that socket for all changes about device
+ * connection and device state.
+ *
+ * For each device that is detected to be online, it then opens a new socket connection to adb,
+ * and issues a "track-jdwp" command to that device. On this connection, it monitors active
+ * clients on the device. Note: a single thread monitors jdwp connections from all devices.
+ * The different socket connections to adb (one per device) are multiplexed over a single selector.
  */
 final class DeviceMonitor {
-    private byte[] mLengthBuffer = new byte[4];
-    private byte[] mLengthBuffer2 = new byte[4];
+    private static final String ADB_TRACK_DEVICES_COMMAND = "host:track-devices";
+    private static final String ADB_TRACK_JDWP_COMMAND = "track-jdwp";
 
-    private boolean mQuit = false;
+    private final byte[] mLengthBuffer2 = new byte[4];
 
-    private AndroidDebugBridge mServer;
+    private volatile boolean mQuit = false;
 
-    private SocketChannel mMainAdbConnection = null;
-    private boolean mMonitoring = false;
-    private int mConnectionAttempt = 0;
-    private int mRestartAttemptCount = 0;
-    private boolean mInitialDeviceListDone = false;
+    private final AndroidDebugBridge mServer;
+    private DeviceListMonitorTask mDeviceListMonitorTask;
 
     private Selector mSelector;
 
-    private final ArrayList<Device> mDevices = new ArrayList<Device>();
-
-    private final ArrayList<Integer> mDebuggerPorts = new ArrayList<Integer>();
-
-    private final HashMap<Client, Integer> mClientsToReopen = new HashMap<Client, Integer>();
+    private final List<Device> mDevices = Lists.newCopyOnWriteArrayList();
+    private final DebuggerPorts mDebuggerPorts =
+            new DebuggerPorts(DdmPreferences.getDebugPortBase());
+    private final Map<Client, Integer> mClientsToReopen = new HashMap<Client, Integer>();
+    private final BlockingQueue<Pair<SocketChannel,Device>> mChannelsToRegister =
+            Queues.newLinkedBlockingQueue();
 
     /**
      * Creates a new {@link DeviceMonitor} object and links it to the running
      * {@link AndroidDebugBridge} object.
      * @param server the running {@link AndroidDebugBridge}.
      */
-    DeviceMonitor(AndroidDebugBridge server) {
+    DeviceMonitor(@NonNull AndroidDebugBridge server) {
         mServer = server;
-
-        mDebuggerPorts.add(DdmPreferences.getDebugPortBase());
     }
 
     /**
      * Starts the monitoring.
      */
     void start() {
-        new Thread("Device List Monitor") { //$NON-NLS-1$
-            @Override
-            public void run() {
-                deviceMonitorLoop();
-            }
-        }.start();
+        mDeviceListMonitorTask = new DeviceListMonitorTask(mServer, new DeviceListUpdateListener());
+        new Thread(mDeviceListMonitorTask, "Device List Monitor").start(); //$NON-NLS-1$
     }
 
     /**
@@ -94,12 +104,8 @@
     void stop() {
         mQuit = true;
 
-        // wakeup the main loop thread by closing the main connection to adb.
-        try {
-            if (mMainAdbConnection != null) {
-                mMainAdbConnection.close();
-            }
-        } catch (IOException e1) {
+        if (mDeviceListMonitorTask != null) {
+            mDeviceListMonitorTask.stop();
         }
 
         // wake up the secondary loop by closing the selector.
@@ -108,37 +114,38 @@
         }
     }
 
-
-
     /**
-     * Returns if the monitor is currently connected to the debug bridge server.
-     * @return
+     * Returns whether the monitor is currently connected to the debug bridge server.
      */
     boolean isMonitoring() {
-        return mMonitoring;
+        return mDeviceListMonitorTask != null && mDeviceListMonitorTask.isMonitoring();
     }
 
     int getConnectionAttemptCount() {
-        return mConnectionAttempt;
+        return mDeviceListMonitorTask == null ? 0
+                : mDeviceListMonitorTask.getConnectionAttemptCount();
     }
 
     int getRestartAttemptCount() {
-        return mRestartAttemptCount;
+        return mDeviceListMonitorTask == null ? 0 : mDeviceListMonitorTask.getRestartAttemptCount();
+    }
+
+    boolean hasInitialDeviceList() {
+        return mDeviceListMonitorTask != null && mDeviceListMonitorTask.hasInitialDeviceList();
     }
 
     /**
      * Returns the devices.
      */
-    Device[] getDevices() {
-        synchronized (mDevices) {
-            return mDevices.toArray(new Device[mDevices.size()]);
-        }
+    @NonNull Device[] getDevices() {
+        // Since this is a copy of write array list, we don't want to do a compound operation
+        // (toArray with an appropriate size) without locking, so we just let the container provide
+        // an appropriately sized array
+        //noinspection ToArrayCallWithZeroLengthArrayArgument
+        return mDevices.toArray(new Device[0]);
     }
 
-    boolean hasInitialDeviceList() {
-        return mInitialDeviceListDone;
-    }
-
+    @NonNull
     AndroidDebugBridge getServer() {
         return mServer;
     }
@@ -146,7 +153,7 @@
     void addClientToDropAndReopen(Client client, int port) {
         synchronized (mClientsToReopen) {
             Log.d("DeviceMonitor",
-                    "Adding " + client + " to list of client to reopen (" + port +").");
+                    "Adding " + client + " to list of client to reopen (" + port + ").");
             if (mClientsToReopen.get(client) == null) {
                 mClientsToReopen.put(client, port);
             }
@@ -155,276 +162,65 @@
     }
 
     /**
-     * Monitors the devices. This connects to the Debug Bridge
-     */
-    private void deviceMonitorLoop() {
-        do {
-            try {
-                if (mMainAdbConnection == null) {
-                    Log.d("DeviceMonitor", "Opening adb connection");
-                    mMainAdbConnection = openAdbConnection();
-                    if (mMainAdbConnection == null) {
-                        mConnectionAttempt++;
-                        Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt);
-                        if (mConnectionAttempt > 10) {
-                            if (!mServer.startAdb()) {
-                                mRestartAttemptCount++;
-                                Log.e("DeviceMonitor",
-                                        "adb restart attempts: " + mRestartAttemptCount);
-                            } else {
-                                Log.i("DeviceMonitor", "adb restarted");
-                                mRestartAttemptCount = 0;
-                            }
-                        }
-                        waitABit();
-                    } else {
-                        Log.d("DeviceMonitor", "Connected to adb for device monitoring");
-                        mConnectionAttempt = 0;
-                    }
-                }
-
-                if (mMainAdbConnection != null && !mMonitoring) {
-                    mMonitoring = sendDeviceListMonitoringRequest();
-                }
-
-                if (mMonitoring) {
-                    // read the length of the incoming message
-                    int length = readLength(mMainAdbConnection, mLengthBuffer);
-
-                    if (length >= 0) {
-                        // read the incoming message
-                        processIncomingDeviceData(length);
-
-                        // flag the fact that we have build the list at least once.
-                        mInitialDeviceListDone = true;
-                    }
-                }
-            } catch (AsynchronousCloseException ace) {
-                // this happens because of a call to Quit. We do nothing, and the loop will break.
-            } catch (TimeoutException ioe) {
-                handleExpectionInMonitorLoop(ioe);
-            } catch (IOException ioe) {
-                handleExpectionInMonitorLoop(ioe);
-            }
-        } while (!mQuit);
-    }
-
-    private void handleExpectionInMonitorLoop(Exception e) {
-        if (!mQuit) {
-            if (e instanceof TimeoutException) {
-                Log.e("DeviceMonitor", "Adb connection Error: timeout");
-            } else {
-                Log.e("DeviceMonitor", "Adb connection Error:" + e.getMessage());
-            }
-            mMonitoring = false;
-            if (mMainAdbConnection != null) {
-                try {
-                    mMainAdbConnection.close();
-                } catch (IOException ioe) {
-                    // we can safely ignore that one.
-                }
-                mMainAdbConnection = null;
-
-                // remove all devices from list
-                // because we are going to call mServer.deviceDisconnected which will acquire this
-                // lock we lock it first, so that the AndroidDebugBridge lock is always locked
-                // first.
-                synchronized (AndroidDebugBridge.getLock()) {
-                    synchronized (mDevices) {
-                        for (int n = mDevices.size() - 1; n >= 0; n--) {
-                            Device device = mDevices.get(0);
-                            removeDevice(device);
-                            mServer.deviceDisconnected(device);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Sleeps for a little bit.
-     */
-    private void waitABit() {
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e1) {
-        }
-    }
-
-    /**
      * Attempts to connect to the debug bridge server.
      * @return a connect socket if success, null otherwise
      */
-    private SocketChannel openAdbConnection() {
-        Log.d("DeviceMonitor", "Connecting to adb for Device List Monitoring...");
-
-        SocketChannel adbChannel = null;
+    @Nullable
+    private static SocketChannel openAdbConnection() {
         try {
-            adbChannel = SocketChannel.open(AndroidDebugBridge.getSocketAddress());
+            SocketChannel adbChannel = SocketChannel.open(AndroidDebugBridge.getSocketAddress());
             adbChannel.socket().setTcpNoDelay(true);
+            return adbChannel;
         } catch (IOException e) {
+            return null;
         }
-
-        return adbChannel;
     }
 
     /**
-     *
-     * @return
-     * @throws IOException
+     * Updates the device list with the new items received from the monitoring service.
      */
-    private boolean sendDeviceListMonitoringRequest() throws TimeoutException, IOException {
-        byte[] request = AdbHelper.formAdbRequest("host:track-devices"); //$NON-NLS-1$
-
-        try {
-            AdbHelper.write(mMainAdbConnection, request);
-
-            AdbResponse resp = AdbHelper.readAdbResponse(mMainAdbConnection,
-                    false /* readDiagString */);
-
-            if (!resp.okay) {
-                // request was refused by adb!
-                Log.e("DeviceMonitor", "adb refused request: " + resp.message);
-            }
-
-            return resp.okay;
-        } catch (IOException e) {
-            Log.e("DeviceMonitor", "Sending Tracking request failed!");
-            mMainAdbConnection.close();
-            throw e;
+    private void updateDevices(@NonNull List<Device> newList) {
+        DeviceListComparisonResult result = DeviceListComparisonResult.compare(mDevices, newList);
+        for (IDevice device : result.removed) {
+            removeDevice((Device) device);
+            mServer.deviceDisconnected(device);
         }
-    }
 
-    /** Processes an incoming device message from the socket */
-    private void processIncomingDeviceData(int length) throws IOException {
-        ArrayList<Device> list = new ArrayList<Device>();
+        List<Device> newlyOnline = Lists.newArrayListWithExpectedSize(mDevices.size());
 
-        if (length > 0) {
-            byte[] buffer = new byte[length];
-            String result = read(mMainAdbConnection, buffer);
+        for (Map.Entry<IDevice, DeviceState> entry : result.updated.entrySet()) {
+            Device device = (Device) entry.getKey();
+            device.setState(entry.getValue());
+            device.update(Device.CHANGE_STATE);
 
-            String[] devices = result.split("\n"); //$NON-NLS-1$
+            if (device.isOnline()) {
+                newlyOnline.add(device);
+            }
+        }
 
-            for (String d : devices) {
-                String[] param = d.split("\t"); //$NON-NLS-1$
-                if (param.length == 2) {
-                    // new adb uses only serial numbers to identify devices
-                    Device device = new Device(this, param[0] /*serialnumber*/,
-                            DeviceState.getState(param[1]));
+        for (IDevice device : result.added) {
+            mDevices.add((Device) device);
+            mServer.deviceConnected(device);
+            if (device.isOnline()) {
+                newlyOnline.add((Device) device);
+            }
+        }
 
-                    //add the device to the list
-                    list.add(device);
+        if (AndroidDebugBridge.getClientSupport()) {
+            for (Device device : newlyOnline) {
+                if (!startMonitoringDevice(device)) {
+                    Log.e("DeviceMonitor", "Failed to start monitoring "
+                            + device.getSerialNumber());
                 }
             }
         }
 
-        // now merge the new devices with the old ones.
-        updateDevices(list);
-    }
-
-    /**
-     *  Updates the device list with the new items received from the monitoring service.
-     */
-    private void updateDevices(ArrayList<Device> newList) {
-        // because we are going to call mServer.deviceDisconnected which will acquire this lock
-        // we lock it first, so that the AndroidDebugBridge lock is always locked first.
-        synchronized (AndroidDebugBridge.getLock()) {
-            // array to store the devices that must be queried for information.
-            // it's important to not do it inside the synchronized loop as this could block
-            // the whole workspace (this lock is acquired during build too).
-            ArrayList<Device> devicesToQuery = new ArrayList<Device>();
-            synchronized (mDevices) {
-                // For each device in the current list, we look for a matching the new list.
-                // * if we find it, we update the current object with whatever new information
-                //   there is
-                //   (mostly state change, if the device becomes ready, we query for build info).
-                //   We also remove the device from the new list to mark it as "processed"
-                // * if we do not find it, we remove it from the current list.
-                // Once this is done, the new list contains device we aren't monitoring yet, so we
-                // add them to the list, and start monitoring them.
-
-                for (int d = 0 ; d < mDevices.size() ;) {
-                    Device device = mDevices.get(d);
-
-                    // look for a similar device in the new list.
-                    int count = newList.size();
-                    boolean foundMatch = false;
-                    for (int dd = 0 ; dd < count ; dd++) {
-                        Device newDevice = newList.get(dd);
-                        // see if it matches in id and serial number.
-                        if (newDevice.getSerialNumber().equals(device.getSerialNumber())) {
-                            foundMatch = true;
-
-                            // update the state if needed.
-                            if (device.getState() != newDevice.getState()) {
-                                device.setState(newDevice.getState());
-                                device.update(Device.CHANGE_STATE);
-
-                                // if the device just got ready/online, we need to start
-                                // monitoring it.
-                                if (device.isOnline()) {
-                                    if (AndroidDebugBridge.getClientSupport()) {
-                                        if (!startMonitoringDevice(device)) {
-                                            Log.e("DeviceMonitor",
-                                                    "Failed to start monitoring "
-                                                    + device.getSerialNumber());
-                                        }
-                                    }
-
-                                    if (device.getPropertyCount() == 0) {
-                                        devicesToQuery.add(device);
-                                    }
-                                }
-                            }
-
-                            // remove the new device from the list since it's been used
-                            newList.remove(dd);
-                            break;
-                        }
-                    }
-
-                    if (!foundMatch) {
-                        // the device is gone, we need to remove it, and keep current index
-                        // to process the next one.
-                        removeDevice(device);
-                        mServer.deviceDisconnected(device);
-                    } else {
-                        // process the next one
-                        d++;
-                    }
-                }
-
-                // at this point we should still have some new devices in newList, so we
-                // process them.
-                for (Device newDevice : newList) {
-                    // add them to the list
-                    mDevices.add(newDevice);
-                    mServer.deviceConnected(newDevice);
-
-                    // start monitoring them.
-                    if (AndroidDebugBridge.getClientSupport()) {
-                        if (newDevice.isOnline()) {
-                            startMonitoringDevice(newDevice);
-                        }
-                    }
-
-                    // look for their build info.
-                    if (newDevice.isOnline()) {
-                        devicesToQuery.add(newDevice);
-                    }
-                }
-            }
-
-            // query the new devices for info.
-            for (Device d : devicesToQuery) {
-                queryNewDeviceForInfo(d);
-            }
+        for (Device device : newlyOnline) {
+            queryAvdName(device);
         }
-        newList.clear();
     }
 
-    private void removeDevice(Device device) {
+    private void removeDevice(@NonNull Device device) {
         device.clearClientList();
         mDevices.remove(device);
 
@@ -438,83 +234,16 @@
         }
     }
 
-    /**
-     * Queries a device for its build info.
-     * @param device the device to query.
-     */
-    private void queryNewDeviceForInfo(Device device) {
-        // TODO: do this in a separate thread.
-        try {
-            queryProperties(device);
-
-            queryNewDeviceForMountingPoint(device, IDevice.MNT_EXTERNAL_STORAGE);
-            queryNewDeviceForMountingPoint(device, IDevice.MNT_DATA);
-            queryNewDeviceForMountingPoint(device, IDevice.MNT_ROOT);
-
-            // now get the emulator Virtual Device name (if applicable).
-            if (device.isEmulator()) {
-                EmulatorConsole console = EmulatorConsole.getConsole(device);
-                if (console != null) {
-                    device.setAvdName(console.getAvdName());
-                    console.close();
-                }
-            }
-        } catch (TimeoutException e) {
-            Log.w("DeviceMonitor", String.format("Connection timeout getting info for device %s",
-                    device.getSerialNumber()));
-
-        } catch (AdbCommandRejectedException e) {
-            // This should never happen as we only do this once the device is online.
-            Log.w("DeviceMonitor", String.format(
-                    "Adb rejected command to get  device %1$s info: %2$s",
-                    device.getSerialNumber(), e.getMessage()));
-
-        } catch (ShellCommandUnresponsiveException e) {
-            Log.w("DeviceMonitor", String.format(
-                    "Adb shell command took too long returning info for device %s",
-                    device.getSerialNumber()));
-
-        } catch (IOException e) {
-            Log.w("DeviceMonitor", String.format(
-                    "IO Error getting info for device %s",
-                    device.getSerialNumber()));
-        } catch (InterruptedException e) {
-            Log.w("DeviceMonitor", String.format(
-                    "Interrupted getting info for device %s",
-                    device.getSerialNumber()));
-        } catch (ExecutionException e) {
-            Log.w("DeviceMonitor", String.format(
-                    "ExecutionException getting info for device %s",
-                    device.getSerialNumber()));
+    private static void queryAvdName(@NonNull Device device) {
+        if (!device.isEmulator()) {
+            return;
         }
-    }
 
-    private void queryProperties(Device device) throws InterruptedException, ExecutionException {
-        // first attempt to populate the list of properties by querying arbitrary prop
-        // TODO: consider removing this call and just let properties be loaded on demand
-        Future<String> prop = device.getSystemProperty("ro.build.id");
-        prop.get();
-    }
-
-    private void queryNewDeviceForMountingPoint(final Device device, final String name)
-            throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
-            IOException {
-        device.executeShellCommand("echo $" + name, new MultiLineReceiver() { //$NON-NLS-1$
-            @Override
-            public boolean isCancelled() {
-                return false;
-            }
-
-            @Override
-            public void processNewLines(String[] lines) {
-                for (String line : lines) {
-                    if (!line.isEmpty()) {
-                        // this should be the only one.
-                        device.setMountingPoint(name, line);
-                    }
-                }
-            }
-        });
+        EmulatorConsole console = EmulatorConsole.getConsole(device);
+        if (console != null) {
+            device.setAvdName(console.getAvdName());
+            console.close();
+        }
     }
 
     /**
@@ -522,7 +251,7 @@
      * @param device the device to monitor.
      * @return true if success.
      */
-    private boolean startMonitoringDevice(Device device) {
+    private boolean startMonitoringDevice(@NonNull Device device) {
         SocketChannel socketChannel = openAdbConnection();
 
         if (socketChannel != null) {
@@ -536,15 +265,14 @@
 
                     device.setClientMonitoringSocket(socketChannel);
 
-                    synchronized (mDevices) {
-                        // always wakeup before doing the register. The synchronized block
-                        // ensure that the selector won't select() before the end of this block.
-                        // @see deviceClientMonitorLoop
-                        mSelector.wakeup();
+                    socketChannel.configureBlocking(false);
 
-                        socketChannel.configureBlocking(false);
-                        socketChannel.register(mSelector, SelectionKey.OP_READ, device);
+                    try {
+                        mChannelsToRegister.put(Pair.of(socketChannel, device));
+                    } catch (InterruptedException e) {
+                        // the queue is unbounded, and isn't going to block
                     }
+                    mSelector.wakeup();
 
                     return true;
                 }
@@ -597,12 +325,6 @@
     private void deviceClientMonitorLoop() {
         do {
             try {
-                // This synchronized block stops us from doing the select() if a new
-                // Device is being added.
-                // @see startMonitoringDevice()
-                synchronized (mDevices) {
-                }
-
                 int count = mSelector.select();
 
                 if (mQuit) {
@@ -622,7 +344,7 @@
 
                             // This is kinda bad, but if we don't wait a bit, the client
                             // will never answer the second handshake!
-                            waitABit();
+                            Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
 
                             int port = mClientsToReopen.get(client);
 
@@ -638,6 +360,17 @@
                     }
                 }
 
+                // register any new channels
+                while (!mChannelsToRegister.isEmpty()) {
+                    try {
+                        Pair<SocketChannel, Device> data = mChannelsToRegister.take();
+                        data.getFirst().register(mSelector, SelectionKey.OP_READ, data.getSecond());
+                    } catch (InterruptedException e) {
+                        // doesn't block: this thread is the only reader and it reads only when
+                        // there is data
+                    }
+                }
+
                 if (count == 0) {
                     continue;
                 }
@@ -668,12 +401,10 @@
                                     socket.close();
 
                                     // restart the monitoring of that device
-                                    synchronized (mDevices) {
-                                        if (mDevices.contains(device)) {
-                                            Log.d("DeviceMonitor",
-                                                    "Restarting monitoring service for " + device);
-                                            startMonitoringDevice(device);
-                                        }
+                                    if (mDevices.contains(device)) {
+                                        Log.d("DeviceMonitor",
+                                                "Restarting monitoring service for " + device);
+                                        startMonitoringDevice(device);
                                     }
                                 }
                             }
@@ -687,17 +418,14 @@
         } while (!mQuit);
     }
 
-    private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device)
+    private static boolean sendDeviceMonitoringRequest(@NonNull SocketChannel socket,
+            @NonNull Device device)
             throws TimeoutException, AdbCommandRejectedException, IOException {
 
         try {
             AdbHelper.setDevice(socket, device);
-
-            byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$
-
-            AdbHelper.write(socket, request);
-
-            AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */);
+            AdbHelper.write(socket, AdbHelper.formAdbRequest(ADB_TRACK_JDWP_COMMAND));
+            AdbResponse resp = AdbHelper.readAdbResponse(socket, false);
 
             if (!resp.okay) {
                 // request was refused by adb!
@@ -714,8 +442,8 @@
         }
     }
 
-    private void processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length)
-            throws IOException {
+    private void processIncomingJdwpData(@NonNull Device device,
+            @NonNull SocketChannel monitorSocket, int length) throws IOException {
 
         // This methods reads @length bytes from the @monitorSocket channel.
         // These bytes correspond to the pids of the current set of processes on the device.
@@ -733,7 +461,7 @@
                 String result = read(monitorSocket, buffer);
 
                 // split each line in its own list and create an array of integer pid
-                String[] pids = result.split("\n"); //$NON-NLS-1$
+                String[] pids = result == null ? new String[0] : result.split("\n"); //$NON-NLS-1$
 
                 for (String pid : pids) {
                     try {
@@ -752,9 +480,7 @@
 
             synchronized (clients) {
                 for (Client c : clients) {
-                    existingClients.put(
-                            c.getClientData().getPid(),
-                            c);
+                    existingClients.put(c.getClientData().getPid(), c);
                 }
             }
 
@@ -781,11 +507,9 @@
         }
     }
 
-    /**
-     * Opens and creates a new client.
-     * @return
-     */
-    private void openClient(Device device, int pid, int port, MonitorThread monitorThread) {
+    /** Opens and creates a new client. */
+    private static void openClient(@NonNull Device device, int pid, int port,
+            @NonNull MonitorThread monitorThread) {
 
         SocketChannel clientSocket;
         try {
@@ -815,16 +539,9 @@
         createClient(device, pid, clientSocket, port, monitorThread);
     }
 
-    /**
-     * Creates a client and register it to the monitor thread
-     * @param device
-     * @param pid
-     * @param socket
-     * @param debuggerPort the debugger port.
-     * @param monitorThread the {@link MonitorThread} object.
-     */
-    private void createClient(Device device, int pid, SocketChannel socket, int debuggerPort,
-            MonitorThread monitorThread) {
+    /** Creates a client and register it to the monitor thread */
+    private static void createClient(@NonNull Device device, int pid, @NonNull SocketChannel socket,
+            int debuggerPort, @NonNull MonitorThread monitorThread) {
 
         /*
          * Successfully connected to something. Create a Client object, add
@@ -858,51 +575,15 @@
         if (client.isValid()) {
             device.addClient(client);
             monitorThread.addClient(client);
-        } else {
-            client = null;
         }
     }
 
     private int getNextDebuggerPort() {
-        // get the first port and remove it
-        synchronized (mDebuggerPorts) {
-            if (!mDebuggerPorts.isEmpty()) {
-                int port = mDebuggerPorts.get(0);
-
-                // remove it.
-                mDebuggerPorts.remove(0);
-
-                // if there's nothing left, add the next port to the list
-                if (mDebuggerPorts.isEmpty()) {
-                    mDebuggerPorts.add(port+1);
-                }
-
-                return port;
-            }
-        }
-
-        return -1;
+        return mDebuggerPorts.next();
     }
 
     void addPortToAvailableList(int port) {
-        if (port > 0) {
-            synchronized (mDebuggerPorts) {
-                // because there could be case where clients are closed twice, we have to make
-                // sure the port number is not already in the list.
-                if (mDebuggerPorts.indexOf(port) == -1) {
-                    // add the port to the list while keeping it sorted. It's not like there's
-                    // going to be tons of objects so we do it linearly.
-                    int count = mDebuggerPorts.size();
-                    for (int i = 0 ; i < count ; i++) {
-                        if (port < mDebuggerPorts.get(i)) {
-                            mDebuggerPorts.add(i, port);
-                            break;
-                        }
-                    }
-                    // TODO: check if we can compact the end of the list.
-                }
-            }
-        }
+        mDebuggerPorts.free(port);
     }
 
     /**
@@ -911,7 +592,8 @@
      * @return the length, or 0 (zero) if no data is available from the socket.
      * @throws IOException if the connection failed.
      */
-    private int readLength(SocketChannel socket, byte[] buffer) throws IOException {
+    private static int readLength(@NonNull SocketChannel socket, @NonNull byte[] buffer)
+            throws IOException {
         String msg = read(socket, buffer);
 
         if (msg != null) {
@@ -920,20 +602,20 @@
             } catch (NumberFormatException nfe) {
                 // we'll throw an exception below.
             }
-       }
+        }
 
         // we receive something we can't read. It's better to reset the connection at this point.
         throw new IOException("Unable to read length");
     }
 
     /**
-     * Fills a buffer from a socket.
-     * @param socket
-     * @param buffer
+     * Fills a buffer by reading data from a socket.
      * @return the content of the buffer as a string, or null if it failed to convert the buffer.
-     * @throws IOException
+     * @throws IOException if there was not enough data to fill the buffer
      */
-    private String read(SocketChannel socket, byte[] buffer) throws IOException {
+    @Nullable
+    private static String read(@NonNull SocketChannel socket, @NonNull byte[] buffer)
+            throws IOException {
         ByteBuffer buf = ByteBuffer.wrap(buffer, 0, buffer.length);
 
         while (buf.position() != buf.limit()) {
@@ -948,10 +630,255 @@
         try {
             return new String(buffer, 0, buf.position(), AdbHelper.DEFAULT_ENCODING);
         } catch (UnsupportedEncodingException e) {
-            // we'll return null below.
+            return null;
         }
-
-        return null;
     }
 
+    private class DeviceListUpdateListener implements DeviceListMonitorTask.UpdateListener {
+        @Override
+        public void connectionError(@NonNull Exception e) {
+            for (Device device : mDevices) {
+                removeDevice(device);
+                mServer.deviceDisconnected(device);
+            }
+        }
+
+        @Override
+        public void deviceListUpdate(@NonNull Map<String, DeviceState> devices) {
+            List<Device> l = Lists.newArrayListWithExpectedSize(devices.size());
+            for (Map.Entry<String, DeviceState> entry : devices.entrySet()) {
+                l.add(new Device(DeviceMonitor.this, entry.getKey(), entry.getValue()));
+            }
+            // now merge the new devices with the old ones.
+            updateDevices(l);
+        }
+    }
+
+    @VisibleForTesting
+    static class DeviceListComparisonResult {
+        @NonNull public final Map<IDevice,DeviceState> updated;
+        @NonNull public final List<IDevice> added;
+        @NonNull public final List<IDevice> removed;
+
+        private DeviceListComparisonResult(@NonNull Map<IDevice,DeviceState> updated,
+                @NonNull List<IDevice> added,
+                @NonNull List<IDevice> removed) {
+            this.updated = updated;
+            this.added = added;
+            this.removed = removed;
+        }
+
+        @NonNull
+        public static DeviceListComparisonResult compare(@NonNull List<? extends IDevice> previous,
+                @NonNull List<? extends IDevice> current) {
+            current = Lists.newArrayList(current);
+
+            final Map<IDevice,DeviceState> updated = Maps.newHashMapWithExpectedSize(current.size());
+            final List<IDevice> added = Lists.newArrayListWithExpectedSize(1);
+            final List<IDevice> removed = Lists.newArrayListWithExpectedSize(1);
+
+            for (IDevice device : previous) {
+                IDevice currentDevice = find(current, device);
+                if (currentDevice != null) {
+                    if (currentDevice.getState() != device.getState()) {
+                        updated.put(device, currentDevice.getState());
+                    }
+                    current.remove(currentDevice);
+                } else {
+                    removed.add(device);
+                }
+            }
+
+            added.addAll(current);
+
+            return new DeviceListComparisonResult(updated, added, removed);
+        }
+
+        @Nullable
+        private static IDevice find(@NonNull List<? extends IDevice> devices,
+                @NonNull IDevice device) {
+            for (IDevice d : devices) {
+                if (d.getSerialNumber().equals(device.getSerialNumber())) {
+                    return d;
+                }
+            }
+
+            return null;
+        }
+    }
+
+    @VisibleForTesting
+    static class DeviceListMonitorTask implements Runnable {
+        private final byte[] mLengthBuffer = new byte[4];
+
+        private final AndroidDebugBridge mBridge;
+        private final UpdateListener mListener;
+
+        private SocketChannel mAdbConnection = null;
+        private boolean mMonitoring = false;
+        private int mConnectionAttempt = 0;
+        private int mRestartAttemptCount = 0;
+        private boolean mInitialDeviceListDone = false;
+
+        private volatile boolean mQuit;
+
+        private interface UpdateListener {
+            void connectionError(@NonNull Exception e);
+            void deviceListUpdate(@NonNull Map<String,DeviceState> devices);
+        }
+
+        public DeviceListMonitorTask(@NonNull AndroidDebugBridge bridge,
+                @NonNull UpdateListener listener) {
+            mBridge = bridge;
+            mListener = listener;
+        }
+
+        @Override
+        public void run() {
+            do {
+                if (mAdbConnection == null) {
+                    Log.d("DeviceMonitor", "Opening adb connection");
+                    mAdbConnection = openAdbConnection();
+                    if (mAdbConnection == null) {
+                        mConnectionAttempt++;
+                        Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt);
+                        if (mConnectionAttempt > 10) {
+                            if (!mBridge.startAdb()) {
+                                mRestartAttemptCount++;
+                                Log.e("DeviceMonitor",
+                                        "adb restart attempts: " + mRestartAttemptCount);
+                            } else {
+                                Log.i("DeviceMonitor", "adb restarted");
+                                mRestartAttemptCount = 0;
+                            }
+                        }
+                        Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
+                    } else {
+                        Log.d("DeviceMonitor", "Connected to adb for device monitoring");
+                        mConnectionAttempt = 0;
+                    }
+                }
+
+                try {
+                    if (mAdbConnection != null && !mMonitoring) {
+                        mMonitoring = sendDeviceListMonitoringRequest();
+                    }
+
+                    if (mMonitoring) {
+                        int length = readLength(mAdbConnection, mLengthBuffer);
+
+                        if (length >= 0) {
+                            // read the incoming message
+                            processIncomingDeviceData(length);
+
+                            // flag the fact that we have build the list at least once.
+                            mInitialDeviceListDone = true;
+                        }
+                    }
+                } catch (AsynchronousCloseException ace) {
+                    // this happens because of a call to Quit. We do nothing, and the loop will break.
+                } catch (TimeoutException ioe) {
+                    handleExceptionInMonitorLoop(ioe);
+                } catch (IOException ioe) {
+                    handleExceptionInMonitorLoop(ioe);
+                }
+            } while (!mQuit);
+        }
+
+        private boolean sendDeviceListMonitoringRequest() throws TimeoutException, IOException {
+            byte[] request = AdbHelper.formAdbRequest(ADB_TRACK_DEVICES_COMMAND);
+
+            try {
+                AdbHelper.write(mAdbConnection, request);
+                AdbResponse resp = AdbHelper.readAdbResponse(mAdbConnection, false);
+                if (!resp.okay) {
+                    // request was refused by adb!
+                    Log.e("DeviceMonitor", "adb refused request: " + resp.message);
+                }
+
+                return resp.okay;
+            } catch (IOException e) {
+                Log.e("DeviceMonitor", "Sending Tracking request failed!");
+                mAdbConnection.close();
+                throw e;
+            }
+        }
+
+        private void handleExceptionInMonitorLoop(@NonNull Exception e) {
+            if (!mQuit) {
+                if (e instanceof TimeoutException) {
+                    Log.e("DeviceMonitor", "Adb connection Error: timeout");
+                } else {
+                    Log.e("DeviceMonitor", "Adb connection Error:" + e.getMessage());
+                }
+                mMonitoring = false;
+                if (mAdbConnection != null) {
+                    try {
+                        mAdbConnection.close();
+                    } catch (IOException ioe) {
+                        // we can safely ignore that one.
+                    }
+                    mAdbConnection = null;
+
+                    mListener.connectionError(e);
+                }
+            }
+        }
+
+        /** Processes an incoming device message from the socket */
+        private void processIncomingDeviceData(int length) throws IOException {
+            Map<String, DeviceState> result;
+            if (length <= 0) {
+                result = Collections.emptyMap();
+            } else {
+                String response = read(mAdbConnection, new byte[length]);
+                result = parseDeviceListResponse(response);
+            }
+
+            mListener.deviceListUpdate(result);
+        }
+
+        @VisibleForTesting
+        static Map<String, DeviceState> parseDeviceListResponse(@Nullable String result) {
+            Map<String, DeviceState> deviceStateMap = Maps.newHashMap();
+            String[] devices = result == null ? new String[0] : result.split("\n"); //$NON-NLS-1$
+
+            for (String d : devices) {
+                String[] param = d.split("\t"); //$NON-NLS-1$
+                if (param.length == 2) {
+                    // new adb uses only serial numbers to identify devices
+                    deviceStateMap.put(param[0], DeviceState.getState(param[1]));
+                }
+            }
+            return deviceStateMap;
+        }
+
+        boolean isMonitoring() {
+            return mMonitoring;
+        }
+
+        boolean hasInitialDeviceList() {
+            return mInitialDeviceListDone;
+        }
+
+        int getConnectionAttemptCount() {
+            return mConnectionAttempt;
+        }
+
+        int getRestartAttemptCount() {
+            return mRestartAttemptCount;
+        }
+
+        public void stop() {
+            mQuit = true;
+
+            // wakeup the main loop thread by closing the main connection to adb.
+            if (mAdbConnection != null) {
+                try {
+                    mAdbConnection.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
 }
diff --git a/ddmlib/src/main/java/com/android/ddmlib/IDevice.java b/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
index b45d436..be45449 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
@@ -35,6 +35,8 @@
     String PROP_BUILD_VERSION = "ro.build.version.release";
     String PROP_BUILD_API_LEVEL = "ro.build.version.sdk";
     String PROP_BUILD_CODENAME = "ro.build.version.codename";
+    String PROP_BUILD_TAGS = "ro.build.tags";
+    String PROP_BUILD_TYPE = "ro.build.type";
     String PROP_DEVICE_MODEL = "ro.product.model";
     String PROP_DEVICE_MANUFACTURER = "ro.product.manufacturer";
     String PROP_DEVICE_CPU_ABI_LIST = "ro.product.cpu.abilist";
@@ -64,7 +66,8 @@
 
     /** Device level hardware features. */
     enum HardwareFeature {
-        WATCH("watch");                   // supports feature watch
+        WATCH("watch"),
+        TV("tv");
 
         private final String mCharacteristic;
 
@@ -239,7 +242,8 @@
      * @see #MNT_ROOT
      * @see #MNT_DATA
      */
-    String getMountPoint(String name);
+    @Nullable
+    String getMountPoint(@NonNull String name);
 
     /**
      * Returns if the device is ready.
diff --git a/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java b/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java
index 48afd1e..79b3ede 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java
@@ -64,16 +64,22 @@
             mArray.clear();
             int start = 0;
             do {
-                int index = s.indexOf("\r\n", start); //$NON-NLS-1$
+                int index = s.indexOf('\n', start); //$NON-NLS-1$
 
-                // if \r\n was not found, this is an unfinished line
+                // if \n was not found, this is an unfinished line
                 // and we store it to be processed for the next packet
                 if (index == -1) {
                     mUnfinishedLine = s.substring(start);
                     break;
                 }
 
-                // so we found a \r\n;
+                // we found a \n, in older devices, this is preceded by a \r
+                int newlineLength = 1;
+                if (index > 0 && s.charAt(index - 1) == '\r') {
+                    index--;
+                    newlineLength = 2;
+                }
+
                 // extract the line
                 String line = s.substring(start, index);
                 if (mTrimLines) {
@@ -82,7 +88,7 @@
                 mArray.add(line);
 
                 // move start to after the \r\n we found
-                start = index + 2;
+                start = index + newlineLength;
             } while (true);
 
             if (!mArray.isEmpty()) {
diff --git a/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java b/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java
index be365bf..72d7c77 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java
@@ -24,7 +24,7 @@
  * information as one object.
  */
 public final class NativeStackCallInfo {
-    private static final Pattern SOURCE_NAME_PATTERN = Pattern.compile("^(.+):(\\d+)$");
+    private static final Pattern SOURCE_NAME_PATTERN = Pattern.compile("^(.+):(\\d+)(\\s+\\(discriminator\\s+\\d+\\))?$");
 
     /** address of this stack frame */
     private long mAddress;
@@ -65,6 +65,10 @@
             } catch (NumberFormatException e) {
                 // do nothing, the line number will stay at -1
             }
+            if (m.groupCount() == 3) {
+                // A discriminator was found, add that in the source file name.
+                mSourceFile += m.group(3);
+            }
         } else {
             mSourceFile = sourceFile;
         }
diff --git a/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java b/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java
index b366d3b..dc83115 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java
@@ -112,7 +112,7 @@
         SettableFuture<String> result;
         if (mCacheState.equals(CacheState.FETCHING)) {
             result = addPendingRequest(name);
-        } else if (mCacheState.equals(CacheState.UNPOPULATED) || !isRoProp(name)) {
+        } else if (mDevice.isOnline() && mCacheState.equals(CacheState.UNPOPULATED) || !isRoProp(name)) {
             // cache is empty, or this is a volatile prop that requires a query
             result = addPendingRequest(name);
             mCacheState = CacheState.FETCHING;
diff --git a/ddmlib/src/main/java/com/android/ddmlib/SyncService.java b/ddmlib/src/main/java/com/android/ddmlib/SyncService.java
index 7958cf9..19c3c14 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/SyncService.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/SyncService.java
@@ -16,6 +16,8 @@
 
 package com.android.ddmlib;
 
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.ddmlib.AdbHelper.AdbResponse;
 import com.android.ddmlib.FileListingService.FileEntry;
 import com.android.ddmlib.SyncException.SyncError;
@@ -29,6 +31,7 @@
 import java.net.InetSocketAddress;
 import java.nio.channels.SocketChannel;
 import java.util.ArrayList;
+import java.util.Date;
 
 /**
  * Sync service class to push/pull to/from devices/emulators, through the debug bridge.
@@ -109,6 +112,30 @@
         void advance(int work);
     }
 
+    public static class FileStat {
+      private final int myMode;
+      private final int mySize;
+      private final Date myLastModified;
+
+      public FileStat(int mode, int size, int lastModifiedSecs) {
+        myMode = mode;
+        mySize = size;
+        myLastModified = new Date((long)(lastModifiedSecs) * 1000);
+      }
+
+      public int getMode() {
+        return myMode;
+      }
+
+      public int getSize() {
+        return mySize;
+      }
+
+      public Date getLastModified() {
+        return myLastModified;
+      }
+    }
+
     /**
      * A Sync progress monitor that does nothing
      */
@@ -307,10 +334,10 @@
      */
     public void pullFile(String remoteFilepath, String localFilename,
             ISyncProgressMonitor monitor) throws TimeoutException, IOException, SyncException {
-        Integer mode = readMode(remoteFilepath);
-        if (mode == null) {
+        FileStat fileStat = statFile(remoteFilepath);
+        if (fileStat == null) {
             // attempts to download anyway
-        } else if (mode == 0) {
+        } else if (fileStat.getMode() == 0) {
             throw new SyncException(SyncError.NO_REMOTE_OBJECT);
         }
 
@@ -626,6 +653,7 @@
         byte[] msg;
 
         final int timeOut = DdmPreferences.getTimeOut();
+        File f = new File(localPath);
 
         try {
             byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
@@ -634,8 +662,6 @@
                 throw new SyncException(SyncError.REMOTE_PATH_LENGTH);
             }
 
-            File f = new File(localPath);
-
             // create the stream to read the file
             fis = new FileInputStream(f);
 
@@ -683,7 +709,7 @@
         }
 
         // create the DONE message
-        long time = System.currentTimeMillis() / 1000;
+        long time = f.lastModified() / 1000;
         msg = createReq(ID_DONE, (int)time);
 
         // and send it.
@@ -727,14 +753,15 @@
     }
 
     /**
-     * Returns the mode of the remote file.
+     * Returns the stat info of the remote file.
      * @param path the remote file
-     * @return an Integer containing the mode if all went well or null
+     * @return an FileStat containing the mode, size and last modified info if all went well or null
      *      otherwise
      * @throws IOException
      * @throws TimeoutException in case of a timeout reading responses from the device.
      */
-    private Integer readMode(String path) throws TimeoutException, IOException {
+    @Nullable
+    public FileStat statFile(@NonNull String path) throws TimeoutException, IOException {
         // create the stat request message.
         byte[] msg = createFileReq(ID_STAT, path);
 
@@ -750,8 +777,10 @@
             return null;
         }
 
-        // we return the mode (2nd int in the array)
-        return ArrayHelper.swap32bitFromArray(statResult, 4);
+        final int mode = ArrayHelper.swap32bitFromArray(statResult, 4);
+        final int size = ArrayHelper.swap32bitFromArray(statResult, 8);
+        final int lastModifiedSecs = ArrayHelper.swap32bitFromArray(statResult, 12);
+        return new FileStat(mode, size, lastModifiedSecs);
     }
 
     /**
diff --git a/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java b/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java
index 4f490d8..3607e60 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java
@@ -390,7 +390,7 @@
         }
     }
 
-    private final Object getValue(Object data, int valueIndex, boolean recursive) {
+    private Object getValue(Object data, int valueIndex, boolean recursive) {
         EventValueType type = getType(data);
 
         switch (type) {
@@ -410,7 +410,7 @@
         return null;
     }
 
-    private final double getValueAsDouble(Object data, int valueIndex, boolean recursive)
+    private double getValueAsDouble(Object data, int valueIndex, boolean recursive)
             throws InvalidTypeException {
         EventValueType type = getType(data);
 
@@ -433,7 +433,7 @@
         throw new InvalidTypeException();
     }
 
-    private final String getValueAsString(Object data, int valueIndex, boolean recursive)
+    private String getValueAsString(Object data, int valueIndex, boolean recursive)
             throws InvalidTypeException {
         EventValueType type = getType(data);
 
diff --git a/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java b/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java
index 859e080..9fd8b59 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java
@@ -296,7 +296,7 @@
      * @param valueIndex the index of the value.
      * @throws InvalidTypeException if index is 0 as it is a string value.
      */
-    private final long getValueAsLong(int valueIndex) throws InvalidTypeException {
+    private long getValueAsLong(int valueIndex) throws InvalidTypeException {
         switch (valueIndex) {
             case 0:
                 throw new InvalidTypeException();
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java
index 6e7bfa2..40c9971 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java
@@ -476,6 +476,7 @@
             case StatusCodes.IGNORED:
                 metrics = getAndResetTestMetrics();
                 for (ITestRunListener listener : mTestListeners) {
+                    listener.testStarted(testId);
                     listener.testIgnored(testId);
                     listener.testEnded(testId, metrics);
                 }
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
index de4f4c6..a8f1491 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -18,7 +18,6 @@
 
 import com.android.annotations.NonNull;
 import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.CollectingOutputReceiver;
 import com.android.ddmlib.IDevice;
 import com.android.ddmlib.IShellEnabledDevice;
 import com.android.ddmlib.Log;
@@ -31,8 +30,6 @@
 import java.util.Hashtable;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -252,7 +249,7 @@
     public void run(Collection<ITestRunListener> listeners)
             throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
             IOException {
-        final String runCaseCommandStr = String.format("am instrument -w %1$s-r %2$s %3$s",
+        final String runCaseCommandStr = String.format("am instrument -w -r %1$s %2$s %3$s",
                 getRunOptions(), getArgsCommand(), getRunnerPath());
         Log.i(LOG_TAG, String.format("Running %1$s on %2$s", runCaseCommandStr,
                 mRemoteDevice.getName()));
@@ -304,7 +301,7 @@
      * See com/android/commands/am/Am.java for full list of options.
      */
     public void setRunOptions(@NonNull String options) {
-        mRunOptions = options + " ";
+        mRunOptions = options;
     }
 
     @Override
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java
index e2486fa..4707f1a 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java
@@ -157,8 +157,7 @@
      * Return total number of tests in a failure state (failed, assumption failure)
      */
     public int getNumAllFailedTests() {
-        return getNumTestsInState(TestStatus.FAILURE) +
-                getNumTestsInState(TestStatus.ASSUMPTION_FAILURE);
+        return getNumTestsInState(TestStatus.FAILURE);
     }
 
     /**
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java
index 9a09c7b..36cb4d4 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java
@@ -285,10 +285,12 @@
         serializer.attribute(ns, ATTR_TIME, Double.toString((double)elapsedTimeMs / 1000.f));
 
         switch (testResult.getStatus()) {
-            case FAILURE: // intentional fall through
-            case ASSUMPTION_FAILURE:
+            case FAILURE:
                 printFailedTest(serializer, FAILURE, testResult.getStackTrace());
                 break;
+            case ASSUMPTION_FAILURE:
+                printFailedTest(serializer, SKIPPED_TAG, testResult.getStackTrace());
+                break;
             case IGNORED:
                 serializer.startTag(ns, SKIPPED_TAG);
                 serializer.endTag(ns, SKIPPED_TAG);
diff --git a/ddmlib/src/main/java/com/android/ddmlib/utils/DebuggerPorts.java b/ddmlib/src/main/java/com/android/ddmlib/utils/DebuggerPorts.java
new file mode 100644
index 0000000..ee2eca3
--- /dev/null
+++ b/ddmlib/src/main/java/com/android/ddmlib/utils/DebuggerPorts.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.ddmlib.utils;
+
+import com.android.annotations.concurrency.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DebuggerPorts {
+    @GuardedBy("mDebuggerPorts")
+    private final List<Integer> mDebuggerPorts = new ArrayList<Integer>();
+
+    public DebuggerPorts(int basePort) {
+        mDebuggerPorts.add(basePort);
+    }
+
+    public int next() {
+        // get the first port and remove it
+        synchronized (mDebuggerPorts) {
+            if (!mDebuggerPorts.isEmpty()) {
+                int port = mDebuggerPorts.get(0);
+
+                // remove it.
+                mDebuggerPorts.remove(0);
+
+                // if there's nothing left, add the next port to the list
+                if (mDebuggerPorts.isEmpty()) {
+                    mDebuggerPorts.add(port+1);
+                }
+
+                return port;
+            }
+        }
+
+        return -1;
+    }
+
+    public void free(int port) {
+        if (port <= 0) {
+            return;
+        }
+
+        synchronized (mDebuggerPorts) {
+            // because there could be case where clients are closed twice, we have to make
+            // sure the port number is not already in the list.
+            if (mDebuggerPorts.indexOf(port) == -1) {
+                // add the port to the list while keeping it sorted. It's not like there's
+                // going to be tons of objects so we do it linearly.
+                int count = mDebuggerPorts.size();
+                for (int i = 0; i < count; i++) {
+                    if (port < mDebuggerPorts.get(i)) {
+                        mDebuggerPorts.add(i, port);
+                        break;
+                    }
+                }
+                // TODO: check if we can compact the end of the list.
+            }
+        }
+    }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/AdbVersionTest.java b/ddmlib/src/test/java/com/android/ddmlib/AdbVersionTest.java
new file mode 100644
index 0000000..6f1c51f
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/AdbVersionTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.ddmlib;
+
+import junit.framework.TestCase;
+
+public class AdbVersionTest extends TestCase {
+    public void testParser() {
+        AdbVersion version = AdbVersion.parseFrom("Android Debug Bridge version 1.0.32");
+        assertEquals("1.0.32", version.toString());
+
+        version = AdbVersion.parseFrom("Android Debug Bridge version 1.0.32 8b22c293a0e5-android");
+        assertEquals(AdbVersion.parseFrom("1.0.32"), version);
+
+        version = AdbVersion.parseFrom("1.0.unknown");
+        assertEquals(AdbVersion.UNKNOWN, version);
+    }
+
+    public void testComponents() {
+        AdbVersion version = AdbVersion
+                .parseFrom("Android Debug Bridge version 1.23.32 8b22c293a0e5-android");
+        assertEquals(1, version.major);
+        assertEquals(23, version.minor);
+        assertEquals(32, version.micro);
+    }
+
+    public void testComparison() {
+        AdbVersion min = AdbVersion.parseFrom("1.0.20");
+        AdbVersion now = AdbVersion.parseFrom("1.0.32");
+        assertTrue(now.compareTo(min) > 0);
+        assertTrue(min.compareTo(now) < 0);
+        assertTrue(now.compareTo(now) == 0);
+
+        AdbVersion f = AdbVersion.parseFrom("2.0.32");
+        assertTrue(f.compareTo(now) > 0);
+    }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java b/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java
index f9aa8a3..85a8f53 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java
@@ -20,9 +20,12 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 
 public class AndroidDebugBridgeTest extends TestCase {
     private String mAndroidHome;
+    private File mAdbPath;
 
     @Override
     protected void setUp() throws Exception {
@@ -31,19 +34,37 @@
                 "This test requires ANDROID_HOME environment variable to point to a valid SDK",
                 mAndroidHome);
 
-        AndroidDebugBridge.init(false);
+        mAdbPath = new File(mAndroidHome, "platform-tools" + File.separator + "adb");
+
+        AndroidDebugBridge.initIfNeeded(false);
     }
 
     // https://code.google.com/p/android/issues/detail?id=63170
     public void testCanRecreateAdb() throws IOException {
-        File adbPath = new File(mAndroidHome, "platform-tools" + File.separator + "adb");
-
-        AndroidDebugBridge adb = AndroidDebugBridge.createBridge(adbPath.getCanonicalPath(), true);
+        AndroidDebugBridge adb = AndroidDebugBridge.createBridge(mAdbPath.getCanonicalPath(), true);
         assertNotNull(adb);
         AndroidDebugBridge.terminate();
 
-        adb = AndroidDebugBridge.createBridge(adbPath.getCanonicalPath(), true);
+        adb = AndroidDebugBridge.createBridge(mAdbPath.getCanonicalPath(), true);
         assertNotNull(adb);
         AndroidDebugBridge.terminate();
     }
+
+    // Some consumers of ddmlib rely on adb being on the path, and hence being
+    // able to create a bridge by simply passing in "adb" as the path to adb.
+    // We should be able to create a bridge in such a case as well.
+    // This test will fail if adb is not currently on the path. It is disabled since we
+    // can't enforce that condition (adb on $PATH) very well from a test..
+    public void disabled_testEmptyAdbPath() throws Exception {
+        AdbVersion version = AndroidDebugBridge.getAdbVersion(
+                new File("adb")).get(5, TimeUnit.SECONDS);
+        assertTrue(version.compareTo(AdbVersion.parseFrom("1.0.20")) > 0);
+    }
+
+    public void testAdbVersion() throws Exception {
+        AdbVersion version = AndroidDebugBridge
+                .getAdbVersion(mAdbPath).get(5, TimeUnit.SECONDS);
+        assertNotSame(version, AdbVersion.UNKNOWN);
+        assertTrue(version.compareTo(AdbVersion.parseFrom("1.0.20")) > 0);
+    }
 }
diff --git a/ddmlib/src/test/java/com/android/ddmlib/DeviceMonitorTest.java b/ddmlib/src/test/java/com/android/ddmlib/DeviceMonitorTest.java
new file mode 100644
index 0000000..0182e69
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/DeviceMonitorTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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.ddmlib;
+
+import com.android.annotations.NonNull;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class DeviceMonitorTest extends TestCase {
+    public void testDeviceListMonitor() {
+        Map<String, IDevice.DeviceState> map = DeviceMonitor.DeviceListMonitorTask
+                .parseDeviceListResponse("R32C801BL5K\tdevice\n0079864fd1d150fd\tunauthorized\n");
+
+        assertEquals(IDevice.DeviceState.ONLINE, map.get("R32C801BL5K"));
+        assertEquals(IDevice.DeviceState.UNAUTHORIZED, map.get("0079864fd1d150fd"));
+    }
+
+    public void testDeviceListComparator() {
+        List<IDevice> previous = Arrays.asList(
+                mockDevice("1", IDevice.DeviceState.ONLINE),
+                mockDevice("2", IDevice.DeviceState.BOOTLOADER)
+        );
+        List<IDevice> current = Arrays.asList(
+                mockDevice("2", IDevice.DeviceState.ONLINE),
+                mockDevice("3", IDevice.DeviceState.OFFLINE)
+        );
+
+        DeviceMonitor.DeviceListComparisonResult result = DeviceMonitor.DeviceListComparisonResult
+                .compare(previous, current);
+
+        assertEquals(1, result.updated.size());
+        assertEquals(IDevice.DeviceState.ONLINE, result.updated.get(previous.get(1)));
+
+        assertEquals(1, result.removed.size());
+        assertEquals("1", result.removed.get(0).getSerialNumber());
+
+        assertEquals(1, result.added.size());
+        assertEquals("3", result.added.get(0).getSerialNumber());
+    }
+
+    private IDevice mockDevice(@NonNull String serial, @NonNull IDevice.DeviceState state) {
+        IDevice device = EasyMock.createMock(IDevice.class);
+        EasyMock.expect(device.getSerialNumber()).andStubReturn(serial);
+        EasyMock.expect(device.getState()).andStubReturn(state);
+        EasyMock.replay(device);
+        return device;
+    }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java b/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
index cb53b14..5de9feb 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
@@ -78,6 +78,7 @@
     static IDevice createMockDevice() {
         IDevice mockDevice = EasyMock.createMock(IDevice.class);
         EasyMock.expect(mockDevice.getSerialNumber()).andStubReturn("serial");
+        EasyMock.expect(mockDevice.isOnline()).andStubReturn(Boolean.TRUE);
         return mockDevice;
     }
 }
diff --git a/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java b/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java
index 1049bdc..e715a6b 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java
@@ -26,8 +26,8 @@
 import java.util.concurrent.TimeUnit;
 
 public class PropertyFetcherTest extends TestCase {
-    final static String GETPROP_RESPONSE =
-            "[ro.sf.lcd_density]: [480]\r\n" +
+    static final String GETPROP_RESPONSE =
+            "\n[ro.sf.lcd_density]: [480]\n" +
             "[ro.secure]: [1]\r\n";
 
     /**
diff --git a/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
index 3e4cdf4..409e2c1 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
@@ -314,6 +314,112 @@
         assertEquals(3, captureMetrics.getValue().size());
     }
 
+    public void testParse_AssumptionFailuresIgnored() {
+        StringBuilder output = new StringBuilder();
+        addLine(output, "INSTRUMENTATION_STATUS: numtests=3");
+        addLine(output, "INSTRUMENTATION_STATUS: stream=");
+        addLine(output,  "com.example.helloworld.FailureAssumptionTest:");
+        addLine(output,  "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+        addLine(output,  "INSTRUMENTATION_STATUS: test=checkIgnoreTestsArePossible");
+        addLine(output,  "INSTRUMENTATION_STATUS: class=com.example.helloworld.FailureAssumptionTest");
+        addLine(output,  "INSTRUMENTATION_STATUS: current=1");
+        addLine(output,  "INSTRUMENTATION_STATUS_CODE: -3");
+        addLine(output,  "INSTRUMENTATION_STATUS: numtests=3");
+        addLine(output,  "INSTRUMENTATION_STATUS: stream=");
+        addLine(output,  "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+        addLine(output,  "INSTRUMENTATION_STATUS: test=checkAssumptionIsSkipped");
+        addLine(output,  "INSTRUMENTATION_STATUS: class=com.example.helloworld.FailureAssumptionTest");
+        addLine(output,  "INSTRUMENTATION_STATUS: current=2");
+        addLine(output,  "INSTRUMENTATION_STATUS_CODE: 1");
+        addLine(output,  "INSTRUMENTATION_STATUS: numtests=3");
+        addLine(output,  "INSTRUMENTATION_STATUS: stream=");
+        addLine(output,  "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+        addLine(output,  "INSTRUMENTATION_STATUS: test=checkAssumptionIsSkipped");
+        addLine(output,  "INSTRUMENTATION_STATUS: class=com.example.helloworld.FailureAssumptionTest");
+        addLine(output,  "INSTRUMENTATION_STATUS: stack=org.junit.AssumptionViolatedException: got: <false>, expected: is <true>");
+        addLine(output,  "at org.junit.Assume.assumeThat(Assume.java:95)");
+        addLine(output,  "at org.junit.Assume.assumeTrue(Assume.java:41)");
+        addLine(output,  "at com.example.helloworld.FailureAssumptionTest.checkAssumptionIsSkipped(FailureAssumptionTest.java:19)");
+        addLine(output,  "at java.lang.reflect.Method.invoke(Native Method)");
+        addLine(output,  "at java.lang.reflect.Method.invoke(Method.java:372)");
+        addLine(output,  "at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)");
+        addLine(output,  "at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)");
+        addLine(output,  "at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)");
+        addLine(output,  "at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)");
+        addLine(output,  "at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)");
+        addLine(output,  "at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)");
+        addLine(output,  "at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)");
+        addLine(output,  "at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)");
+        addLine(output,  "at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)");
+        addLine(output,  "at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)");
+        addLine(output,  "at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)");
+        addLine(output,  "at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)");
+        addLine(output,  "at org.junit.runners.ParentRunner.run(ParentRunner.java:363)");
+        addLine(output,  "at org.junit.runners.Suite.runChild(Suite.java:128)");
+        addLine(output,  "at org.junit.runners.Suite.runChild(Suite.java:27)");
+        addLine(output,  "at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)");
+        addLine(output,  "at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)");
+        addLine(output,  "at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)");
+        addLine(output,  "at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)");
+        addLine(output,  "at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)");
+        addLine(output,  "at org.junit.runners.ParentRunner.run(ParentRunner.java:363)");
+        addLine(output,  "at org.junit.runner.JUnitCore.run(JUnitCore.java:137)");
+        addLine(output,  "at org.junit.runner.JUnitCore.run(JUnitCore.java:115)");
+        addLine(output,  "at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)");
+        addLine(output,  "at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:228)");
+        addLine(output,  "at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)");
+        addLine(output,  "");
+        addLine(output,  "INSTRUMENTATION_STATUS: current=2");
+        addLine(output,  "INSTRUMENTATION_STATUS_CODE: -4");
+        addLine(output,  "INSTRUMENTATION_STATUS: numtests=3");
+        addLine(output,  "INSTRUMENTATION_STATUS: stream=");
+        addLine(output,  "com.example.helloworld.HelloWorldTest:");
+        addLine(output,  "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+        addLine(output,  "INSTRUMENTATION_STATUS: test=testPreconditions");
+        addLine(output,  "INSTRUMENTATION_STATUS: class=com.example.helloworld.HelloWorldTest");
+        addLine(output,  "INSTRUMENTATION_STATUS: current=3");
+        addLine(output,  "INSTRUMENTATION_STATUS_CODE: 1");
+        addLine(output,  "INSTRUMENTATION_STATUS: numtests=3");
+        addLine(output,  "INSTRUMENTATION_STATUS: stream=.");
+        addLine(output,  "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+        addLine(output,  "INSTRUMENTATION_STATUS: test=testPreconditions");
+        addLine(output,  "INSTRUMENTATION_STATUS: class=com.example.helloworld.HelloWorldTest");
+        addLine(output,  "INSTRUMENTATION_STATUS: current=3");
+        addLine(output,  "INSTRUMENTATION_STATUS_CODE: 0");
+        addLine(output,  "INSTRUMENTATION_RESULT: stream=");
+        addLine(output,  "");
+        addLine(output,  "Time: 0.676");
+        addLine(output,  "");
+        addLine(output,  "OK (2 tests)");
+        addLine(output,  "");
+        addLine(output,  "");
+        addLine(output,  "INSTRUMENTATION_CODE: -1");
+
+
+        mMockListener.testRunStarted(RUN_NAME, 3);
+        final TestIdentifier IGNORED_ANNOTATION = new TestIdentifier(
+                "com.example.helloworld.FailureAssumptionTest",
+                "checkIgnoreTestsArePossible");
+        mMockListener.testStarted(IGNORED_ANNOTATION);
+        mMockListener.testIgnored(IGNORED_ANNOTATION);
+        mMockListener.testEnded(IGNORED_ANNOTATION, Collections.EMPTY_MAP);
+        final TestIdentifier ASSUME_FALSE = new TestIdentifier(
+                "com.example.helloworld.FailureAssumptionTest",
+                "checkAssumptionIsSkipped");
+        mMockListener.testStarted(ASSUME_FALSE);
+        mMockListener.testAssumptionFailure(EasyMock.eq(ASSUME_FALSE),
+                EasyMock.startsWith(
+                        "org.junit.AssumptionViolatedException: got: <false>, expected: is <true>"));
+        mMockListener.testEnded(ASSUME_FALSE, Collections.EMPTY_MAP);
+        final TestIdentifier HELLO_WORLD = new TestIdentifier("com.example.helloworld.HelloWorldTest",
+                "testPreconditions");
+        mMockListener.testStarted(HELLO_WORLD);
+        mMockListener.testEnded(HELLO_WORLD, Collections.EMPTY_MAP);
+        mMockListener.testRunEnded(EasyMock.eq(676L), EasyMock.anyObject(Map.class));
+
+        injectAndVerifyTestString(output.toString());
+}
+
     /**
      * Builds a common test result using TEST_NAME and TEST_CLASS.
      */
diff --git a/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index eaa6f19..523e599 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -54,7 +54,7 @@
      * Test the basic case building of the instrumentation runner command with no arguments.
      */
     public void testRun() throws Exception {
-        String expectedCmd = EasyMock.eq(String.format("am instrument -w -r  %s/%s", TEST_PACKAGE,
+        String expectedCmd = EasyMock.eq(String.format("am instrument -w -r   %s/%s", TEST_PACKAGE,
                 TEST_RUNNER));
         runAndVerify(expectedCmd);
     }
@@ -103,6 +103,20 @@
     }
 
     /**
+     * Test additional run options.
+     */
+    public void testRun_runOptions() throws Exception {
+        mRunner.setRunOptions("--no-window-animation");
+        String expectedCmd =
+                EasyMock.eq(
+                        String.format(
+                                "am instrument -w -r --no-window-animation  %s/%s",
+                                TEST_PACKAGE,
+                                TEST_RUNNER));
+        runAndVerify(expectedCmd);
+    }
+
+    /**
      * Test run when the device throws a IOException
      */
     @SuppressWarnings("unchecked")
diff --git a/ddmlib/src/test/java/com/android/ddmlib/utils/DebuggerPortsTest.java b/ddmlib/src/test/java/com/android/ddmlib/utils/DebuggerPortsTest.java
new file mode 100644
index 0000000..ac54c4b
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/utils/DebuggerPortsTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.ddmlib.utils;
+
+import junit.framework.TestCase;
+
+public class DebuggerPortsTest extends TestCase {
+    public void testNextFreePort() {
+        DebuggerPorts freePorts = new DebuggerPorts(9000);
+        assertEquals(9000, freePorts.next());
+        assertEquals(9001, freePorts.next());
+    }
+
+    public void testReleasePort() {
+        DebuggerPorts freePorts = new DebuggerPorts(9000);
+        int first = freePorts.next();
+        int second = freePorts.next();
+
+        freePorts.free(first);
+        assertEquals(first, freePorts.next());
+        assertEquals(second + 1, freePorts.next());
+
+        freePorts.free(second);
+        assertEquals(second, freePorts.next());
+    }
+}
diff --git a/device_validator/dvlib/build.gradle b/device_validator/dvlib/build.gradle
index ceea5a9..4111d48 100644
--- a/device_validator/dvlib/build.gradle
+++ b/device_validator/dvlib/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 dependencies {
diff --git a/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java b/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java
index cd89d7e..4aaa6f7 100644
--- a/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java
+++ b/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java
@@ -46,7 +46,6 @@
 import javax.xml.transform.stream.StreamSource;
 import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
 
 public class DeviceSchema {
 
diff --git a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd
index 2589e46..2017a6a 100644
--- a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd
+++ b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd
@@ -515,7 +515,9 @@
                         <xsd:enumeration value="mdpi" />
                         <xsd:enumeration value="tvdpi" />
                         <xsd:enumeration value="hdpi" />
+                        <xsd:enumeration value="280dpi" />
                         <xsd:enumeration value="xhdpi" />
+                        <xsd:enumeration value="360dpi" />
                         <xsd:enumeration value="400dpi" />
                         <xsd:enumeration value="xxhdpi" />
                         <xsd:enumeration value="560dpi" />
diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml
index 59587b8..808d6ff 100644
--- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml
+++ b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml
@@ -398,5 +398,92 @@
         </d:boot-props>
     </d:device>
 
+    <d:device>
+        <d:name>Android Wear Round Chin</d:name>
+        <d:id>wear_round_chin</d:id>
+        <d:manufacturer>Google</d:manufacturer>
+        <d:meta/>
+        <d:hardware>
+            <d:screen>
+                <d:screen-size>small</d:screen-size>
+                <d:diagonal-length>1.65</d:diagonal-length>
+                <d:pixel-density>tvdpi</d:pixel-density>
+                <d:screen-ratio>notlong</d:screen-ratio>
+                <d:dimensions>
+                    <d:x-dimension>320</d:x-dimension>
+                    <d:y-dimension>290</d:y-dimension>
+                </d:dimensions>
 
+                <d:xdpi>239.99</d:xdpi>
+                <d:ydpi>239.99</d:ydpi>
+                <d:touch>
+                    <d:multitouch>jazz-hands</d:multitouch>
+                    <d:mechanism>finger</d:mechanism>
+                    <d:screen-type>capacitive</d:screen-type>
+                </d:touch>
+            </d:screen>
+            <d:networking>
+                Bluetooth
+                Wifi
+                NFC
+            </d:networking>
+            <d:sensors>
+                Accelerometer
+                Barometer
+                Compass
+                GPS
+                Gyroscope
+                LightSensor
+                ProximitySensor
+            </d:sensors>
+            <d:mic>true</d:mic>
+            <d:keyboard>qwerty</d:keyboard>
+            <d:nav>dpad</d:nav>
+            <d:ram unit="MiB">512</d:ram>
+            <d:buttons>hard</d:buttons>
+            <d:internal-storage unit="KiB">
+                7811891
+            </d:internal-storage>
+            <d:removable-storage unit="TiB"/>
+            <d:cpu>Qualcomm Snapdragon S4 Pro</d:cpu>
+            <d:gpu>Adreno 320</d:gpu>
+            <d:abi>
+                armeabi-v7a
+            </d:abi>
+            <d:dock/>
+            <d:power-type>battery</d:power-type>
+            <d:skin>AndroidWearRound</d:skin>
+        </d:hardware>
+        <d:software>
+            <d:api-level>20-</d:api-level>
+            <d:live-wallpaper-support>true</d:live-wallpaper-support>
+            <d:bluetooth-profiles/>
+            <d:gl-version>2.0</d:gl-version>
+            <d:gl-extensions/>
+            <d:status-bar>false</d:status-bar>
+        </d:software>
+        <d:state name="Portrait">
+            <d:description>The device in portrait orientation</d:description>
+            <d:screen-orientation>port</d:screen-orientation>
+            <d:keyboard-state>keyshidden</d:keyboard-state>
+            <d:nav-state>navexposed</d:nav-state>
+        </d:state>
+        <d:state default="true" name="Landscape">
+            <d:description>The device in landscape orientation</d:description>
+            <d:screen-orientation>land</d:screen-orientation>
+            <d:keyboard-state>keyshidden</d:keyboard-state>
+            <d:nav-state>navexposed</d:nav-state>
+        </d:state>
+        <d:tag-id>android-wear</d:tag-id>
+        <d:boot-props>
+            <d:boot-prop>
+                <d:prop-name>ro.emulator.circular</d:prop-name>
+                <d:prop-value>true</d:prop-value>
+            </d:boot-prop>
+            <d:boot-prop>
+                <d:prop-name>ro.emu.win_outset_bottom_px</d:prop-name>
+                <d:prop-value>30</d:prop-value>
+            </d:boot-prop>
+        </d:boot-props>
+    </d:device>
 </d:devices>
diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml
index 54a283c..d1804fb 100755
--- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml
+++ b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml
@@ -406,5 +406,92 @@
         </d:boot-props>
     </d:device>
 
+    <d:device>
+        <d:name>Android Wear Round Chin</d:name>
+        <d:id>wear_round_chin</d:id>
+        <d:manufacturer>Google</d:manufacturer>
+        <d:meta/>
+        <d:hardware>
+            <d:screen>
+                <d:screen-size>small</d:screen-size>
+                <d:diagonal-length>1.65</d:diagonal-length>
+                <d:pixel-density>tvdpi</d:pixel-density>
+                <d:screen-ratio>notlong</d:screen-ratio>
+                <d:dimensions>
+                    <d:x-dimension>320</d:x-dimension>
+                    <d:y-dimension>290</d:y-dimension>
+                </d:dimensions>
 
+                <d:xdpi>239.99</d:xdpi>
+                <d:ydpi>239.99</d:ydpi>
+                <d:touch>
+                    <d:multitouch>jazz-hands</d:multitouch>
+                    <d:mechanism>finger</d:mechanism>
+                    <d:screen-type>capacitive</d:screen-type>
+                </d:touch>
+            </d:screen>
+            <d:networking>
+                Bluetooth
+                Wifi
+                NFC
+            </d:networking>
+            <d:sensors>
+                Accelerometer
+                Barometer
+                Compass
+                GPS
+                Gyroscope
+                LightSensor
+                ProximitySensor
+            </d:sensors>
+            <d:mic>true</d:mic>
+            <d:keyboard>qwerty</d:keyboard>
+            <d:nav>dpad</d:nav>
+            <d:ram unit="MiB">512</d:ram>
+            <d:buttons>hard</d:buttons>
+            <d:internal-storage unit="KiB">
+                7811891
+            </d:internal-storage>
+            <d:removable-storage unit="TiB"/>
+            <d:cpu>Qualcomm Snapdragon S4 Pro</d:cpu>
+            <d:gpu>Adreno 320</d:gpu>
+            <d:abi>
+                armeabi-v7a
+            </d:abi>
+            <d:dock/>
+            <d:power-type>battery</d:power-type>
+            <d:skin>AndroidWearRound</d:skin>
+        </d:hardware>
+        <d:software>
+            <d:api-level>20-</d:api-level>
+            <d:live-wallpaper-support>true</d:live-wallpaper-support>
+            <d:bluetooth-profiles/>
+            <d:gl-version>2.0</d:gl-version>
+            <d:gl-extensions/>
+            <d:status-bar>false</d:status-bar>
+        </d:software>
+        <d:state name="Portrait">
+            <d:description>The device in portrait orientation</d:description>
+            <d:screen-orientation>port</d:screen-orientation>
+            <d:keyboard-state>keyshidden</d:keyboard-state>
+            <d:nav-state>navexposed</d:nav-state>
+        </d:state>
+        <d:state default="true" name="Landscape">
+            <d:description>The device in landscape orientation</d:description>
+            <d:screen-orientation>land</d:screen-orientation>
+            <d:keyboard-state>keyshidden</d:keyboard-state>
+            <d:nav-state>navexposed</d:nav-state>
+        </d:state>
+        <d:tag-id>android-wear</d:tag-id>
+        <d:boot-props>
+            <d:boot-prop>
+                <d:prop-name>ro.emulator.circular</d:prop-name>
+                <d:prop-value>true</d:prop-value>
+            </d:boot-prop>
+            <d:boot-prop>
+                <d:prop-name>ro.emu.win_outset_bottom_px</d:prop-name>
+                <d:prop-value>30</d:prop-value>
+            </d:boot-prop>
+        </d:boot-props>
+    </d:device>
 </d:devices>
diff --git a/draw9patch/build.gradle b/draw9patch/build.gradle
index ec84cd1..b54a7f0 100644
--- a/draw9patch/build.gradle
+++ b/draw9patch/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools'
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
index d745a69..d3858ee 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
@@ -21,7 +21,6 @@
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
-import java.awt.FlowLayout;
 import java.awt.Graphics2D;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
@@ -37,7 +36,6 @@
 import java.net.URL;
 
 import javax.swing.Box;
-import javax.swing.BoxLayout;
 import javax.swing.JCheckBox;
 import javax.swing.JComponent;
 import javax.swing.JFileChooser;
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java b/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
index 1b9c73d..0d28302 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
@@ -135,7 +135,7 @@
     }
 
     private Pair<Integer> getPadding(List<Pair<Integer>> pairs) {
-        if (pairs.size() == 0) {
+        if (pairs.isEmpty()) {
             return new Pair<Integer>(0, 0);
         } else if (pairs.size() == 1) {
             if (pairs.get(0).first == 1) {
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java b/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java
index f799a18..1206dd3 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java
@@ -163,7 +163,7 @@
         both.remainderVertical = both.scaledHeight - remainderVertical;
 
         horizontalPatchesSum = 0;
-        if (patchInfo.horizontalPatches.size() > 0) {
+        if (!patchInfo.horizontalPatches.isEmpty()) {
             int start = -1;
             for (Rectangle rect : patchInfo.horizontalPatches) {
                 if (rect.x > start) {
@@ -182,7 +182,7 @@
         }
 
         verticalPatchesSum = 0;
-        if (patchInfo.verticalPatches.size() > 0) {
+        if (!patchInfo.verticalPatches.isEmpty()) {
             int start = -1;
             for (Rectangle rect : patchInfo.verticalPatches) {
                 if (rect.y > start) {
diff --git a/jack/jack-api/README.txt b/jack/jack-api/README.txt
new file mode 100644
index 0000000..1cb0166
--- /dev/null
+++ b/jack/jack-api/README.txt
@@ -0,0 +1,5 @@
+Jack-API version 0.9
+
+This is a clone of the jack-api source code so that it can be 
+build and uploaded to bintray/mavenCentral using the same release
+mechanism as the rest of the Gradle plugin libraries.
\ No newline at end of file
diff --git a/jack/jack-api/build.gradle b/jack/jack-api/build.gradle
new file mode 100644
index 0000000..d5bf9de
--- /dev/null
+++ b/jack/jack-api/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'java'
+
+group = 'com.android.tools.jack'
+archivesBaseName = 'jack-api'
+version = '0.9.0'
+
+configurations {
+  provided
+}
+
+dependencies {
+  provided 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
+compileJava.classpath += configurations.provided
+
+project.ext.pomName = 'Android Jack API'
+project.ext.pomDesc = 'API to dynamically load Jack'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+javadoc {
+    classpath += configurations.provided
+}
diff --git a/jack/jack-api/jack-api.iml b/jack/jack-api/jack-api.iml
new file mode 100644
index 0000000..00040c6
--- /dev/null
+++ b/jack/jack-api/jack-api.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/.settings" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="library" exported="" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="library" name="jsr-305" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/ConfigNotSupportedException.java b/jack/jack-api/src/main/java/com/android/jack/api/ConfigNotSupportedException.java
new file mode 100644
index 0000000..8fea150
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/ConfigNotSupportedException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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.jack.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when the requested Jack configuration for a given API version is not supported.
+ */
+public class ConfigNotSupportedException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public ConfigNotSupportedException() {
+    super();
+  }
+
+  public ConfigNotSupportedException(@Nonnull String message) {
+    super(message);
+  }
+
+  public ConfigNotSupportedException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public ConfigNotSupportedException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/JackConfig.java b/jack/jack-api/src/main/java/com/android/jack/api/JackConfig.java
new file mode 100644
index 0000000..31e56f7
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/JackConfig.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 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.jack.api;
+
+/**
+ * Allows to set a configuration for Jack.
+ */
+public interface JackConfig {
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/JackProvider.java b/jack/jack-api/src/main/java/com/android/jack/api/JackProvider.java
new file mode 100644
index 0000000..0a802ac
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/JackProvider.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.jack.api;
+
+import java.util.Collection;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Provides instances of {@link JackConfig}.
+ */
+public interface JackProvider {
+  /**
+   * Creates a {@link JackConfig} instance for an interface representing a {@link JackConfig} API
+   * version.
+   * @param cls the {@link JackConfig} API interface
+   * @return the {@link JackConfig} instance
+   * @throws ConfigNotSupportedException If no implementation is found for the given interface.
+   */
+  @Nonnull
+  <T extends JackConfig> T createConfig(@Nonnull Class<T> cls) throws ConfigNotSupportedException;
+
+  /**
+   * Returns whether an interface representing a {@link JackConfig} API version is supported.
+   *
+   * @param cls the {@link JackConfig} API interface
+   * @return <code>true</true> if the config is supported
+   */
+  @Nonnull
+  <T extends JackConfig> boolean isConfigSupported(@Nonnull Class<T> cls);
+
+  /**
+   * Gives a {@link Collection} containing supported {@link JackConfig} API versions.
+   * @return the supported {@link JackConfig} API versions
+   */
+  @Nonnull
+  Collection<Class<? extends JackConfig>> getSupportedConfigs();
+
+  /**
+   * Gives the version of this Jack compiler, summarized in one string (e.g. "1.1-rc1", "2.0-a2",
+   * ...).
+   *
+   * @return the version
+   */
+  @Nonnull
+  String getCompilerVersion();
+
+  /**
+   * Gives the release name of this Jack compiler (e.g. Arzon, Brest, ...).
+   *
+   * @return the release name
+   */
+  @Nonnull
+  String getCompilerReleaseName();
+
+  /**
+   * Gives an integer value that represents the release of this Jack compiler, relative to other
+   * releases.
+   *
+   * @return the release code
+   */
+  @Nonnegative
+  int getCompilerReleaseCode();
+
+  /**
+   * Gives an integer value that represents the sub-release of this Jack compiler, relative to other
+   * sub-releases of the same release.
+   *
+   * @return the sub-release code
+   */
+  @Nonnegative
+  int getCompilerSubReleaseCode();
+
+  /**
+   * Gives the kind of sub-release of this Jack compiler.
+   *
+   * @return the sub-release kind
+   */
+  @Nonnull
+  SubReleaseKind getCompilerSubReleaseKind();
+
+  /**
+   * The kind of sub-release.
+   */
+  public enum SubReleaseKind {
+    /**
+     * A sub-release from an engineering development, not tested, not in the code base repository.
+     */
+    ENGINEERING,
+    /**
+     * A sub-release that is not feature complete, not tested.
+     */
+    PRE_ALPHA,
+    /**
+     * A sub-release that is not feature complete, tested.
+     */
+    ALPHA,
+    /**
+     * A sub-release that is feature complete, tested, but likely contains known or unknown bugs.
+     */
+    BETA,
+    /**
+     * A pre-production sub-release, tested.
+     */
+    CANDIDATE,
+    /**
+     * A production and stable sub-release.
+     */
+    RELEASE;
+  }
+
+  /**
+   * The build ID of this Jack compiler.
+   * @return the build ID, or null if not available
+   */
+  @CheckForNull
+  String getCompilerBuildId();
+
+  /**
+   * Identify the source code base of this Jack compiler.
+   * @return the source code base, or null if not available
+   */
+  @CheckForNull
+  String getCompilerSourceCodeBase();
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01CompilationTask.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01CompilationTask.java
new file mode 100644
index 0000000..cf6141f
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01CompilationTask.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+
+/**
+ * A task allowing to run the Jack compiler once.
+ */
+public interface Api01CompilationTask {
+
+  /**
+   * Runs the Jack compiler. May be called only once.
+   * @throws CompilationException If a fatal error occurred during the compilation
+   * @throws UnrecoverableException If an error out of Jack's control occurred
+   * @throws ConfigurationException If there is an error in the configuration
+   * @throws IllegalStateException If Jack is run more than once
+   */
+  void run() throws CompilationException, UnrecoverableException, ConfigurationException,
+      IllegalStateException;
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01Config.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01Config.java
new file mode 100644
index 0000000..81e552a
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01Config.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+import com.android.jack.api.JackConfig;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A configuration for API level 01 of the Jack compiler.
+ */
+public interface Api01Config extends JackConfig {
+
+  /**
+   * Sets an {@link OutputStream} where Jack will write errors, warnings and other information.
+   * @param reporterKind The type of reporter
+   * @param reporterStream The stream where to write
+   * @throws ConfigurationException
+   */
+  void setReporter(@Nonnull ReporterKind reporterKind, @Nonnull OutputStream reporterStream)
+      throws ConfigurationException;
+
+  /**
+   * Sets the policy to follow when there is a collision between imported types.
+   * @param typeImportCollisionPolicy the collision policy for imported types
+   * @throws ConfigurationException
+   */
+  void setTypeImportCollisionPolicy(@Nonnull TypeCollisionPolicy typeImportCollisionPolicy)
+      throws ConfigurationException;
+
+  /**
+   * Sets the policy to follow when there is a collision between imported resources.
+   * @param resourceImportCollisionPolicy the collision policy for imported resources
+   * @throws ConfigurationException
+   */
+  void setResourceImportCollisionPolicy(
+      @Nonnull ResourceCollisionPolicy resourceImportCollisionPolicy) throws ConfigurationException;
+
+  /**
+   * Sets the Java source version.
+   * @param javaSourceVersion the Java source version
+   * @throws ConfigurationException
+   */
+  void setJavaSourceVersion(@Nonnull JavaSourceVersion javaSourceVersion)
+      throws ConfigurationException;
+
+  /**
+   * Sets the file where to write the obfuscation mapping. The file may already exist and will be
+   * overwritten.
+   * @param obfuscationMappingOutputFile the obfuscation mapping output file
+   * @throws ConfigurationException
+   */
+  void setObfuscationMappingOutputFile(@Nonnull File obfuscationMappingOutputFile)
+      throws ConfigurationException;
+
+  /**
+   * Sets the classpath.
+   * @param classpath The classpath as a list
+   * @throws ConfigurationException
+   */
+  void setClasspath(@Nonnull List<File> classpath) throws ConfigurationException;
+
+  /**
+   * Sets the Jack library files that will be imported into the output.
+   * @param importedJackLibraryFiles The Jack library files to import
+   * @throws ConfigurationException
+   */
+  void setImportedJackLibraryFiles(@Nonnull List<File> importedJackLibraryFiles)
+      throws ConfigurationException;
+
+  /**
+   * Sets the directories containing files to import into the output as meta-files.
+   * @param metaDirs The directories containing the meta-files
+   * @throws ConfigurationException
+   */
+  void setMetaDirs(@Nonnull List<File> metaDirs) throws ConfigurationException;
+
+  /**
+   * Sets the directories containing files to import into the output as resources.
+   * @param resourceDirs The directories containing the resources
+   * @throws ConfigurationException
+   */
+  void setResourceDirs(@Nonnull List<File> resourceDirs) throws ConfigurationException;
+
+  /**
+   * Sets the directory that will be used to store data for incremental support. This directory must
+   * already exist.
+   * @param incrementalDir The directory used for incremental data
+   * @throws ConfigurationException
+   */
+  void setIncrementalDir(@Nonnull File incrementalDir) throws ConfigurationException;
+
+  /**
+   * Sets the directory that will be used to write dex files and resources. This directory must
+   * already exist.
+   * @param outputDexDir The output directory for dex files and resources
+   * @throws ConfigurationException
+   */
+  void setOutputDexDir(@Nonnull File outputDexDir) throws ConfigurationException;
+
+  /**
+   * Sets the file where the output Jack library will be written. The file may already exist and
+   * will be overwritten.
+   * @param outputJackFile The output Jack library file
+   * @throws ConfigurationException
+   */
+  void setOutputJackFile(@Nonnull File outputJackFile) throws ConfigurationException;
+
+  /**
+   * Sets JarJar configuration files to use for repackaging.
+   * @param jarjarConfigFiles The JarJar configuration files
+   * @throws ConfigurationException
+   */
+  void setJarJarConfigFiles(@Nonnull List<File> jarjarConfigFiles) throws ConfigurationException;
+
+  /**
+   * Sets ProGuard configuration files.
+   * @param proguardConfigFiles The ProGuard configuration files
+   * @throws ConfigurationException
+   */
+  void setProguardConfigFiles(@Nonnull List<File> proguardConfigFiles)
+      throws ConfigurationException;
+
+  /**
+   * Set how much debug info should be emitted.
+   * @param debugInfoLevel The level of debug info to emit
+   * @throws ConfigurationException
+   */
+  void setDebugInfoLevel(@Nonnull DebugInfoLevel debugInfoLevel) throws ConfigurationException;
+
+  /**
+   * Sets whether to allow splitting the output in several dex files, and which method to use.
+   * @param multiDexKind the multidex kind
+   * @throws ConfigurationException
+   */
+  void setMultiDexKind(@Nonnull MultiDexKind multiDexKind) throws ConfigurationException;
+
+  /**
+   * Sets the verbosity level.
+   * @param verbosityLevel the verbosity level
+   * @throws ConfigurationException
+   */
+  void setVerbosityLevel(@Nonnull VerbosityLevel verbosityLevel) throws ConfigurationException;
+
+  /**
+   * Sets the class names of the annotation processors to run.
+   * @param processorNames Annotation processor class names
+   * @throws ConfigurationException
+   */
+  void setProcessorNames(@Nonnull List<String> processorNames) throws ConfigurationException;
+
+  /**
+   * Sets the path where to find annotation processors.
+   * @param processorPath The processor path as a list
+   * @throws ConfigurationException
+   */
+  void setProcessorPath(@Nonnull List<File> processorPath) throws ConfigurationException;
+
+  /**
+   * Sets options for the annotation processors.
+   * @param processorOptions The processor options as a map
+   * @throws ConfigurationException
+   */
+  void setProcessorOptions(@Nonnull Map<String, String> processorOptions)
+      throws ConfigurationException;
+
+  /**
+   * Sets the Java source files entries to compile.
+   * @param sourceEntries The source entries
+   * @throws ConfigurationException
+   */
+  void setSourceEntries(@Nonnull Collection<File> sourceEntries) throws ConfigurationException;
+
+  /**
+   * Sets the value for the given property.
+   * @param key The name of the property
+   * @param value The value to set the property to
+   * @throws ConfigurationException
+   */
+  void setProperty(@Nonnull String key, @Nonnull String value) throws ConfigurationException;
+
+  /**
+   * Creates an instance of the {@link Api01CompilationTask} according to this configuration.
+   * @return The {@link Api01CompilationTask}
+   * @throws ConfigurationException
+   */
+  @Nonnull
+  Api01CompilationTask getTask() throws ConfigurationException;
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ChainedException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ChainedException.java
new file mode 100644
index 0000000..1efea16
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ChainedException.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 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.jack.api.v01;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Abstract class to easily chain exceptions together.
+ *
+ * The exception can be managed like any other exception. In this case, the first one will be the
+ * only one used.
+ *
+ * Special management can use the {@link #iterator()} or the {@link #getNextException()} to browse
+ * all chained exceptions and dispatch them.
+ *
+ * See {@link ChainedExceptionBuilder} to build the chain of exceptions.
+ */
+public abstract class ChainedException extends Exception
+    implements Iterable<ChainedException> {
+  private static final long serialVersionUID = 1L;
+
+  @Nonnull
+  private String message;
+
+  @Nonnegative
+  private int count = 1;
+
+  @Nonnull
+  private ChainedException tail = this;
+
+  @CheckForNull
+  private ChainedException next = null;
+
+  public ChainedException(@Nonnull String message) {
+    super("");
+    this.message = message;
+  }
+
+  public ChainedException(@Nonnull String message, @Nonnull Throwable cause) {
+    super("", cause);
+    this.message = message;
+  }
+
+  public ChainedException(@Nonnull Throwable cause) {
+    super(cause);
+    this.message = cause.getMessage();
+  }
+
+  @Override
+  @Nonnull
+  public String getMessage() {
+    return message;
+  }
+
+  @Override
+  @Nonnull
+  public String getLocalizedMessage() {
+    return message;
+  }
+
+  public void setMessage(@Nonnull String message) {
+    this.message = message;
+  }
+
+  @Nonnull
+  protected ChainedException putAsLastExceptionOf(
+      @CheckForNull ChainedException head) {
+    if (head == null) {
+      this.tail  = this;
+      this.next  = null;
+      this.count = 1;
+
+      return this;
+    } else {
+      head.tail.next = this;
+      head.tail = this;
+      head.count++;
+
+      return head;
+    }
+  }
+
+  @CheckForNull
+  public ChainedException getNextException() {
+    return next;
+  }
+
+  @Nonnegative
+  public int getNextExceptionCount() {
+    return count;
+  }
+
+  @Override
+  @Nonnull
+  public Iterator<ChainedException> iterator() {
+    ArrayList<ChainedException> list = new ArrayList<ChainedException>(count);
+
+    ChainedException exception = this;
+    do {
+      list.add(exception);
+      exception = exception.next;
+    } while (exception != null);
+
+    return list.iterator();
+  }
+
+  /**
+   * Builder to construct a chain of exceptions.
+   */
+  public static class ChainedExceptionBuilder<T extends ChainedException> {
+    @CheckForNull
+    private T head = null;
+
+    @SuppressWarnings("unchecked")
+    public void appendException(@Nonnull T exceptions) {
+      for (ChainedException exception : exceptions) {
+        head = (T) exception.putAsLastExceptionOf(head);
+      }
+    }
+
+    public void throwIfNecessary() throws T {
+      if (head != null) {
+        throw head;
+      }
+    }
+
+    @Nonnull
+    public T getException() {
+      assert head != null;
+      return head;
+    }
+  }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/CompilationException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/CompilationException.java
new file mode 100644
index 0000000..99423a8
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/CompilationException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A fatal problem that caused Jack to abort the compilation. The problem should already have
+ * reported, so it is safe to ignore its message.
+ */
+public class CompilationException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public CompilationException() {
+    super();
+  }
+
+  public CompilationException(@Nonnull String message) {
+    super(message);
+  }
+
+  public CompilationException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public CompilationException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ConfigurationException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ConfigurationException.java
new file mode 100644
index 0000000..63b008f
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ConfigurationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.jack.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when something is wrong in Jack's configuration.
+ */
+public class ConfigurationException extends ChainedException {
+  private static final long serialVersionUID = 1L;
+
+  public ConfigurationException(@Nonnull String message) {
+    super(message);
+  }
+
+  public ConfigurationException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public ConfigurationException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/DebugInfoLevel.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/DebugInfoLevel.java
new file mode 100644
index 0000000..01db098
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/DebugInfoLevel.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+/**
+ * Various levels of debug information.
+ */
+public enum DebugInfoLevel {
+  /**
+   * No debug info.
+   */
+  NONE,
+
+  /**
+   * Line and source file debug info.
+   */
+  LINES,
+
+  /**
+   * All debug info.
+   */
+  FULL
+}
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/JavaSourceVersion.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/JavaSourceVersion.java
new file mode 100644
index 0000000..c0f9dec
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/JavaSourceVersion.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+/**
+ * Supported Java source version.
+ */
+public enum JavaSourceVersion {
+  JAVA_3, JAVA_4, JAVA_5, JAVA_6, JAVA_7
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/MultiDexKind.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/MultiDexKind.java
new file mode 100644
index 0000000..59bcf70
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/MultiDexKind.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+/**
+ * The method to use to split in several dex files.
+ */
+public enum MultiDexKind {
+  /**
+   * Do not split.
+   */
+  NONE,
+
+  /**
+   * Multidex split targeting L-or-more platforms.
+   */
+  NATIVE,
+
+  /**
+   * Multidex split targeting K-or-less platforms.
+   */
+  LEGACY
+}
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ReporterKind.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ReporterKind.java
new file mode 100644
index 0000000..5b54464
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ReporterKind.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+/**
+ * Available reporters.
+ */
+public enum ReporterKind {
+  DEFAULT, SDK
+}
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ResourceCollisionPolicy.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ResourceCollisionPolicy.java
new file mode 100644
index 0000000..ad68fd9
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ResourceCollisionPolicy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+/**
+ * How to handle resource collisions.
+ */
+public enum ResourceCollisionPolicy {
+  /**
+   * In case of collision between resources, keep the first resource that was encountered.
+   */
+  KEEP_FIRST,
+
+  /**
+   * In case of collision between resources, fail.
+   */
+  FAIL
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/TypeCollisionPolicy.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/TypeCollisionPolicy.java
new file mode 100644
index 0000000..8e19505
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/TypeCollisionPolicy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+/**
+ * How to handle type collisions.
+ */
+public enum TypeCollisionPolicy {
+  /**
+   * In case of collision between types, keep the first type that was encountered.
+   */
+  KEEP_FIRST,
+
+  /**
+   * In case of collision between types, fail.
+   */
+  FAIL
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/UnrecoverableException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/UnrecoverableException.java
new file mode 100644
index 0000000..631827f7
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/UnrecoverableException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when a major problem occurred because of an event out of control (e.g. an external process
+ * remove a temporary file used by Jack). Handling this error should only be reporting to the user
+ * and maybe just retry exactly the same thing as the one that has thrown.
+ */
+public class UnrecoverableException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public UnrecoverableException() {
+    super();
+  }
+
+  public UnrecoverableException(@Nonnull String message) {
+    super(message);
+  }
+
+  public UnrecoverableException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public UnrecoverableException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/VerbosityLevel.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/VerbosityLevel.java
new file mode 100644
index 0000000..6a88745
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/VerbosityLevel.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 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.jack.api.v01;
+
+
+/**
+ * Jack verbosity level
+ */
+public enum VerbosityLevel {
+  ERROR,
+  WARNING,
+  INFO,
+  DEBUG
+}
\ No newline at end of file
diff --git a/jack/jill-api/README.txt b/jack/jill-api/README.txt
new file mode 100644
index 0000000..f8c1fcd
--- /dev/null
+++ b/jack/jill-api/README.txt
@@ -0,0 +1,5 @@
+Jill-API version 0.9
+
+This is a clone of the jill-api source code so that it can be 
+build and uploaded to bintray/mavenCentral using the same release
+mechanism as the rest of the Gradle plugin libraries.
\ No newline at end of file
diff --git a/jack/jill-api/build.gradle b/jack/jill-api/build.gradle
new file mode 100644
index 0000000..5c0edf4
--- /dev/null
+++ b/jack/jill-api/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'java'
+
+group = 'com.android.tools.jill'
+archivesBaseName = 'jill-api'
+version = '0.9.0'
+
+configurations {
+  provided
+}
+
+dependencies {
+  provided 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
+compileJava.classpath += configurations.provided
+
+project.ext.pomName = 'Android Jill API'
+project.ext.pomDesc = 'API to dynamically load Jill'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+javadoc {
+    classpath += configurations.provided
+}
diff --git a/jack/jill-api/jill-api.iml b/jack/jill-api/jill-api.iml
new file mode 100644
index 0000000..00040c6
--- /dev/null
+++ b/jack/jill-api/jill-api.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/.settings" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="library" exported="" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="library" name="jsr-305" level="project" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/ConfigNotSupportedException.java b/jack/jill-api/src/main/java/com/android/jill/api/ConfigNotSupportedException.java
new file mode 100644
index 0000000..e4848f5
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/ConfigNotSupportedException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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.jill.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when the requested Jill configuration for a given API version is not supported.
+ */
+public class ConfigNotSupportedException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public ConfigNotSupportedException() {
+    super();
+  }
+
+  public ConfigNotSupportedException(@Nonnull String message) {
+    super(message);
+  }
+
+  public ConfigNotSupportedException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public ConfigNotSupportedException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/JillConfig.java b/jack/jill-api/src/main/java/com/android/jill/api/JillConfig.java
new file mode 100644
index 0000000..cde0c35
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/JillConfig.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 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.jill.api;
+
+/**
+ * Allows to set a configuration for Jill.
+ */
+public interface JillConfig {
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/JillProvider.java b/jack/jill-api/src/main/java/com/android/jill/api/JillProvider.java
new file mode 100644
index 0000000..30fd4e3
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/JillProvider.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 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.jill.api;
+
+import java.util.Collection;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Provides instances of {@link JillConfig}.
+ */
+public interface JillProvider {
+  /**
+   * Creates a {@link JillConfig} instance for an interface representing a {@link JillConfig} API
+   * version.
+   * @param cls the {@link JillConfig} API interface
+   * @return the {@link JillConfig} instance
+   * @throws ConfigNotSupportedException If no implementation is found for the given interface.
+   */
+  @Nonnull
+  <T extends JillConfig> T createConfig(@Nonnull Class<T> cls) throws ConfigNotSupportedException;
+
+  /**
+   * Returns whether an interface representing a {@link JillConfig} API version is supported.
+   *
+   * @param cls the {@link JillConfig} API interface
+   * @return <code>true</true> if the config is supported
+   */
+  @Nonnull
+  <T extends JillConfig> boolean isConfigSupported(@Nonnull Class<T> cls);
+
+  /**
+   * Gives a {@link Collection} containing supported {@link JillConfig} API versions.
+   * @return the supported {@link JillConfig} API versions
+   */
+  @Nonnull
+  Collection<Class<? extends JillConfig>> getSupportedConfigs();
+
+  /**
+   * Gives the version of this Jill, summarized in one string (e.g. "1.1-rc1", "2.0-a2",
+   * ...).
+   *
+   * @return the version
+   */
+  @Nonnull
+  String getTranslatorVersion();
+
+  /**
+   * Gives the release name of this Jill (e.g. Arzon, Brest, ...).
+   *
+   * @return the release name
+   */
+  @Nonnull
+  String getTranslatorReleaseName();
+
+  /**
+   * Gives an integer value that represents the release of this Jill, relative to other
+   * releases.
+   *
+   * @return the release code
+   */
+  @Nonnegative
+  int getTranslatorReleaseCode();
+
+  /**
+   * Gives an integer value that represents the sub-release of this Jill, relative to other
+   * sub-releases of the same release.
+   *
+   * @return the sub-release code
+   */
+  @Nonnegative
+  int getTranslatorSubReleaseCode();
+
+  /**
+   * Gives the kind of sub-release of this Jill.
+   *
+   * @return the sub-release kind
+   */
+  @Nonnull
+  SubReleaseKind getTranslatorSubReleaseKind();
+
+  /**
+   * The kind of sub-release.
+   */
+  public enum SubReleaseKind {
+    /**
+     * A sub-release from an engineering development, not tested, not in the code base repository.
+     */
+    ENGINEERING,
+    /**
+     * A sub-release that is not feature complete, not tested.
+     */
+    PRE_ALPHA,
+    /**
+     * A sub-release that is not feature complete, tested.
+     */
+    ALPHA,
+    /**
+     * A sub-release that is feature complete, tested, but likely contains known or unknown bugs.
+     */
+    BETA,
+    /**
+     * A pre-production sub-release, tested.
+     */
+    CANDIDATE,
+    /**
+     * A production and stable sub-release.
+     */
+    RELEASE;
+  }
+
+  /**
+   * The build ID of this Jill.
+   * @return the build ID, or null if not available
+   */
+  @CheckForNull
+  String getTranslatorBuildId();
+
+  /**
+   * Identify the source code base of this Jill.
+   * @return the source code base, or null if not available
+   */
+  @CheckForNull
+  String getTranslatorSourceCodeBase();
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01Config.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01Config.java
new file mode 100644
index 0000000..b1923bd
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01Config.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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.jill.api.v01;
+
+
+import com.android.jill.api.JillConfig;
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+* A configuration implementation for API level 01 of Jill.
+*/
+public interface Api01Config extends JillConfig {
+
+  /**
+   * Sets verbosity mode.
+   * @param isVerbose the desired verbosity mode
+   */
+  void setVerbose(boolean isVerbose) throws ConfigurationException;
+
+  /**
+   * Sets jar file to apply the Jill translation onto. The file must exist.
+   * @param input jar file to translate
+   */
+  void setInputJavaBinaryFile(@Nonnull File input) throws ConfigurationException;
+
+  /**
+   * Sets the file where the output Jack library will be written. The file may already exist and
+   * will be overwritten.
+   * @param outputJackFile The output Jack library file
+   * @throws ConfigurationException
+   */
+  void setOutputJackFile(@Nonnull File outputJackFile) throws ConfigurationException;
+
+
+  /**
+   * Sets whether debug info should be emitted.
+   * @param debugInfo the desired mode for debug info emission
+   */
+  void setDebugInfo(boolean debugInfo) throws ConfigurationException;
+
+  /**
+   * Creates an instance of the {@link Api01TranslationTask} according to this configuration.
+   * @return The {@link Api01TranslationTask}
+   */
+  @Nonnull
+  Api01TranslationTask getTask() throws ConfigurationException;
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01TranslationTask.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01TranslationTask.java
new file mode 100644
index 0000000..25f70bf
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01TranslationTask.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.jill.api.v01;
+
+
+/**
+ * A task allowing to run Jill once.
+ */
+public interface Api01TranslationTask {
+
+  /**
+   * Runs the translation task. May be called only once.
+   * @throws TranslationException If a fatal error occurred during the translation
+   * @throws IllegalStateException If the translation task is run more than once
+   */
+  void run() throws TranslationException, IllegalStateException;
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/ConfigurationException.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/ConfigurationException.java
new file mode 100644
index 0000000..76f0d87
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/ConfigurationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.jill.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when something is wrong in Jill configuration.
+ */
+public class ConfigurationException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public ConfigurationException(@Nonnull String message) {
+    super(message);
+  }
+
+  public ConfigurationException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public ConfigurationException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/TranslationException.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/TranslationException.java
new file mode 100644
index 0000000..41121b3
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/TranslationException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 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.jill.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A fatal problem that caused Jill to abort the translation. The problem should already have
+ * reported, so it is safe to ignore its message.
+ */
+public class TranslationException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public TranslationException() {
+    super();
+  }
+
+  public TranslationException(@Nonnull String message) {
+    super(message);
+  }
+
+  public TranslationException(@Nonnull String message, @Nonnull Throwable cause) {
+    super(message, cause);
+  }
+
+  public TranslationException(@Nonnull Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/layoutlib-api/build.gradle b/layoutlib-api/build.gradle
index 97c76ec..2ac4682 100644
--- a/layoutlib-api/build.gradle
+++ b/layoutlib-api/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools.layoutlib'
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java
index 16af248..e356ea6 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java
@@ -23,9 +23,7 @@
 
 import java.awt.image.BufferedImage;
 import java.io.File;
-import java.util.Collections;
 import java.util.EnumSet;
-import java.util.List;
 import java.util.Map;
 
 /**
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java
index 89f1424..2e1dfd6 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java
@@ -16,8 +16,10 @@
 
 package com.android.ide.common.rendering.api;
 
+import com.android.annotations.Nullable;
 import com.android.resources.Density;
 import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
 
 /**
@@ -35,6 +37,7 @@
     private final float mYdpi;
     private final ScreenOrientation mOrientation;
     private final ScreenSize mScreenSize;
+    private final ScreenRound mScreenRound;
 
     private final boolean mSoftwareButtons;
 
@@ -46,6 +49,7 @@
             float ydpi,
             ScreenSize screenSize,
             ScreenOrientation orientation,
+            ScreenRound screenRoundness,
             boolean softwareButtons) {
         mScreenWidth = screenWidth;
         mScreenHeight = screenHeight;
@@ -54,6 +58,7 @@
         mYdpi = ydpi;
         mScreenSize = screenSize;
         mOrientation = orientation;
+        mScreenRound = screenRoundness;
         mSoftwareButtons = softwareButtons;
     }
 
@@ -88,4 +93,20 @@
     public boolean hasSoftwareButtons() {
         return mSoftwareButtons;
     }
+
+  /**
+   * @since 15
+   */
+  @Nullable
+  public ScreenRound getScreenRoundness() {
+        return mScreenRound;
+    }
+
+    /**
+     * @since 15
+     */
+    @SuppressWarnings({"MethodMayBeStatic", "unused"})
+    public int getDensityDpi() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java
index ab6d0f2..24f11e0 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java
@@ -57,10 +57,12 @@
      * to use a different constructor or replace it with a MockView.
      * <p/>
      * This is done so that LayoutLib can continue to work on older versions of the IDE.
+     * Newer versions of LayoutLib should call {@link
+     * LayoutlibCallback#loadClass(String, Class[], Object[])} in such a case.
      *
-     * @param name The fully qualified name of the class.
+     * @param name                 The fully qualified name of the class.
      * @param constructorSignature The signature of the class to use
-     * @param constructorArgs The arguments to use on the constructor
+     * @param constructorArgs      The arguments to use on the constructor
      * @return A newly instantiated object.
      */
     Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java
index f48cbce..41e6651 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java
@@ -25,22 +25,32 @@
     /**
      * @see #ItemResourceValue(String, boolean, String, boolean)
      */
-    public ItemResourceValue(String name, boolean isFrameworkAttr, boolean isFramework) {
-        this(name, isFrameworkAttr, null, isFramework);
+    public ItemResourceValue(String name, boolean isFrameworkAttr, boolean isFrameworkStyle) {
+        this(name, isFrameworkAttr, null, isFrameworkStyle);
     }
 
     /**
+     * If the value is a reference to a framework resource or not is NOT represented with a boolean!
+     * but can be deduced with:
+     * <pre> {@code
+     *        boolean isFrameworkValue = item.isFramework() ||
+     *            item.getValue().startsWith(SdkConstants.ANDROID_PREFIX) ||
+     *            item.getValue().startsWith(SdkConstants.ANDROID_THEME_PREFIX);
+     * } </pre>
      * For {@code <item name="foo">bar</item>}, item in a style resource, the values of the
      * parameters will be as follows:
      *
-     * @param attributeName foo
+     * @param attributeName   foo
      * @param isFrameworkAttr is foo in framework namespace.
-     * @param value bar
-     * @param isFramework if the style is a framework file or project file.
+     * @param value           bar (in case of a reference, the value may include the namespace.
+     *                        if the namespace is absent, default namespace is assumed based on
+     *                        isFrameworkStyle (android namespace when isFrameworkStyle=true and app
+     *                        namespace when isFrameworkStyle=false))
+     * @param isFrameworkStyle if the style is a framework file or project file.
      */
     public ItemResourceValue(String attributeName, boolean isFrameworkAttr, String value,
-            boolean isFramework) {
-        super(null, attributeName, value, isFramework);
+            boolean isFrameworkStyle) {
+        super(null, attributeName, value, isFrameworkStyle);
         mIsFrameworkAttr = isFrameworkAttr;
     }
 
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java
index 4d4d4cb..7e45604 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java
@@ -129,6 +129,17 @@
     public static final String TAG_MISSING_ASSET = "missingasset";
 
     /**
+     * Fidelity Tag used when something went wrong in rendering text.
+     */
+    public static final String TAG_TEXT_RENDERING = "textRendering";
+
+    /**
+     * Tag for reporting messages that should not be logged in the render error panel,
+     * but silently logged to a more detailed log file.
+     */
+    public static final String TAG_INFO = "info";
+
+    /**
      * Logs a warning.
      * @param tag a tag describing the type of the warning
      * @param message the message of the warning
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java
index 04d7f63..3829d82 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java
@@ -32,7 +32,7 @@
  * Clients should use this instead of {@link IProjectCallback} to target both old and new
  * Layout Libraries.
  */
-@SuppressWarnings("deprecation")
+@SuppressWarnings({"deprecation", "MethodMayBeStatic", "unused"})
 public abstract class LayoutlibCallback implements IProjectCallback,
         com.android.layoutlib.api.IProjectCallback {
 
@@ -78,6 +78,28 @@
         return null;
     }
 
+    /**
+     * Get a ParserFactory which can be used to create XmlPullParsers.
+     * @since API 15
+     */
+    @NonNull
+    public ParserFactory getParserFactory() {
+        throw new UnsupportedOperationException("getParserFactory not supported.");
+    }
+
+    /**
+     * Find a custom class in the project.
+     * <p/>
+     * Like {@link #loadClass(String, Class[], Object[])}, but doesn't instantiate
+     * an object and just returns the class found.
+     * @param name className in binary format. (see {@link ClassLoader}.
+     * @since API 15
+     */
+    @NonNull
+    public Class<?> findClass(@NonNull String name) throws ClassNotFoundException {
+        throw new ClassNotFoundException(name + " not found.");
+    }
+
     // ------ implementation of the old interface using the new interface.
 
     @Override
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ParserFactory.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ParserFactory.java
new file mode 100644
index 0000000..7948c88
--- /dev/null
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ParserFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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.ide.common.rendering.api;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Callback used by LayoutLib to create {@link XmlPullParser}s.
+ */
+public abstract class ParserFactory {
+    /**
+     * Creates a new XmlPullParser with an optional display name.
+     *
+     * @param debugName an optional name to aid with debugging.
+     * @since API 15
+     */
+    @NonNull
+    public XmlPullParser createParser(@Nullable String debugName) throws XmlPullParserException {
+        throw new UnsupportedOperationException("createNewParser not supported.");
+    }
+}
diff --git a/layoutlib-api/src/main/java/com/android/resources/Density.java b/layoutlib-api/src/main/java/com/android/resources/Density.java
index 4f56909..15c81f5 100644
--- a/layoutlib-api/src/main/java/com/android/resources/Density.java
+++ b/layoutlib-api/src/main/java/com/android/resources/Density.java
@@ -27,6 +27,7 @@
     DPI_560("560dpi",  "560 DPI Density",  560,  1), //$NON-NLS-1$
     XXHIGH( "xxhdpi",  "XX-High Density",  480, 16), //$NON-NLS-1$
     DPI_400("400dpi",  "400 DPI Density",  400,  1), //$NON-NLS-1$
+    DPI_360("360dpi",  "360 DPI Density",  360, 23), //$NON-NLS-1$
     XHIGH(  "xhdpi",   "X-High Density",   320,  8), //$NON-NLS-1$
     DPI_280("280dpi",  "280 DPI Density",  280, 22), //$NON-NLS-1$
     HIGH(   "hdpi",    "High Density",     240,  4), //$NON-NLS-1$
@@ -140,6 +141,7 @@
         switch (this) {
             case TV:
             case DPI_280:
+            case DPI_360:
             case DPI_400:
             case DPI_560:
                 return false;
diff --git a/layoutlib-api/src/main/java/com/android/resources/ScreenRound.java b/layoutlib-api/src/main/java/com/android/resources/ScreenRound.java
new file mode 100644
index 0000000..5c7935d
--- /dev/null
+++ b/layoutlib-api/src/main/java/com/android/resources/ScreenRound.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 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.resources;
+
+/**
+ * Screen Round enum.
+ * <p/>This is used in the resource folder names.
+ */
+public enum ScreenRound implements ResourceEnum {
+    NOTROUND("notround", "Not Round", "Not Round screen"), //$NON-NLS-1$
+    ROUND(   "round",    "Round",     "Round screen"); //$NON-NLS-1$
+
+    private final String mValue;
+    private final String mShortDisplayValue;
+    private final String mLongDisplayValue;
+
+    ScreenRound(String value, String displayValue, String longDisplayValue) {
+        mValue = value;
+        mShortDisplayValue = displayValue;
+        mLongDisplayValue = longDisplayValue;
+    }
+
+    /**
+     * Returns the enum for matching the provided qualifier value.
+     * @param value The qualifier value.
+     * @return the enum for the qualifier value or null if no matching was found.
+     */
+    public static ScreenRound getEnum(String value) {
+        for (ScreenRound orient : values()) {
+            if (orient.mValue.equals(value)) {
+                return orient;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public String getResourceValue() {
+        return mValue;
+    }
+
+    @Override
+    public String getShortDisplayValue() {
+        return mShortDisplayValue;
+    }
+
+    @Override
+    public String getLongDisplayValue() {
+        return mLongDisplayValue;
+    }
+
+    public static int getIndex(ScreenRound orientation) {
+        int i = 0;
+        for (ScreenRound orient : values()) {
+            if (orient == orientation) {
+                return i;
+            }
+
+            i++;
+        }
+
+        return -1;
+    }
+
+    public static ScreenRound getByIndex(int index) {
+        ScreenRound[] values = values();
+        if (index >= 0 && index < values.length) {
+            return values[index];
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isFakeValue() {
+        return false;
+    }
+
+    @Override
+    public boolean isValidValueForDevice() {
+        return true;
+    }
+
+}
+
diff --git a/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java b/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java
index 809eae7..5d09b36 100644
--- a/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java
+++ b/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java
@@ -16,10 +16,6 @@
 
 package com.android.resources;
 
-import com.android.resources.FolderTypeRelationship;
-import com.android.resources.ResourceFolderType;
-import com.android.resources.ResourceType;
-
 import junit.framework.TestCase;
 
 public class FolderTypeRelationShipTest extends TestCase {
@@ -29,7 +25,7 @@
         // loop on all the enum, and make sure there's at least one folder type for it.
         for (ResourceType type : ResourceType.values()) {
             assertTrue(type.getDisplayName(),
-                    FolderTypeRelationship.getRelatedFolders(type).size() > 0);
+                    !FolderTypeRelationship.getRelatedFolders(type).isEmpty());
         }
     }
 
@@ -38,7 +34,7 @@
         // loop on all the enum, and make sure there's at least one res type for it.
         for (ResourceFolderType type : ResourceFolderType.values()) {
             assertTrue(type.getName(),
-                    FolderTypeRelationship.getRelatedResourceTypes(type).size() > 0);
+                    !FolderTypeRelationship.getRelatedResourceTypes(type).isEmpty());
         }
     }
 }
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
index 6690df8..ea14dac 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
@@ -77,7 +77,7 @@
      * If the extension is null, this will disable compression for all  files in assets/ and
      * res/raw/
      */
-    public final static class NoCompress {
+    public static final class NoCompress {
         String mExtension;
 
         /**
@@ -149,7 +149,7 @@
         }
     }
 
-    private final static InputPathFactory sPathFactory = new InputPathFactory() {
+    private static final InputPathFactory sPathFactory = new InputPathFactory() {
 
         @Override
         public InputPath createPath(File file, Set<String> extensionsToCheck) {
@@ -206,7 +206,7 @@
     }
 
     public void setVersioncode(String versionCode) {
-        if (versionCode.length() > 0) {
+        if (!versionCode.isEmpty()) {
             try {
                 mVersionCode = Integer.decode(versionCode);
             } catch (NumberFormatException e) {
@@ -245,7 +245,7 @@
      * @param packageName The package ID the APK should have.
      */
     public void setManifestpackage(String packageName) {
-        if (packageName != null && packageName.length() != 0) {
+        if (packageName != null && !packageName.isEmpty()) {
             mManifestPackage = packageName;
         }
     }
@@ -332,7 +332,7 @@
     }
 
     public void setresourcefilter(String filter) {
-        if (filter != null && filter.length() > 0) {
+        if (filter != null && !filter.isEmpty()) {
             mResourceFilter = filter;
         }
     }
@@ -559,7 +559,7 @@
         }
 
         // filters if needed
-        if (mResourceFilter != null && mResourceFilter.length() > 0) {
+        if (mResourceFilter != null && !mResourceFilter.isEmpty()) {
             task.createArg().setValue("-c");
             task.createArg().setValue(mResourceFilter);
         }
@@ -588,7 +588,7 @@
         }
 
         // if this is a library or there are library dependencies
-        if (mNonConstantId || (libPkgProp != null && libPkgProp.length() > 0)) {
+        if (mNonConstantId || (libPkgProp != null && !libPkgProp.isEmpty())) {
             if (mBinFolder == null) {
                 throw new BuildException(
                         "Missing attribute binFolder when compiling libraries or projects with libraries.");
@@ -607,13 +607,13 @@
             task.createArg().setValue(Integer.toString(mVersionCode));
         }
 
-        if (mVersionName != null && mVersionName.length() > 0) {
+        if (mVersionName != null && !mVersionName.isEmpty()) {
             task.createArg().setValue("--version-name");
             task.createArg().setValue(mVersionName);
         }
 
         // manifest location
-        if (mManifestFile != null && mManifestFile.length() > 0) {
+        if (mManifestFile != null && !mManifestFile.isEmpty()) {
             task.createArg().setValue("-M");
             task.createArg().setValue(mManifestFile);
         }
@@ -625,7 +625,7 @@
         }
 
         // resources locations.
-        if (mResources.size() > 0) {
+        if (!mResources.isEmpty()) {
             for (Path pathList : mResources) {
                 for (String path : pathList.list()) {
                     // This may not exists, and aapt doesn't like it, so we check first.
@@ -681,7 +681,7 @@
         }
 
         // ignore assets flag
-        if (mIgnoreAssets != null && mIgnoreAssets.length() > 0) {
+        if (mIgnoreAssets != null && !mIgnoreAssets.isEmpty()) {
             task.createArg().setValue("--ignore-assets");
             task.createArg().setValue(mIgnoreAssets);
         }
@@ -690,7 +690,7 @@
         task.createArg().setValue("--generate-dependencies");
 
         // use the proguard file
-        if (mProguardFile != null && mProguardFile.length() > 0) {
+        if (mProguardFile != null && !mProguardFile.isEmpty()) {
             task.createArg().setValue("-G");
             task.createArg().setValue(mProguardFile);
         }
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java
index 8997ad1..f5d8e6b 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java
@@ -33,7 +33,7 @@
 
 public class ApkBuilderTask extends SingleDependencyTask {
 
-    private final static Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$",
+    private static final Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$",
             Pattern.CASE_INSENSITIVE);
 
     private String mOutFolder;
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java
index 98312f9..3d40af1 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java
@@ -34,7 +34,7 @@
  */
 public class CheckEnvTask extends Task {
 
-    private final static String ANT_MIN_VERSION = "1.8.0";
+    private static final String ANT_MIN_VERSION = "1.8.0";
 
     @Override
     public void execute() {
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java b/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java
index e3952c4..51a9cf7 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java
@@ -58,7 +58,7 @@
      * Advanced version of the {@link LibraryProcessor} that provides the library properties
      * to the processor.
      */
-    public static abstract class AdvancedLibraryProcessor implements LibraryProcessor {
+    public abstract static class AdvancedLibraryProcessor implements LibraryProcessor {
 
         public abstract void processLibrary(String libRootPath, IPropertySource properties);
 
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java
index 4e89c9e..2d2ccfe 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java
@@ -83,5 +83,9 @@
         }
 
         antProject.setProperty(mName, buildToolInfo.getLocation().getAbsolutePath());
+        antProject.setProperty("aidl", buildToolInfo.getPath(BuildToolInfo.PathId.AIDL));
+        antProject.setProperty("aapt", buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));
+        antProject.setProperty("zipalign", buildToolInfo.getPath(BuildToolInfo.PathId.ZIP_ALIGN));
+        antProject.setProperty("dx", buildToolInfo.getPath(BuildToolInfo.PathId.DX));
     }
 }
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java
index f449f8d..10c6d9c 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java
@@ -68,12 +68,12 @@
 
         String libraryPackagesValue = getProject().getProperty(mLibraryPackagesRefId);
 
-        if (libraryPackagesValue != null && libraryPackagesValue.length() > 0) {
+        if (libraryPackagesValue != null && !libraryPackagesValue.isEmpty()) {
             // split the app packages.
             String[] libPackages = libraryPackagesValue.split(";");
 
             for (String libPackage : libPackages) {
-                if (libPackage.length() > 0) {
+                if (!libPackage.isEmpty()) {
                     for (String filterClass : FILTER_CLASSES) {
                         sb.append(libPackage).append('.').append(filterClass).append(',');
                     }
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java
index 813574e..d80e381 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java
@@ -46,7 +46,7 @@
     private boolean mVerbose = false;
 
     private static class LeafProcessor extends AdvancedLibraryProcessor {
-        private final static Pattern PH = Pattern.compile("^\\@\\{(.*)\\}$");
+        private static final Pattern PH = Pattern.compile("^\\@\\{(.*)\\}$");
 
         private Path mPath;
         private final String[] mLeafSegments;
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java
index 1dc33b3..3a6df9a 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java
@@ -21,7 +21,7 @@
 import com.android.annotations.Nullable;
 import com.android.sdklib.AndroidVersion;
 import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.utils.ILogger;
@@ -40,6 +40,7 @@
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathExpressionException;
@@ -194,17 +195,15 @@
         element.setPath(androidJar);
 
         // create PathElement for each optional library.
-        IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
-        if (libraries != null) {
-            HashSet<String> visitedJars = new HashSet<String>();
-            for (IOptionalLibrary library : libraries) {
-                String jarPath = library.getJarPath();
-                if (visitedJars.contains(jarPath) == false) {
-                    visitedJars.add(jarPath);
+        List<OptionalLibrary> libraries = androidTarget.getAdditionalLibraries();
+        HashSet<File> visitedJars = new HashSet<File>();
+        for (OptionalLibrary library : libraries) {
+            File jarFile = library.getJar();
+            if (!visitedJars.contains(jarFile)) {
+                visitedJars.add(jarFile);
 
-                    element = bootclasspath.createPathElement();
-                    element.setPath(jarPath);
-                }
+                element = bootclasspath.createPathElement();
+                element.setPath(jarFile.getAbsolutePath());
             }
         }
 
@@ -260,7 +259,7 @@
                 antProject.setProperty(mMinSdkVersionOut,
                         Integer.toString(androidVersion.getApiLevel()));
 
-            } else if (value.length() > 0) {
+            } else if (!value.isEmpty()) {
                 // for normal platform, we'll only display warnings if the value is lower or higher
                 // than the target api level.
                 // First convert to an int.
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java
index 143bc35..e7b6f53 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java
@@ -89,7 +89,7 @@
                     ":" + AndroidManifest.ATTRIBUTE_TARGET_PACKAGE,
                     new InputSource(new FileInputStream(manifest)));
 
-            if (value != null && value.length() > 0) {
+            if (value != null && !value.isEmpty()) {
                 System.out.println("Project Type: Self-Tested Application");
 
                 antProject.setProperty(mProjectTypeOut, "test-app");
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java
index 283a4b2..762c827 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java
@@ -19,7 +19,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.utils.ILogger;
@@ -33,6 +33,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Task to resolve the target of the current Android uiautomator project.
@@ -114,7 +115,7 @@
 
         // display the project info
         System.out.println(    "Project Target:   " + androidTarget.getName());
-        if (androidTarget.isPlatform() == false) {
+        if (!androidTarget.isPlatform()) {
             System.out.println("Vendor:           " + androidTarget.getVendor());
             System.out.println("Platform Version: " + androidTarget.getVersionName());
         }
@@ -141,17 +142,15 @@
         element.setPath(uiAutomatorJar);
 
         // create PathElement for each optional library.
-        IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
-        if (libraries != null) {
-            HashSet<String> visitedJars = new HashSet<String>();
-            for (IOptionalLibrary library : libraries) {
-                String jarPath = library.getJarPath();
-                if (visitedJars.contains(jarPath) == false) {
-                    visitedJars.add(jarPath);
+        List<OptionalLibrary> libraries = androidTarget.getAdditionalLibraries();
+        HashSet<File> visitedJars = new HashSet<File>();
+        for (OptionalLibrary library : libraries) {
+            File jarPath = library.getJar();
+            if (!visitedJars.contains(jarPath)) {
+                visitedJars.add(jarPath);
 
-                    element = compileclasspath.createPathElement();
-                    element.setPath(jarPath);
-                }
+                element = compileclasspath.createPathElement();
+                element.setPath(jarPath.getAbsolutePath());
             }
         }
 
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java
index c7d8853..60b2e50 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java
@@ -73,7 +73,7 @@
     private int mTargetApi = 0;
     private boolean mSupportMode = false;
 
-    public enum OptLevel { O0, O1, O2, O3 };
+    public enum OptLevel { O0, O1, O2, O3 }
 
     private OptLevel mOptLevel;
     private boolean mDebug = false;
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java
index 6c29d68..60cdff0 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java
@@ -137,7 +137,7 @@
         }
     }
 
-    private final static class NullZipFilter implements IZipEntryFilter {
+    private static final class NullZipFilter implements IZipEntryFilter {
 
         @Override
         public boolean checkEntry(String archivePath) throws ZipAbortException {
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java
index e0dc178..08f86bf 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java
@@ -38,7 +38,7 @@
         InputPath createPath(File file, Set<String> extensionsToCheck);
     }
 
-    private final static InputPathFactory sDefaultFactory = new InputPathFactory() {
+    private static final InputPathFactory sDefaultFactory = new InputPathFactory() {
         @Override
         public InputPath createPath(File file, Set<String> extensionsToCheck) {
             return new InputPath(file, extensionsToCheck);
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java b/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java
index c93b193..426ec25 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java
@@ -55,7 +55,7 @@
         String sdkOsPath = antProject.getProperty(ProjectProperties.PROPERTY_SDK);
 
         // check if it's valid and exists
-        if (sdkOsPath == null || sdkOsPath.length() == 0) {
+        if (sdkOsPath == null || sdkOsPath.isEmpty()) {
             throw new BuildException("SDK Location is not set.");
         }
 
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java
index b6de469..c3f3242 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java
@@ -76,7 +76,7 @@
 
             String file = mManifestFile.list()[0];
             String result = xpath.evaluate(mExpression, new InputSource(new FileInputStream(file)));
-            if (result.length() == 0 && mDefault != null) {
+            if (result.isEmpty() && mDefault != null) {
                 result = mDefault;
             }
 
diff --git a/lint/cli/.classpath b/lint/cli/.classpath
index b1e76db..f9b6945 100644
--- a/lint/cli/.classpath
+++ b/lint/cli/.classpath
@@ -3,6 +3,6 @@
 	<classpathentry kind="src" path="src/main/java"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/lint-checks"/>
-	<classpathentry exported="true" kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4-sources.jar"/>
+	<classpathentry exported="true" kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2-sources.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/lint/cli/build.gradle b/lint/cli/build.gradle
index 9399b0c..7d7fe85 100644
--- a/lint/cli/build.gradle
+++ b/lint/cli/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools.lint'
@@ -16,11 +8,7 @@
 
 dependencies {
     compile project(':base:lint-checks')
-    compile 'org.eclipse.jdt.core.compiler:ecj:4.4'
-
-    testCompile project(':base:lint-tests')
-    testCompile 'org.mockito:mockito-all:1.9.5'
-    testCompile 'org.codehaus.groovy:groovy-all:2.2.1'
+    compile 'org.eclipse.jdt.core.compiler:ecj:4.4.2'
 }
 
 sourceSets {
diff --git a/lint/cli/etc/lint b/lint/cli/etc/lint
index 4a09eb9..3d2f9f4 100755
--- a/lint/cli/etc/lint
+++ b/lint/cli/etc/lint
@@ -66,7 +66,7 @@
 jarpath="$frameworkdir/$jarfile"
 
 exec "$javaCmd" \
-    -Xmx512m $os_opts $java_debug \
+    -Xmx1024m $os_opts $java_debug \
     -Dcom.android.tools.lint.bindir="$progdir" \
     -Djava.awt.headless=true \
     -classpath "$jarpath" \
diff --git a/lint/cli/etc/lint.bat b/lint/cli/etc/lint.bat
index ff0bb52..31dd760 100755
--- a/lint/cli/etc/lint.bat
+++ b/lint/cli/etc/lint.bat
@@ -54,5 +54,5 @@
 set jarpath=%frameworkdir%\%jarfile%
 set javaextdirs=%frameworkdir%
 
-call "%java_exe%" %java_debug% -Xmx512m "-Dcom.android.tools.lint.bindir=%prog_dir%" "-Dcom.android.tools.lint.workdir=%work_dir%" -Djava.awt.headless=true -classpath "%jarpath%" com.android.tools.lint.Main %*
+call "%java_exe%" %java_debug% -Xmx1024m "-Dcom.android.tools.lint.bindir=%prog_dir%" "-Dcom.android.tools.lint.workdir=%work_dir%" -Djava.awt.headless=true -classpath "%jarpath%" com.android.tools.lint.Main %*
 
diff --git a/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java b/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java
index 9103597..090e3f1 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java
@@ -23,6 +23,7 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
 import com.android.sdklib.IAndroidTarget;
 import com.android.tools.lint.client.api.JavaParser;
 import com.android.tools.lint.client.api.LintClient;
@@ -93,6 +94,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
 import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
@@ -106,6 +108,7 @@
 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -551,6 +554,11 @@
         } else if (nativeNode instanceof ExplicitConstructorCall) {
             return resolve(((ExplicitConstructorCall) nativeNode).binding);
         } else if (nativeNode instanceof Annotation) {
+            AnnotationBinding compilerAnnotation =
+                    ((Annotation) nativeNode).getCompilerAnnotation();
+            if (compilerAnnotation != null) {
+                return new EcjResolvedAnnotation(compilerAnnotation);
+            }
             return resolve(((Annotation) nativeNode).resolvedType);
         } else if (nativeNode instanceof AbstractMethodDeclaration) {
             return resolve(((AbstractMethodDeclaration) nativeNode).binding);
@@ -558,7 +566,16 @@
             if (nativeNode instanceof LocalDeclaration) {
                 return resolve(((LocalDeclaration) nativeNode).binding);
             } else if (nativeNode instanceof FieldDeclaration) {
-                return resolve(((FieldDeclaration) nativeNode).binding);
+                FieldDeclaration fieldDeclaration = (FieldDeclaration) nativeNode;
+                if (fieldDeclaration.initialization instanceof AllocationExpression) {
+                    AllocationExpression allocation =
+                            (AllocationExpression)fieldDeclaration.initialization;
+                    if (allocation.binding != null) {
+                        // Field constructor call: this is an enum constant.
+                        return new EcjResolvedMethod(allocation.binding);
+                    }
+                }
+                return resolve(fieldDeclaration.binding);
             }
         }
 
@@ -747,17 +764,39 @@
         return new DefaultTypeDescriptor(fqn);
     }
 
+    /** Computes the super method, if any, given a method binding */
+    private static MethodBinding findSuperMethodBinding(@NonNull MethodBinding binding) {
+        try {
+            ReferenceBinding superclass = binding.declaringClass.superclass();
+            while (superclass != null) {
+                MethodBinding[] methods = superclass.getMethods(binding.selector,
+                        binding.parameters.length);
+                for (MethodBinding method : methods) {
+                    if (method.areParameterErasuresEqual(binding)) {
+                        return method;
+                    }
+                }
+
+                superclass = superclass.superclass();
+            }
+        } catch (Exception ignore) {
+            // Work around ECJ bugs; see https://code.google.com/p/android/issues/detail?id=172268
+        }
+
+        return null;
+    }
+
     @NonNull
     private static Collection<ResolvedAnnotation> merge(
             @Nullable Collection<ResolvedAnnotation> first,
             @Nullable Collection<ResolvedAnnotation> second) {
-        if (first == null) {
+        if (first == null || first.isEmpty()) {
             if (second == null) {
                 return Collections.emptyList();
             } else {
                 return second;
             }
-        } else if (second == null) {
+        } else if (second == null || second.isEmpty()) {
             return first;
         } else {
             int size = first.size() + second.size();
@@ -972,52 +1011,81 @@
             return mBinding.isConstructor();
         }
 
+        @Override
+        @Nullable
+        public ResolvedMethod getSuperMethod() {
+            MethodBinding superBinding = findSuperMethodBinding(mBinding);
+            if (superBinding != null) {
+                return new EcjResolvedMethod(superBinding);
+            }
+
+            return null;
+        }
+
         @NonNull
         @Override
         public Iterable<ResolvedAnnotation> getAnnotations() {
-            List<ResolvedAnnotation> compiled = null;
-            AnnotationBinding[] annotations = mBinding.getAnnotations();
-            int count = annotations.length;
-            if (count > 0) {
-                compiled = Lists.newArrayListWithExpectedSize(count);
-                for (AnnotationBinding annotation : annotations) {
-                    if (annotation != null) {
-                        compiled.add(new EcjResolvedAnnotation(annotation));
+            List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(4);
+            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+
+            MethodBinding binding = this.mBinding;
+            while (binding != null) {
+                AnnotationBinding[] annotations = binding.getAnnotations();
+                int count = annotations.length;
+                if (count > 0) {
+                    for (AnnotationBinding annotation : annotations) {
+                        if (annotation != null) {
+                            all.add(new EcjResolvedAnnotation(annotation));
+                        }
                     }
                 }
+
+                // Look for external annotations
+                Collection<ResolvedAnnotation> external = manager.getAnnotations(
+                        new EcjResolvedMethod(binding));
+                if (external != null) {
+                    all.addAll(external);
+                }
+
+                binding = findSuperMethodBinding(binding);
             }
 
-            // Look for external annotations
-            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
-            Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
-
-            return merge(compiled, external);
+            return all;
         }
 
         @NonNull
         @Override
         public Iterable<ResolvedAnnotation> getParameterAnnotations(int index) {
-            List<ResolvedAnnotation> compiled = null;
-            AnnotationBinding[][] parameterAnnotations = mBinding.getParameterAnnotations();
-            if (parameterAnnotations != null &&
-                    index >= 0 && index < parameterAnnotations.length) {
-                AnnotationBinding[] annotations = parameterAnnotations[index];
-                int count = annotations.length;
-                if (count > 0) {
-                    compiled = Lists.newArrayListWithExpectedSize(count);
-                    for (AnnotationBinding annotation : annotations) {
-                        if (annotation != null) {
-                            compiled.add(new EcjResolvedAnnotation(annotation));
+            List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(4);
+            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+
+            MethodBinding binding = this.mBinding;
+            while (binding != null) {
+                AnnotationBinding[][] parameterAnnotations = binding.getParameterAnnotations();
+                if (parameterAnnotations != null &&
+                        index >= 0 && index < parameterAnnotations.length) {
+                    AnnotationBinding[] annotations = parameterAnnotations[index];
+                    int count = annotations.length;
+                    if (count > 0) {
+                        for (AnnotationBinding annotation : annotations) {
+                            if (annotation != null) {
+                                all.add(new EcjResolvedAnnotation(annotation));
+                            }
                         }
                     }
                 }
+
+                // Look for external annotations
+                Collection<ResolvedAnnotation> external = manager.getAnnotations(
+                        new EcjResolvedMethod(binding), index);
+                if (external != null) {
+                    all.addAll(external);
+                }
+
+                binding = findSuperMethodBinding(binding);
             }
 
-            // Look for external annotations
-            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
-            Collection<ResolvedAnnotation> external = manager.getAnnotations(this, index);
-
-            return merge(compiled, external);
+            return all;
         }
 
         @Override
@@ -1030,6 +1098,17 @@
             return mBinding.toString();
         }
 
+        @Override
+        public boolean isInPackage(@NonNull String pkgName, boolean includeSubPackages) {
+            PackageBinding pkg = mBinding.declaringClass.getPackage();
+            if (pkg != null) {
+                return includeSubPackages ?
+                        startsWithCompound(pkgName, pkg.compoundName) :
+                        equalsCompound(pkgName, pkg.compoundName);
+            }
+            return false;
+        }
+
         @SuppressWarnings("RedundantIfStatement")
         @Override
         public boolean equals(Object o) {
@@ -1225,26 +1304,94 @@
         @NonNull
         @Override
         public Iterable<ResolvedAnnotation> getAnnotations() {
-            List<ResolvedAnnotation> compiled = null;
+            List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(2);
+            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+
             if (mBinding instanceof ReferenceBinding) {
                 ReferenceBinding cls = (ReferenceBinding) mBinding;
-                AnnotationBinding[] annotations = cls.getAnnotations();
-                int count = annotations.length;
-                if (count > 0) {
-                    compiled = Lists.newArrayListWithExpectedSize(count);
-                    for (AnnotationBinding annotation : annotations) {
-                        if (annotation != null) {
-                            compiled.add(new EcjResolvedAnnotation(annotation));
+                while (cls != null) {
+                    AnnotationBinding[] annotations = cls.getAnnotations();
+                    int count = annotations.length;
+                    if (count > 0) {
+                        all = Lists.newArrayListWithExpectedSize(count);
+                        for (AnnotationBinding annotation : annotations) {
+                            if (annotation != null) {
+                                all.add(new EcjResolvedAnnotation(annotation));
+                            }
                         }
                     }
+
+                    // Look for external annotations
+                    Collection<ResolvedAnnotation> external = manager.getAnnotations(
+                            new EcjResolvedClass(cls));
+                    if (external != null) {
+                        all.addAll(external);
+                    }
+
+                    cls = cls.superclass();
+                }
+            } else {
+                Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
+                if (external != null) {
+                    all.addAll(external);
+                }
+            }
+
+            return all;
+        }
+
+        @NonNull
+        @Override
+        public Iterable<ResolvedField> getFields(boolean includeInherited) {
+            if (mBinding instanceof ReferenceBinding) {
+                ReferenceBinding cls = (ReferenceBinding) mBinding;
+                if (includeInherited) {
+                    List<ResolvedField> result = null;
+                    while (cls != null) {
+                        FieldBinding[] fields = cls.fields();
+                        if (fields != null) {
+                            int count = fields.length;
+                            if (count > 0) {
+                                if (result == null) {
+                                    result = Lists.newArrayListWithExpectedSize(count);
+                                }
+                                for (FieldBinding field : fields) {
+                                    // See if this field looks like it's masked
+                                    boolean masked = false;
+                                    for (ResolvedField f : result) {
+                                        FieldBinding mb = ((EcjResolvedField) f).mBinding;
+                                        if (Arrays.equals(mb.readableName(),
+                                                field.readableName())) {
+                                            masked = true;
+                                            break;
+                                        }
+                                    }
+                                    if (masked) {
+                                        continue;
+                                    }
+
+                                    result.add(new EcjResolvedField(field));
+                                }
+                            }
+                        }
+                        cls = cls.superclass();
+                    }
+
+                    return result != null ? result : Collections.<ResolvedField>emptyList();
+                } else {
+                    FieldBinding[] fields = cls.fields();
+                    if (fields != null) {
+                        int count = fields.length;
+                        List<ResolvedField> result = Lists.newArrayListWithExpectedSize(count);
+                        for (FieldBinding field : fields) {
+                            result.add(new EcjResolvedField(field));
+                        }
+                        return result;
+                    }
                 }
             }
 
-            // Look for external annotations
-            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
-            Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
-
-            return merge(compiled, external);
+            return Collections.emptyList();
         }
 
         @Override
@@ -1272,6 +1419,12 @@
             return null;
         }
 
+        @Nullable
+        @Override
+        public ResolvedPackage getPackage() {
+            return new EcjResolvedPackage(mBinding.getPackage());
+        }
+
         @Override
         public int getModifiers() {
             if (mBinding instanceof ReferenceBinding) {
@@ -1288,6 +1441,17 @@
             return getName();
         }
 
+        @Override
+        public boolean isInPackage(@NonNull String pkgName, boolean includeSubPackages) {
+            PackageBinding pkg = mBinding.getPackage();
+            if (pkg != null) {
+                return includeSubPackages ?
+                        startsWithCompound(pkgName, pkg.compoundName) :
+                        equalsCompound(pkgName, pkg.compoundName);
+            }
+            return false;
+        }
+
         @SuppressWarnings("RedundantIfStatement")
         @Override
         public boolean equals(Object o) {
@@ -1313,6 +1477,67 @@
         }
     }
 
+    // "package-info" as a char
+    private static final char[] PACKAGE_INFO_CHARS = new char[] {
+            'p', 'a', 'c', 'k', 'a', 'g', 'e', '-', 'i', 'n', 'f', 'o'
+    };
+
+    private class EcjResolvedPackage extends ResolvedPackage {
+        private final PackageBinding mBinding;
+
+        public EcjResolvedPackage(PackageBinding binding) {
+            mBinding = binding;
+        }
+
+        @NonNull
+        @Override
+        public String getName() {
+            return new String(mBinding.readableName());
+        }
+
+        @Override
+        public String getSignature() {
+            return getName();
+        }
+
+        @NonNull
+        @Override
+        public Iterable<ResolvedAnnotation> getAnnotations() {
+            List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(2);
+
+            AnnotationBinding[] annotations = mBinding.getAnnotations();
+            int count = annotations.length;
+            if (count == 0) {
+                Binding pkgInfo = mBinding.getTypeOrPackage(PACKAGE_INFO_CHARS);
+                if (pkgInfo != null) {
+                    annotations = pkgInfo.getAnnotations();
+                }
+                count = annotations.length;
+            }
+            if (count > 0) {
+                for (AnnotationBinding annotation : annotations) {
+                    if (annotation != null) {
+                        all.add(new EcjResolvedAnnotation(annotation));
+                    }
+                }
+            }
+
+            // Merge external annotations
+            ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+            Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
+            if (external != null) {
+                all.addAll(external);
+            }
+
+            return all;
+        }
+
+        @Override
+        public int getModifiers() {
+            return 0;
+        }
+    }
+
     private class EcjResolvedField extends ResolvedField {
         private FieldBinding mBinding;
 
@@ -1383,6 +1608,17 @@
             return mBinding.toString();
         }
 
+        @Override
+        public boolean isInPackage(@NonNull String pkgName, boolean includeSubPackages) {
+            PackageBinding pkg = mBinding.declaringClass.getPackage();
+            if (pkg != null) {
+                return includeSubPackages ?
+                        startsWithCompound(pkgName, pkg.compoundName) :
+                        equalsCompound(pkgName, pkg.compoundName);
+            }
+            return false;
+        }
+
         @SuppressWarnings("RedundantIfStatement")
         @Override
         public boolean equals(Object o) {
@@ -1740,8 +1976,11 @@
     }
 
     @Nullable
-    private static Object getConstantValue(@Nullable Object value) {
+    private Object getConstantValue(@Nullable Object value) {
         if (value instanceof Constant) {
+            if (value == Constant.NotAConstant) {
+                return null;
+            }
             if (value instanceof StringConstant) {
                 return ((StringConstant) value).stringValue();
             } else if (value instanceof IntConstant) {
@@ -1786,6 +2025,8 @@
 
                 return list.toArray();
             }
+        } else if (value instanceof AnnotationBinding) {
+            return new EcjResolvedAnnotation((AnnotationBinding) value);
         }
 
         return value;
@@ -1805,4 +2046,79 @@
 
         return true;
     }
+
+    /**
+     * Does the given compound name match the given string?
+     * <p>
+     * TODO: Check if ECJ already has this as a utility somewhere
+     */
+    @VisibleForTesting
+    static boolean startsWithCompound(@NonNull String name, @NonNull char[][] compoundName) {
+        int length = name.length();
+        if (length == 0) {
+            return false;
+        }
+        int index = 0;
+        for (int i = 0, n = compoundName.length; i < n; i++) {
+            char[] o = compoundName[i];
+            for (int j = 0, m = o.length; j < m; j++) {
+                if (index == length) {
+                    return false; // Don't allow prefix in a compound name
+                }
+                if (name.charAt(index) != o[j]) {
+                    return false;
+                }
+                index++;
+            }
+            if (i < n - 1) {
+                if (index == length) {
+                    return true;
+                }
+                if (name.charAt(index) != '.') {
+                    return false;
+                }
+                index++;
+                if (index == length) {
+                    return true;
+                }
+            }
+        }
+
+        return index == length;
+    }
+
+    @VisibleForTesting
+    static boolean equalsCompound(@NonNull String name, @NonNull char[][] compoundName) {
+        int length = name.length();
+        if (length == 0) {
+            return false;
+        }
+        int index = 0;
+        for (int i = 0, n = compoundName.length; i < n; i++) {
+            char[] o = compoundName[i];
+            for (int j = 0, m = o.length; j < m; j++) {
+                if (index == length) {
+                    return false; // Don't allow prefix in a compound name
+                }
+                if (name.charAt(index) != o[j]) {
+                    return false;
+                }
+                index++;
+            }
+            if (i < n - 1) {
+                if (index == length) {
+                    return false;
+                }
+                if (name.charAt(index) != '.') {
+                    return false;
+                }
+                index++;
+                if (index == length) {
+                    return false;
+                }
+            }
+        }
+
+        return index == length;
+    }
 }
\ No newline at end of file
diff --git a/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java b/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java
index 5154222..0cf2eff 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java
@@ -21,6 +21,7 @@
 import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP;
 import static com.android.SdkConstants.VALUE_FALSE;
 import static com.android.SdkConstants.VALUE_TRUE;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
@@ -35,6 +36,7 @@
 import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
 import com.android.tools.lint.client.api.JavaParser.ResolvedField;
 import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaParser.ResolvedPackage;
 import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.LintUtils;
@@ -337,6 +339,29 @@
         return null;
     }
 
+    @Nullable
+    public ResolvedAnnotation getAnnotation(@NonNull ResolvedPackage pkg, @NonNull String type) {
+        for (AnnotationsDatabase database : mDatabases) {
+            ResolvedAnnotation annotation = database.getAnnotation(pkg, type);
+            if (annotation != null) {
+                return annotation;
+            }
+        }
+
+        return null;
+    }
+    @Nullable
+    public Collection<ResolvedAnnotation> getAnnotations(@NonNull ResolvedPackage pkg) {
+        for (AnnotationsDatabase database : mDatabases) {
+            Collection<ResolvedAnnotation> annotations = database.getAnnotations(pkg);
+            if (annotations != null) {
+                return annotations;
+            }
+        }
+
+        return null;
+    }
+
     // ---- Reading from storage ----
 
     private static final Pattern XML_SIGNATURE = Pattern.compile(
@@ -371,7 +396,8 @@
      * */
     static class AnnotationsDatabase {
         AnnotationsDatabase(@NonNull File file) throws IOException {
-            if (file.getPath().endsWith(DOT_JAR)) {
+            String path = file.getPath();
+            if (path.endsWith(DOT_JAR) || path.endsWith(FN_ANNOTATIONS_ZIP)) {
                 initializeFromJar(file);
             } else {
                 assert file.isDirectory() : file;
@@ -486,6 +512,35 @@
         }
 
         @Nullable
+        public ResolvedAnnotation getAnnotation(@NonNull ResolvedPackage pkg, @NonNull String type) {
+            ClassInfo c = findPackage(pkg);
+
+            if (c == null) {
+                return null;
+            }
+
+            if (c.annotations != null) {
+                for (ResolvedAnnotation annotation : c.annotations) {
+                    if (type.equals(annotation.getSignature())) {
+                        return annotation;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        @Nullable
+        public List<ResolvedAnnotation> getAnnotations(@NonNull ResolvedPackage pkg) {
+            ClassInfo c = findPackage(pkg);
+            if (c == null) {
+                return null;
+            }
+
+            return c.annotations;
+        }
+
+        @Nullable
         public ResolvedAnnotation getAnnotation(@NonNull ResolvedField field, @NonNull String type) {
             FieldInfo f = findField(field);
 
@@ -616,6 +671,10 @@
             return mClassMap.get(cls.getName());
         }
 
+        private ClassInfo findPackage(@NonNull ResolvedPackage pkg) {
+            return mClassMap.get(pkg.getName() +".package-info");
+        }
+
         @Nullable
         private MethodInfo findMethod(@NonNull ResolvedMethod method) {
             ClassInfo c = findClass(method.getContainingClass());
@@ -901,20 +960,6 @@
             public List<Value> getValues() {
                 return mValues == null ? Collections.<Value>emptyList() : mValues;
             }
-
-            @Nullable
-            @Override
-            public Object getValue(@NonNull String name) {
-                if (mValues != null) {
-                    for (Value value : mValues) {
-                        if (name.equals(value.name)) {
-                            return value.value;
-                        }
-                    }
-                }
-
-                return null;
-            }
         }
 
         private Map<String, ResolvedExternalAnnotation> mMarkerAnnotations = Maps.newHashMapWithExpectedSize(30);
@@ -933,7 +978,11 @@
             annotation = new ResolvedExternalAnnotation(name);
 
             List<Element> valueElements = getChildren(annotationElement);
-            if (valueElements.isEmpty()) {
+            if (valueElements.isEmpty()
+                    // Permission annotations are sometimes used as marker annotations (on
+                    // parameters) but that shouldn't let us conclude that any future
+                    // permission annotations are
+                    && !name.startsWith(PERMISSION_ANNOTATION)) {
                 mMarkerAnnotations.put(name, annotation);
                 return annotation;
             }
@@ -951,20 +1000,31 @@
                             value = false;
                         } else if (valueString.startsWith("\"") && valueString.endsWith("\"") &&
                                 valueString.length() >= 2) {
-                            value = valueString.substring(1, valueString.length() - 2);
+                            value = valueString.substring(1, valueString.length() - 1);
                         } else if (valueString.startsWith("{") && valueString.endsWith("}")) {
                             // Array of values
-                            String listString = valueString.substring(1, valueString.length() - 2);
-                            // We can't actually know; this could be for example
-                            //  {java.util.zip.ZipEntry.STORED, java.util.zip.ZipEntry.DEFLATED}
-                            // and we don't know the types of these
-                            // For now, initialize the fields to the string names
+                            String listString = valueString.substring(1, valueString.length() - 1);
+                            // We don't know the types, but we'll assume that they're either
+                            // all strings (the most common array type in our annotations), or
+                            // field references. We can't know the types of the fields; it's
+                            // not part of the annotation metadata. We'll place them in an Object[]
+                            // for now.
+                            boolean allStrings = true;
                             Splitter splitter = Splitter.on(',').omitEmptyStrings().trimResults();
                             List<Object> result = Lists.newArrayList();
                             for (String reference : splitter.split(listString)) {
-                                result.add(new ResolvedExternalField(reference));
+                                if (reference.startsWith("\"")) {
+                                    result.add(reference.substring(1, reference.length() - 1));
+                                } else {
+                                    result.add(new ResolvedExternalField(reference));
+                                    allStrings = false;
+                                }
                             }
-                            value = result.toArray();
+                            if (allStrings) {
+                                value = result.toArray(new String[result.size()]);
+                            } else {
+                                value = result.toArray();
+                            }
 
                             // We don't know the actual type of these fields; we'll assume they're
                             // a special form of
diff --git a/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java b/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java
index bd9bc62..c71b5a0 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java
@@ -472,7 +472,7 @@
 
                 // See if any projects disable this warning
                 for (Project project : projects) {
-                    if (!project.getConfiguration().isEnabled(issue)) {
+                    if (!project.getConfiguration(null).isEnabled(issue)) {
                         map.put(issue, "Project lint.xml file");
                         break;
                     }
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
index ad11229..8bcbedf 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
@@ -182,8 +182,9 @@
         return new LintCliXmlParser();
     }
 
+    @NonNull
     @Override
-    public Configuration getConfiguration(@NonNull Project project) {
+    public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
         return new CliConfiguration(getConfiguration(), project, mFlags.isFatalOnly());
     }
 
@@ -545,7 +546,7 @@
             if (!isSuppressed(IssueRegistry.LINT_ERROR)) {
                 report(new Context(mDriver, project, project, project.getDir()),
                         IssueRegistry.LINT_ERROR,
-                        project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
+                        project.getConfiguration(mDriver).getSeverity(IssueRegistry.LINT_ERROR),
                         location, message, TextFormat.RAW);
             }
         } else {
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java b/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java
index 7165d9b..61d3a37 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 import com.android.tools.lint.client.api.IssueRegistry;
 import com.android.tools.lint.client.api.XmlParser;
+import com.android.tools.lint.detector.api.DefaultPosition;
 import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Location.Handle;
 import com.android.tools.lint.detector.api.Position;
@@ -43,14 +44,6 @@
  * It also catches and reports parser errors as lint errors.
  */
 public class LintCliXmlParser extends XmlParser {
-    private final PositionXmlParser mParser = new PositionXmlParser() {
-        @NonNull
-        @Override
-        protected OffsetPosition createPosition(int line, int column, int offset) {
-            return new OffsetPosition(line, column, offset);
-        }
-    };
-
     @Override
     public Document parseXml(@NonNull XmlContext context) {
         String xml = null;
@@ -58,7 +51,7 @@
             // Do we need to provide an input stream for encoding?
             xml = context.getContents();
             if (xml != null) {
-                return mParser.parse(xml);
+                return PositionXmlParser.parse(xml);
             }
         } catch (UnsupportedEncodingException e) {
             context.report(
@@ -66,7 +59,7 @@
                     // is valid
                     IssueRegistry.PARSER_ERROR, Location.create(context.file),
                     e.getCause() != null ? e.getCause().getLocalizedMessage() :
-                        e.getLocalizedMessage()
+                            e.getLocalizedMessage()
             );
         } catch (SAXException e) {
             Location location = Location.create(context.file);
@@ -96,24 +89,14 @@
     @NonNull
     @Override
     public Location getLocation(@NonNull XmlContext context, @NonNull Node node) {
-        OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, -1, -1);
-        if (pos != null) {
-            return Location.create(context.file, pos, (OffsetPosition) pos.getEnd());
-        }
-
-        return Location.create(context.file);
+        return Location.create(context.file, PositionXmlParser.getPosition(node));
     }
 
     @NonNull
     @Override
     public Location getLocation(@NonNull XmlContext context, @NonNull Node node,
             int start, int end) {
-        OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, start, end);
-        if (pos != null) {
-            return Location.create(context.file, pos, (OffsetPosition) pos.getEnd());
-        }
-
-        return Location.create(context.file);
+        return Location.create(context.file, PositionXmlParser.getPosition(node, start, end));
     }
 
     @Override
@@ -130,8 +113,8 @@
         int startOffset = start.getOffset() + delta;
         int startColumn = start.getColumn() + delta;
         return Location.create(location.getFile(),
-                new OffsetPosition(start.getLine(), startColumn, startOffset),
-                new OffsetPosition(end.getLine(), startColumn + length, startOffset + length));
+                new DefaultPosition(start.getLine(), startColumn, startOffset),
+                new DefaultPosition(end.getLine(), startColumn + length, startOffset + length));
     }
 
     @Override
@@ -149,8 +132,8 @@
         int startOffset = start.getOffset() + delta;
         int startColumn = start.getColumn() + delta;
         return Location.create(location.getFile(),
-                new OffsetPosition(start.getLine(), startColumn, startOffset),
-                new OffsetPosition(end.getLine(), startColumn + length, startOffset + length));
+                new DefaultPosition(start.getLine(), startColumn, startOffset),
+                new DefaultPosition(end.getLine(), startColumn + length, startOffset + length));
     }
 
     @NonNull
@@ -159,92 +142,14 @@
         return new LocationHandle(context.file, node);
     }
 
-    private static class OffsetPosition extends com.android.tools.lint.detector.api.Position
-            implements PositionXmlParser.Position {
-        /** The line number (0-based where the first line is line 0) */
-        private final int mLine;
-
-        /**
-         * The column number (where the first character on the line is 0), or -1 if
-         * unknown
-         */
-        private final int mColumn;
-
-        /** The character offset */
-        private final int mOffset;
-
-        /**
-         * Linked position: for a begin offset this will point to the end
-         * offset, and for an end offset this will be null
-         */
-        private PositionXmlParser.Position mEnd;
-
-        /**
-         * Creates a new {@link OffsetPosition}
-         *
-         * @param line the 0-based line number, or -1 if unknown
-         * @param column the 0-based column number, or -1 if unknown
-         * @param offset the offset, or -1 if unknown
-         */
-        public OffsetPosition(int line, int column, int offset) {
-            mLine = line;
-            mColumn = column;
-            mOffset = offset;
-        }
-
-        @Override
-        public int getLine() {
-            return mLine;
-        }
-
-        @Override
-        public int getOffset() {
-            return mOffset;
-        }
-
-        @Override
-        public int getColumn() {
-            return mColumn;
-        }
-
-        @Override
-        public PositionXmlParser.Position getEnd() {
-            return mEnd;
-        }
-
-        @Override
-        public void setEnd(@NonNull PositionXmlParser.Position end) {
-            mEnd = end;
-        }
-
-        @Override
-        public String toString() {
-            return "OffsetPosition [line=" + mLine + ", column=" + mColumn + ", offset="
-                    + mOffset + ", end=" + mEnd + ']';
-        }
-    }
-
     @Override
     public int getNodeStartOffset(@NonNull XmlContext context, @NonNull Node node) {
-        OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, -1, -1);
-        if (pos != null) {
-            return pos.getOffset();
-        }
-
-        return -1;
+        return  PositionXmlParser.getPosition(node).getStartOffset();
     }
 
     @Override
     public int getNodeEndOffset(@NonNull XmlContext context, @NonNull Node node) {
-        OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, -1, -1);
-        if (pos != null) {
-            PositionXmlParser.Position end = pos.getEnd();
-            if (end != null) {
-                return end.getOffset();
-            }
-        }
-
-        return -1;
+        return  PositionXmlParser.getPosition(node).getEndOffset();
     }
 
     /* Handle for creating DOM positions cheaply and returning full fledged locations later */
@@ -261,12 +166,7 @@
         @NonNull
         @Override
         public Location resolve() {
-            OffsetPosition pos = (OffsetPosition) mParser.getPosition(mNode);
-            if (pos != null) {
-                return Location.create(mFile, pos, (OffsetPosition) pos.getEnd());
-            }
-
-            return Location.create(mFile);
+            return Location.create(mFile, PositionXmlParser.getPosition(mNode));
         }
 
         @Override
diff --git a/lint/cli/src/main/java/com/android/tools/lint/Main.java b/lint/cli/src/main/java/com/android/tools/lint/Main.java
index af3846a..126d02f 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/Main.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/Main.java
@@ -32,6 +32,7 @@
 import com.android.tools.lint.checks.BuiltinIssueRegistry;
 import com.android.tools.lint.client.api.Configuration;
 import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.LintDriver;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Issue;
@@ -145,19 +146,22 @@
                     Location location = Location.create(project.getDir());
                     Context context = new Context(mDriver, project, project, project.getDir());
                     if (context.isEnabled(IssueRegistry.LINT_ERROR) &&
-                            !getConfiguration(project).isIgnored(context, IssueRegistry.LINT_ERROR,
-                            location, message)) {
+                            !getConfiguration(project, null).isIgnored(context,
+                                    IssueRegistry.LINT_ERROR, location, message)) {
                         report(context,
-                               IssueRegistry.LINT_ERROR,
-                               project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
-                               location, message, TextFormat.RAW);
+                                IssueRegistry.LINT_ERROR,
+                                project.getConfiguration(null).getSeverity(
+                                        IssueRegistry.LINT_ERROR), location, message,
+                                TextFormat.RAW);
                     }
                 }
                 return project;
             }
 
+            @NonNull
             @Override
-            public Configuration getConfiguration(@NonNull final Project project) {
+            public Configuration getConfiguration(@NonNull final Project project,
+                    @Nullable LintDriver driver) {
                 if (project.isGradleProject()) {
                     // Don't report any issues when analyzing a Gradle project from the
                     // non-Gradle runner; they are likely to be false, and will hide the real
@@ -184,7 +188,7 @@
                        }
                    };
                 }
-                return super.getConfiguration(project);
+                return super.getConfiguration(project, driver);
             }
         };
 
diff --git a/lint/cli/src/test/java/com/android/tools/lint/EcjParserTest.java b/lint/cli/src/test/java/com/android/tools/lint/EcjParserTest.java
deleted file mode 100644
index cb97e27..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/EcjParserTest.java
+++ /dev/null
@@ -1,772 +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.tools.lint;
-
-import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
-
-import com.android.annotations.NonNull;
-import com.android.tools.lint.checks.AbstractCheckTest;
-import com.android.tools.lint.checks.SdCardDetector;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.LintUtilsTest;
-import com.android.tools.lint.detector.api.Project;
-
-import junit.framework.Assert;
-
-import java.io.File;
-
-import lombok.ast.AnnotationElement;
-import lombok.ast.BinaryExpression;
-import lombok.ast.Block;
-import lombok.ast.DescribedNode;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.KeywordModifier;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Modifiers;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.Select;
-import lombok.ast.TypeReferencePart;
-import lombok.ast.VariableDeclaration;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableReference;
-import lombok.ast.printer.SourceFormatter;
-import lombok.ast.printer.SourcePrinter;
-import lombok.ast.printer.TextFormatter;
-
-public class EcjParserTest extends AbstractCheckTest {
-    public void testTryCatchHang() throws Exception {
-        // Ensure that we're really using this parser
-        JavaParser javaParser = createClient().getJavaParser(null);
-        assertNotNull(javaParser);
-        assertTrue(javaParser.getClass().getName(), javaParser instanceof EcjParser);
-
-        // See https://code.google.com/p/projectlombok/issues/detail?id=573#c6
-        // With lombok.ast 0.2.1 and the parboiled-based Java parser this test will hang forever.
-        assertEquals(
-                "No warnings.",
-
-                lintProject("src/test/pkg/TryCatchHang.java.txt=>src/test/pkg/TryCatchHang.java"));
-    }
-
-    public void testKitKatLanguageFeatures() throws Exception {
-        String testClass = "" +
-                "package test.pkg;\n" +
-                "\n" +
-                "import java.io.BufferedReader;\n" +
-                "import java.io.FileReader;\n" +
-                "import java.io.IOException;\n" +
-                "import java.lang.reflect.InvocationTargetException;\n" +
-                "import java.util.List;\n" +
-                "import java.util.Map;\n" +
-                "import java.util.TreeMap;\n" +
-                "\n" +
-                "public class Java7LanguageFeatureTest {\n" +
-                "    public void testDiamondOperator() {\n" +
-                "        Map<String, List<Integer>> map = new TreeMap<>();\n" +
-                "    }\n" +
-                "\n" +
-                "    public int testStringSwitches(String value) {\n" +
-                "        final String first = \"first\";\n" +
-                "        final String second = \"second\";\n" +
-                "\n" +
-                "        switch (value) {\n" +
-                "            case first:\n" +
-                "                return 41;\n" +
-                "            case second:\n" +
-                "                return 42;\n" +
-                "            default:\n" +
-                "                return 0;\n" +
-                "        }\n" +
-                "    }\n" +
-                "\n" +
-                "    public String testTryWithResources(String path) throws IOException {\n" +
-                "        try (BufferedReader br = new BufferedReader(new FileReader(path))) {\n" +
-                "            return br.readLine();\n" +
-                "        }\n" +
-                "    }\n" +
-                "\n" +
-                "    public void testNumericLiterals() {\n" +
-                "        int thousand = 1_000;\n" +
-                "        int million = 1_000_000;\n" +
-                "        int binary = 0B01010101;\n" +
-                "    }\n" +
-                "\n" +
-                "    public void testMultiCatch() {\n" +
-                "\n" +
-                "        try {\n" +
-                "            Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n" +
-                "        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {\n" +
-                "            e.printStackTrace();\n" +
-                "        } catch (ClassNotFoundException e) {\n" +
-                "            // TODO: Logging here\n" +
-                "        }\n" +
-                "    }\n" +
-                "}\n";
-
-        Node unit = LintUtilsTest.getCompilationUnit(testClass);
-        assertNotNull(unit);
-
-        // Now print the AST back and make sure that it contains at least the essence of the AST
-        TextFormatter formatter = new TextFormatter();
-        unit.accept(new SourcePrinter(formatter));
-        String actual = formatter.finish();
-        assertEquals(""
-                + "package test.pkg;\n"
-                + "\n"
-                + "import java.io.BufferedReader;\n"
-                + "import java.io.FileReader;\n"
-                + "import java.io.IOException;\n"
-                + "import java.lang.reflect.InvocationTargetException;\n"
-                + "import java.util.List;\n"
-                + "import java.util.Map;\n"
-                + "import java.util.TreeMap;\n"
-                + "\n"
-                + "public class Java7LanguageFeatureTest {\n"
-                + "    public void testDiamondOperator() {\n"
-                + "        Map<String, List<Integer>> map = new TreeMap();\n" // missing types on rhs
-                + "    }\n"
-                + "    \n"
-                + "    public int testStringSwitches(String value) {\n"
-                + "        final String first = \"first\";\n"
-                + "        final String second = \"second\";\n"
-                + "        switch (value) {\n"
-                + "        case first:\n"
-                + "            return 41;\n"
-                + "        case second:\n"
-                + "            return 42;\n"
-                + "        default:\n"
-                + "            return 0;\n"
-                + "        }\n"
-                + "    }\n"
-                + "    \n"
-                + "    public String testTryWithResources(String path) throws IOException {\n"
-                + "        try {\n" // Note how the initialization clause is gone here
-                + "            return br.readLine();\n"
-                + "        }\n"
-                + "    }\n"
-                + "    \n"
-                + "    public void testNumericLiterals() {\n"
-                + "        int thousand = 1_000;\n"
-                + "        int million = 1_000_000;\n"
-                + "        int binary = 0B01010101;\n"
-                + "    }\n"
-                + "    \n"
-                + "    public void testMultiCatch() {\n"
-                + "        try {\n"
-                + "            Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n"
-                + "        } catch (IllegalAccessException e) {\n" // Note: missing other union types
-                + "            e.printStackTrace();\n"
-                + "        } catch (ClassNotFoundException e) {\n"
-                + "        }\n"
-                + "    }\n"
-                + "}",
-                actual);
-    }
-
-    public void testResolution() throws Exception {
-        String source =
-                "package test.pkg;\n" +
-                "\n" +
-                "import java.io.File;\n" +
-                "\n" +
-                "public class TypeResolutionTest {\n" +
-                "    public static class Inner extends File {\n" +
-                "        public float myField = 5f;\n" +
-                "        public int[] myInts;\n" +
-                "\n" +
-                "        public Inner(File dir, String name) {\n" +
-                "            super(dir, name);\n" +
-                "        }\n" +
-                "\n" +
-                "        public void call(int arg1, double arg2) {\n" +
-                "            boolean x = super.canRead();\n" +
-                "            System.out.println(x);\n" +
-                "        }\n" +
-                "    }\n" +
-                "\n" +
-                "    @SuppressWarnings(\"all\")\n" +
-                "    public static class Other {\n" +
-                "         private void client(int z) {\n" +
-                "             int x = z;\n" +
-                "             int y = x + 5;\n" +
-                "             Inner inner = new Inner(null, null);\n" +
-                "             inner.myField = 6;\n" +
-                "             System.out.println(inner.myInts);\n" +
-                "         }\n" +
-                "    }\n" +
-                "}\n";
-
-        Node unit = LintUtilsTest.getCompilationUnit(source,
-                new File("src/test/pkg/TypeResolutionTest.java"));
-
-        // Visit all nodes and assert nativeNode != null unless I expect it!
-        unit.accept(new ForwardingAstVisitor() {
-            @SuppressWarnings("Contract")
-            @Override
-            public boolean visitNode(Node node) {
-                if (node.getNativeNode() == null && requiresNativeNode(node)) {
-                    fail("Expected native node on node of type " +
-                                    node.getClass().getSimpleName());
-                }
-                return super.visitNode(node);
-            }
-
-            private boolean requiresNativeNode(Node node) {
-                if (node instanceof TypeReferencePart &&
-                        node.getParent().getNativeNode() != null) {
-                    return false;
-                }
-
-                if (node instanceof Identifier
-                        || node instanceof NormalTypeBody
-                        || node instanceof Block
-                        || node instanceof VariableDeclaration
-                        || node instanceof VariableDefinition
-                        || node instanceof AnnotationElement
-                        || node instanceof BinaryExpression
-                        || node instanceof Modifiers
-                        || node instanceof KeywordModifier) {
-                    return false;
-                }
-
-                if (node instanceof VariableReference) {
-                    VariableReference reference = (VariableReference)node;
-                    if (reference.getParent() instanceof Select) {
-                        return false;
-                    }
-                } else if (node instanceof MethodInvocation) {
-                    Node parent = node.getParent();
-                    if (parent instanceof ExpressionStatement &&
-                            parent.getNativeNode() != null) {
-                        return false;
-                    }
-                }
-
-                return true;
-            }
-        });
-
-        JavaParser parser = new EcjParser(new LintCliClient(), null);
-        AstPrettyPrinter astPrettyPrinter = new AstPrettyPrinter(parser);
-        unit.accept(new SourcePrinter(astPrettyPrinter));
-        String actual = astPrettyPrinter.finish();
-        assertEquals(
-                "[CompilationUnit]\n" +
-                "  [PackageDeclaration]\n" +
-                "    [Identifier test]\n" +
-                "      PROPERTY: name = test\n" +
-                "    [Identifier pkg]\n" +
-                "      PROPERTY: name = pkg\n" +
-                "  [ImportDeclaration]\n" +
-                "    PROPERTY: static = false\n" +
-                "    PROPERTY: star = false\n" +
-                "    [Identifier java]\n" +
-                "      PROPERTY: name = java\n" +
-                "    [Identifier io]\n" +
-                "      PROPERTY: name = io\n" +
-                "    [Identifier File]\n" +
-                "      PROPERTY: name = File\n" +
-                "  [ClassDeclaration TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
-                "    [Modifiers], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
-                "      [KeywordModifier public]\n" +
-                "        PROPERTY: modifier = public\n" +
-                "    typeName: [Identifier TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
-                "      PROPERTY: name = TypeResolutionTest\n" +
-                "    [NormalTypeBody], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
-                "        [ClassDeclaration Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "          [Modifiers], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "            [KeywordModifier public]\n" +
-                "              PROPERTY: modifier = public\n" +
-                "            [KeywordModifier static]\n" +
-                "              PROPERTY: modifier = static\n" +
-                "          typeName: [Identifier Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "            PROPERTY: name = Inner\n" +
-                "          extends: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
-                "            PROPERTY: WildcardKind = NONE\n" +
-                "            PROPERTY: arrayDimensions = 0\n" +
-                "            [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
-                "              [Identifier File]\n" +
-                "                PROPERTY: name = File\n" +
-                "          [NormalTypeBody], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "              [VariableDeclaration], type: float, resolved class: float \n" +
-                "                [VariableDefinition]\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                    [KeywordModifier public]\n" +
-                "                      PROPERTY: modifier = public\n" +
-                "                  type: [TypeReference float], type: float, resolved class: float \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    [TypeReferencePart], type: float, resolved class: float \n" +
-                "                      [Identifier float]\n" +
-                "                        PROPERTY: name = float\n" +
-                "                  [VariableDefinitionEntry], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier myField], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
-                "                      PROPERTY: name = myField\n" +
-                "                    [FloatingPointLiteral 5.0], type: float\n" +
-                "                      PROPERTY: value = 5f\n" +
-                "              [VariableDeclaration], type: int[], resolved class: int[] \n" +
-                "                [VariableDefinition]\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                    [KeywordModifier public]\n" +
-                "                      PROPERTY: modifier = public\n" +
-                "                  type: [TypeReference int[]], type: int[], resolved class: int[] \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 1\n" +
-                "                    [TypeReferencePart], type: int[], resolved class: int[] \n" +
-                "                      [Identifier int]\n" +
-                "                        PROPERTY: name = int\n" +
-                "                  [VariableDefinitionEntry], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier myInts], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
-                "                      PROPERTY: name = myInts\n" +
-                "              [ConstructorDeclaration], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                [Modifiers], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                  [KeywordModifier public]\n" +
-                "                    PROPERTY: modifier = public\n" +
-                "                typeName: [Identifier Inner], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                  PROPERTY: name = Inner\n" +
-                "                parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                  type: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
-                "                      [Identifier File]\n" +
-                "                        PROPERTY: name = File\n" +
-                "                  [VariableDefinitionEntry], resolved variable: dir java.io.File\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier dir], resolved variable: dir java.io.File\n" +
-                "                      PROPERTY: name = dir\n" +
-                "                parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                  type: [TypeReference String], type: java.lang.String, resolved class: java.lang.String \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    [TypeReferencePart], type: java.lang.String, resolved class: java.lang.String \n" +
-                "                      [Identifier String]\n" +
-                "                        PROPERTY: name = String\n" +
-                "                  [VariableDefinitionEntry], resolved variable: name java.lang.String\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier name], resolved variable: name java.lang.String\n" +
-                "                      PROPERTY: name = name\n" +
-                "                [Block], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                    [SuperConstructorInvocation], resolved method: java.io.File java.io.File\n" +
-                "                      [VariableReference], type: java.io.File, resolved variable: dir java.io.File\n" +
-                "                        [Identifier dir], type: java.io.File, resolved variable: dir java.io.File\n" +
-                "                          PROPERTY: name = dir\n" +
-                "                      [VariableReference], type: java.lang.String, resolved variable: name java.lang.String\n" +
-                "                        [Identifier name], type: java.lang.String, resolved variable: name java.lang.String\n" +
-                "                          PROPERTY: name = name\n" +
-                "              [MethodDeclaration call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
-                "                [Modifiers], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
-                "                  [KeywordModifier public]\n" +
-                "                    PROPERTY: modifier = public\n" +
-                "                returnType: [TypeReference void], type: void, resolved class: void \n" +
-                "                  PROPERTY: WildcardKind = NONE\n" +
-                "                  PROPERTY: arrayDimensions = 0\n" +
-                "                  [TypeReferencePart], type: void, resolved class: void \n" +
-                "                    [Identifier void]\n" +
-                "                      PROPERTY: name = void\n" +
-                "                methodName: [Identifier call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
-                "                  PROPERTY: name = call\n" +
-                "                parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                  type: [TypeReference int], type: int, resolved class: int \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    [TypeReferencePart], type: int, resolved class: int \n" +
-                "                      [Identifier int]\n" +
-                "                        PROPERTY: name = int\n" +
-                "                  [VariableDefinitionEntry], resolved variable: arg1 int\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier arg1], resolved variable: arg1 int\n" +
-                "                      PROPERTY: name = arg1\n" +
-                "                parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                  type: [TypeReference double], type: double, resolved class: double \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    [TypeReferencePart], type: double, resolved class: double \n" +
-                "                      [Identifier double]\n" +
-                "                        PROPERTY: name = double\n" +
-                "                  [VariableDefinitionEntry], resolved variable: arg2 double\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier arg2], resolved variable: arg2 double\n" +
-                "                      PROPERTY: name = arg2\n" +
-                "                [Block], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
-                "                    [VariableDeclaration], type: boolean, resolved class: boolean \n" +
-                "                      [VariableDefinition]\n" +
-                "                        PROPERTY: varargs = false\n" +
-                "                        [Modifiers]\n" +
-                "                        type: [TypeReference boolean], type: boolean, resolved class: boolean \n" +
-                "                          PROPERTY: WildcardKind = NONE\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          [TypeReferencePart], type: boolean, resolved class: boolean \n" +
-                "                            [Identifier boolean]\n" +
-                "                              PROPERTY: name = boolean\n" +
-                "                        [VariableDefinitionEntry], resolved variable: x boolean\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          varName: [Identifier x], resolved variable: x boolean\n" +
-                "                            PROPERTY: name = x\n" +
-                "                          [MethodInvocation canRead], type: boolean, resolved method: canRead java.io.File\n" +
-                "                            operand: [Super], type: java.io.File\n" +
-                "                            methodName: [Identifier canRead], type: boolean, resolved method: canRead java.io.File\n" +
-                "                              PROPERTY: name = canRead\n" +
-                "                    [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
-                "                      [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
-                "                        operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
-                "                          operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
-                "                            [Identifier System]\n" +
-                "                              PROPERTY: name = System\n" +
-                "                          selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
-                "                            PROPERTY: name = out\n" +
-                "                        methodName: [Identifier println]\n" +
-                "                          PROPERTY: name = println\n" +
-                "                        [VariableReference], type: boolean, resolved variable: x boolean\n" +
-                "                          [Identifier x], type: boolean, resolved variable: x boolean\n" +
-                "                            PROPERTY: name = x\n" +
-                "        [ClassDeclaration Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
-                "          [Modifiers], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
-                "            [Annotation SuppressWarnings], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
-                "              [TypeReference SuppressWarnings], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
-                "                PROPERTY: WildcardKind = NONE\n" +
-                "                PROPERTY: arrayDimensions = 0\n" +
-                "                [TypeReferencePart], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
-                "                  [Identifier SuppressWarnings]\n" +
-                "                    PROPERTY: name = SuppressWarnings\n" +
-                "              [AnnotationElement null], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
-                "                [StringLiteral all], type: java.lang.String\n" +
-                "                  PROPERTY: value = \"all\"\n" +
-                "            [KeywordModifier public]\n" +
-                "              PROPERTY: modifier = public\n" +
-                "            [KeywordModifier static]\n" +
-                "              PROPERTY: modifier = static\n" +
-                "          typeName: [Identifier Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
-                "            PROPERTY: name = Other\n" +
-                "          [NormalTypeBody], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
-                "              [MethodDeclaration client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
-                "                [Modifiers], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
-                "                  [KeywordModifier private]\n" +
-                "                    PROPERTY: modifier = private\n" +
-                "                returnType: [TypeReference void], type: void, resolved class: void \n" +
-                "                  PROPERTY: WildcardKind = NONE\n" +
-                "                  PROPERTY: arrayDimensions = 0\n" +
-                "                  [TypeReferencePart], type: void, resolved class: void \n" +
-                "                    [Identifier void]\n" +
-                "                      PROPERTY: name = void\n" +
-                "                methodName: [Identifier client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
-                "                  PROPERTY: name = client\n" +
-                "                parameter: [VariableDefinition], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
-                "                  PROPERTY: varargs = false\n" +
-                "                  [Modifiers]\n" +
-                "                  type: [TypeReference int], type: int, resolved class: int \n" +
-                "                    PROPERTY: WildcardKind = NONE\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    [TypeReferencePart], type: int, resolved class: int \n" +
-                "                      [Identifier int]\n" +
-                "                        PROPERTY: name = int\n" +
-                "                  [VariableDefinitionEntry], resolved variable: z int\n" +
-                "                    PROPERTY: arrayDimensions = 0\n" +
-                "                    varName: [Identifier z], resolved variable: z int\n" +
-                "                      PROPERTY: name = z\n" +
-                "                [Block], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
-                "                    [VariableDeclaration], type: int, resolved class: int \n" +
-                "                      [VariableDefinition]\n" +
-                "                        PROPERTY: varargs = false\n" +
-                "                        [Modifiers]\n" +
-                "                        type: [TypeReference int], type: int, resolved class: int \n" +
-                "                          PROPERTY: WildcardKind = NONE\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          [TypeReferencePart], type: int, resolved class: int \n" +
-                "                            [Identifier int]\n" +
-                "                              PROPERTY: name = int\n" +
-                "                        [VariableDefinitionEntry], resolved variable: x int\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          varName: [Identifier x], resolved variable: x int\n" +
-                "                            PROPERTY: name = x\n" +
-                "                          [VariableReference], type: int, resolved variable: z int\n" +
-                "                            [Identifier z], type: int, resolved variable: z int\n" +
-                "                              PROPERTY: name = z\n" +
-                "                    [VariableDeclaration], type: int, resolved class: int \n" +
-                "                      [VariableDefinition]\n" +
-                "                        PROPERTY: varargs = false\n" +
-                "                        [Modifiers]\n" +
-                "                        type: [TypeReference int], type: int, resolved class: int \n" +
-                "                          PROPERTY: WildcardKind = NONE\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          [TypeReferencePart], type: int, resolved class: int \n" +
-                "                            [Identifier int]\n" +
-                "                              PROPERTY: name = int\n" +
-                "                        [VariableDefinitionEntry], resolved variable: y int\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          varName: [Identifier y], resolved variable: y int\n" +
-                "                            PROPERTY: name = y\n" +
-                "                          [BinaryExpression +], type: int\n" +
-                "                            PROPERTY: operator = +\n" +
-                "                            left: [VariableReference], type: int, resolved variable: x int\n" +
-                "                              [Identifier x], type: int, resolved variable: x int\n" +
-                "                                PROPERTY: name = x\n" +
-                "                            right: [IntegralLiteral 5], type: int\n" +
-                "                              PROPERTY: value = 5\n" +
-                "                    [VariableDeclaration], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "                      [VariableDefinition]\n" +
-                "                        PROPERTY: varargs = false\n" +
-                "                        [Modifiers]\n" +
-                "                        type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "                          PROPERTY: WildcardKind = NONE\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "                            [Identifier Inner]\n" +
-                "                              PROPERTY: name = Inner\n" +
-                "                        [VariableDefinitionEntry], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                          PROPERTY: arrayDimensions = 0\n" +
-                "                          varName: [Identifier inner], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                            PROPERTY: name = inner\n" +
-                "                          [ConstructorInvocation Inner], type: test.pkg.TypeResolutionTest.Inner, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                            type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "                              PROPERTY: WildcardKind = NONE\n" +
-                "                              PROPERTY: arrayDimensions = 0\n" +
-                "                              [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
-                "                                [Identifier Inner]\n" +
-                "                                  PROPERTY: name = Inner\n" +
-                "                            [NullLiteral], type: null\n" +
-                "                            [NullLiteral], type: null\n" +
-                "                    [ExpressionStatement], type: float\n" +
-                "                      [BinaryExpression =], type: float\n" +
-                "                        PROPERTY: operator = =\n" +
-                "                        left: [Select], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                          operand: [VariableReference], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                            [Identifier inner]\n" +
-                "                              PROPERTY: name = inner\n" +
-                "                          selected: [Identifier myField], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                            PROPERTY: name = myField\n" +
-                "                        right: [IntegralLiteral 6], type: int\n" +
-                "                          PROPERTY: value = 6\n" +
-                "                    [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
-                "                      [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
-                "                        operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
-                "                          operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
-                "                            [Identifier System]\n" +
-                "                              PROPERTY: name = System\n" +
-                "                          selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
-                "                            PROPERTY: name = out\n" +
-                "                        methodName: [Identifier println]\n" +
-                "                          PROPERTY: name = println\n" +
-                "                        [Select], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                          operand: [VariableReference], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                            [Identifier inner]\n" +
-                "                              PROPERTY: name = inner\n" +
-                "                          selected: [Identifier myInts], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
-                "                            PROPERTY: name = myInts\n",
-                actual);
-    }
-
-    @Override
-    protected Detector getDetector() {
-        return new SdCardDetector();
-    }
-
-    @Override
-    protected TestLintClient createClient() {
-        return new TestLintClient() {
-            @NonNull
-            @Override
-            protected ClassPathInfo getClassPath(@NonNull Project project) {
-                ClassPathInfo classPath = super.getClassPath(project);
-                // Insert fake classpath entries (non existent directories) to
-                // make sure the parser handles that gracefully. See issue 87740.
-                classPath.getLibraries().add(new File("nonexistent path"));
-                return classPath;
-            }
-        };
-    }
-
-    public static class AstPrettyPrinter implements SourceFormatter {
-
-        private final StringBuilder mOutput = new StringBuilder(1000);
-
-        private final JavaParser mResolver;
-
-        private int mIndent;
-
-        private String mName;
-
-        public AstPrettyPrinter(JavaParser resolver) {
-            mResolver = resolver;
-        }
-
-        private void add(String in, Object... args) {
-            for (int i = 0; i < mIndent; i++) {
-                mOutput.append("  ");
-            }
-            if (mName != null) {
-                mOutput.append(mName).append(": ");
-                mName = null;
-            }
-            if (args.length == 0) {
-                mOutput.append(in);
-            } else {
-                mOutput.append(String.format(in, args));
-            }
-        }
-
-        @Override
-        public void buildInline(Node node) {
-            buildNode(node);
-        }
-
-        @Override
-        public void buildBlock(Node node) {
-            buildNode(node);
-        }
-
-        private void buildNode(Node node) {
-            if (node == null) {
-                mIndent++;
-                return;
-            }
-            String name = node.getClass().getSimpleName();
-            String description = "";
-            if (node instanceof DescribedNode) {
-                description = " " + ((DescribedNode) node).getDescription();
-            }
-
-            String typeDescription = "";
-            String resolutionDescription = "";
-            JavaParser.TypeDescriptor t = mResolver.getType(null, node);
-            if (t != null) {
-                typeDescription = ", type: " + t.getName();
-            }
-            ResolvedNode resolved = mResolver.resolve(null, node);
-            if (resolved != null) {
-                String c = "unknown";
-                String extra = "";
-                if (resolved instanceof ResolvedClass) {
-                    c = "class";
-                } else if (resolved instanceof ResolvedMethod) {
-                    c = "method";
-                    ResolvedMethod method = (ResolvedMethod) resolved;
-                    extra = method.getContainingClass().getName();
-                } else if (resolved instanceof ResolvedField) {
-                    c = "field";
-                    ResolvedField field = (ResolvedField) resolved;
-                    extra = field.getContainingClass().getName();
-                } else if (resolved instanceof ResolvedVariable) {
-                    c = "variable";
-                    ResolvedVariable variable = (ResolvedVariable) resolved;
-                    extra = variable.getType().getName();
-                }
-                resolutionDescription = String.format(", resolved %1$s: %2$s %3$s",
-                        c, resolved.getName(), extra);
-            }
-
-            add("[%1$s%2$s]%3$s%4$s\n", name, description, typeDescription, resolutionDescription);
-
-            mIndent++;
-        }
-
-        @Override
-        public void fail(String fail) {
-            Assert.fail(fail);
-        }
-
-        @Override
-        public void property(String name, Object value) {
-            add("PROPERTY: %s = %s\n", name, value);
-        }
-
-        @Override
-        public void keyword(String text) {
-        }
-
-        @Override
-        public void operator(String text) {
-        }
-
-        @Override
-        public void verticalSpace() {
-        }
-
-        @Override
-        public void space() {
-        }
-
-        @Override
-        public void append(String text) {
-        }
-
-        @Override
-        public void startSuppressBlock() {
-        }
-
-        @Override
-        public void endSuppressBlock() {
-        }
-
-        @Override
-        public void startSuppressIndent() {
-        }
-
-        @Override
-        public void endSuppressIndent() {
-        }
-
-        @Override
-        public void closeInline() {
-            mIndent--;
-        }
-
-        @Override
-        public void closeBlock() {
-            mIndent--;
-        }
-
-        @Override
-        public void addError(int start, int end, String message) {
-            fail(message);
-        }
-
-        @Override
-        public String finish() {
-            return mOutput.toString();
-        }
-
-        @Override
-        public void setTimeTaken(long taken) {
-        }
-
-        @Override
-        public void nameNextElement(String name) {
-            mName = name;
-        }
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java b/lint/cli/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
deleted file mode 100644
index 71a0392..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Copyright (C) 2015 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.tools.lint;
-
-import static com.android.tools.lint.ExternalAnnotationRepository.FN_ANNOTATIONS_XML;
-import static com.google.common.base.Charsets.UTF_8;
-import static java.io.File.separatorChar;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.testutils.SdkTestCase;
-import com.android.tools.lint.client.api.JavaParser.DefaultTypeDescriptor;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
-import com.android.tools.lint.detector.api.JavaContext;
-import com.android.tools.lint.detector.api.LintUtilsTest;
-import com.google.common.base.Splitter;
-import com.google.common.io.Files;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.Node;
-
-public class ExternalAnnotationRepositoryTest extends SdkTestCase {
-
-    @Nullable
-    private ExternalAnnotationRepository getSdkAnnotations() {
-        File annotations = findSrcRelativeDir("tools/adt/idea/android/annotations");
-        if (annotations != null) {
-            List<File> files = Collections.singletonList(annotations);
-            ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
-            assertNotNull(manager);
-            return manager;
-        } else {
-            // Can't find it when running from Gradle; ignore for now
-            //fail("Could not find annotations database");
-        }
-
-        return null;
-    }
-
-    @Nullable
-    private ExternalAnnotationRepository getExternalAnnotations(@NonNull String pkg,
-            @NonNull String contents) throws IOException {
-        File dir = Files.createTempDir();
-        try {
-            File pkgDir = new File(dir, pkg.replace('.', separatorChar));
-            boolean mkdirs = pkgDir.mkdirs();
-            assertTrue(mkdirs);
-            Files.write(contents, new File(pkgDir, FN_ANNOTATIONS_XML), UTF_8);
-
-            List<File> files = Collections.singletonList(dir);
-            ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
-            assertNotNull(manager);
-            return manager;
-
-        } finally {
-            deleteFile(dir);
-        }
-    }
-
-    public void testFields() throws Exception {
-        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
-                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<root>\n"
-                + "  <item name=\"android.graphics.Color\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation1\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Color BLUE\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Color TRANSPARENT\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "</root>\n");
-        assertNotNull(manager);
-        ResolvedClass cls = createClass("android.graphics.Color");
-        assertNotNull(manager.getAnnotation(cls, "android.support.annotation.Annotation1"));
-        ResolvedField blueField = createField("android.graphics.Color", "BLUE");
-        ResolvedField transparentField = createField("android.graphics.Color", "TRANSPARENT");
-        assertNotNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation3"));
-        assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation4"));
-        assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation5"));
-
-        assertNotNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation5"));
-        assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation3"));
-        assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation4"));
-    }
-
-    public void testMethods1() throws Exception {
-        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
-                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<root>\n"
-                + "  <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
-                + "      <val name=\"value\" val=\"3\" />\n"
-                + "    </annotation>\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Color int alpha(int)\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Color int argb(int, int, int, int)\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.ArrayAdapter ArrayAdapter(android.content.Context, int, int, java.util.List&lt;T&gt;) 3\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
-                + "  </item>"
-                + "</root>\n");
-        assertNotNull(manager);
-        ResolvedMethod method1 = createMethod("android.graphics.Color", "int", "HSVToColor",
-                "float[]");
-        assertNotNull(manager.getAnnotation(method1, 0, "android.support.annotation.Annotation5"));
-
-        // Generic types
-        ResolvedMethod method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
-                "android.content.Context, int, int, java.util.List<T>");
-        assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
-
-        // Raw types
-        method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
-                "android.content.Context, int, int, java.util.List");
-        assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
-    }
-
-    public void testMethods2() throws Exception {
-        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
-                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<root>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod()\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "    <annotation name=\"android.support.annotation.Annotation6\">\n"
-                + "      <val name=\"suggest\" val=\"&quot;#other(String,String)&quot;\" />\n"
-                + "    </annotation>\n"
-                + "  </item>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(int) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
-                + "      <val name=\"value\" val=\"3\" />\n"
-                + "    </annotation>\n"
-                + "  </item>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(int[]) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(int,java.lang.Object) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List&lt;T&gt;) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List&lt;T&gt;) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(java.util.Map&lt;java.lang.String,java.util.Map&lt;java.lang.String,java.lang.String&gt;&gt;,int) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
-                + "  </item>\n"
-                + "</root>\n");
-        assertNotNull(manager);
-        ResolvedMethod method;
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "");
-        assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
-        assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation6"));
-
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int");
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
-
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int[]");
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation3"));
-        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
-
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
-                "int,java.lang.Object");
-        assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
-
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
-                "android.content.Context, int, int, java.util.List<T>");
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
-        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
-        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
-        assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
-        assertNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
-
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
-                "android.content.Context, int, int, java.util.List");
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
-        assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
-
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
-                Arrays.asList("java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>",
-                        "int"), false);
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
-        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
-                "java.util.Map,int");
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
-    }
-
-    // test intdef!
-
-
-    public void testAnnotationAttributes() throws Exception {
-        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
-                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<root>\n"
-                + "  <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
-                + "      <val name=\"value\" val=\"3\" />\n"
-                + "    </annotation>\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
-                + "      <val name=\"value\" val=\"3\" />\n"
-                + "    </annotation>\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Canvas void drawLines(float[], android.graphics.Paint) 0\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
-                + "      <val name=\"min\" val=\"4\" />\n"
-                + "      <val name=\"multiple\" val=\"2\" />\n"
-                + "    </annotation>\n"
-                + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Canvas int saveLayer(android.graphics.RectF, android.graphics.Paint, int) 2\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation8\">\n"
-                + "      <val name=\"value\" val=\"{android.graphics.Canvas.MATRIX_SAVE_FLAG, android.graphics.Canvas.CLIP_SAVE_FLAG, android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG, android.graphics.Canvas.FULL_COLOR_LAYER_SAVE_FLAG, android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG, android.graphics.Canvas.ALL_SAVE_FLAG}\" />\n"
-                + "      <val name=\"flag\" val=\"true\" />\n"
-                + "    </annotation>\n"
-                + "  </item>\n"
-                + "</root>\n");
-        assertNotNull(manager);
-        ResolvedMethod method;
-        ResolvedAnnotation annotation;
-
-        // Size 1
-        method = createMethod("android.graphics.Color", "int", "HSVToColor", "int, float[]");
-        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation7"));
-        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
-        annotation = manager.getAnnotation(method, 1, "android.support.annotation.Annotation7");
-        assertNotNull(annotation);
-        assertEquals(3L, annotation.getValue());
-        assertEquals(3L, annotation.getValue("value"));
-        //noinspection ConstantConditions
-        assertEquals(3, ((Number)annotation.getValue("value")).intValue());
-        assertNotNull(annotation);
-
-        // Size 2
-        method = createMethod("android.graphics.Canvas", "void", "drawLines", "float[], android.graphics.Paint");
-        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
-        annotation = manager.getAnnotation(method, 0, "android.support.annotation.Annotation7");
-        assertNotNull(annotation);
-        assertEquals(4L, annotation.getValue("min"));
-        assertEquals(2L, annotation.getValue("multiple"));
-        assertNotNull(annotation);
-
-        // Intdef
-        method = createMethod("android.graphics.Canvas", "int", "saveLayer",
-                "android.graphics.RectF, android.graphics.Paint, int");
-        annotation = manager.getAnnotation(method, 2, "android.support.annotation.Annotation8");
-        assertNotNull(annotation);
-        assertEquals(true, annotation.getValue("flag"));
-        Object[] values = (Object[]) annotation.getValue("value");
-        assertNotNull(values);
-        assertEquals(6, values.length);
-        assertTrue(values[0] instanceof ResolvedField);
-        assertFalse(values[0].equals(createField("android.graphics.Canvas", "WRONG_NAME")));
-        assertEquals(values[0], createField("android.graphics.Canvas", "MATRIX_SAVE_FLAG"));
-        assertEquals(values[1], createField("android.graphics.Canvas", "CLIP_SAVE_FLAG"));
-        assertEquals(values[2], createField("android.graphics.Canvas", "HAS_ALPHA_LAYER_SAVE_FLAG"));
-        assertEquals(values[3], createField("android.graphics.Canvas", "FULL_COLOR_LAYER_SAVE_FLAG"));
-        assertEquals(values[4], createField("android.graphics.Canvas", "CLIP_TO_LAYER_SAVE_FLAG"));
-        assertEquals(values[5], createField("android.graphics.Canvas", "ALL_SAVE_FLA"));
-
-        ResolvedField field = (ResolvedField)values[0];
-        assertEquals("android.graphics.Canvas.MATRIX_SAVE_FLAG", field.getSignature());
-        assertEquals("android.graphics.Canvas", field.getContainingClassName());
-        assertEquals("MATRIX_SAVE_FLAG", field.getName());
-    }
-
-    public void testConstructors() throws Exception {
-        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
-                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<root>\n"
-                + "  <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "</root>\n");
-        assertNotNull(manager);
-        ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
-                "RadialGradient",
-                "float, float, float, int[], float[], android.graphics.Shader.TileMode");
-        assertNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation4"));
-        assertNotNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation5"));
-    }
-
-    public void testVarArgs() throws Exception {
-        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
-                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                + "<root>\n"
-                + "  <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int...) 3\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "  <item name=\"android.graphics.Bitmap android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]) 2\">\n"
-                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                + "  </item>\n"
-                + "</root>\n");
-        assertNotNull(manager);
-        // Match "..." in external annotation with ... in code lookup
-        ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
-                "RadialGradient",
-                "float, float, float, int...");
-        assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
-        // Match "..." in external annotation with [] in code lookup
-        method = createConstructor("android.graphics.RadialGradient",
-                "RadialGradient",
-                "float, float, float, int[]");
-        assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
-
-        // Match "[]" in external annotation with [] in code lookup
-        method = createMethod("android.graphics.Bitmap",
-                "android.graphics.Bitmap",
-                "extractAlpha",
-                "android.graphics.Paint, int[]");
-        assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
-
-        // Match "[]" in external annotation with ... in code lookup
-        method = createMethod("android.graphics.Bitmap",
-                "android.graphics.Bitmap",
-                "extractAlpha",
-                "android.graphics.Paint, int...");
-        assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
-    }
-
-    public void testMatchWithEcj() throws Exception {
-        try {
-            ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
-                    + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                    + "<root>\n"
-                    + "  <item name=\"test.pkg.Test\">\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation1\" />\n"
-                    + "  </item>\n"
-                    + "  <item name=\"test.pkg.Test.Inner\">\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation2\" />\n"
-                    + "  </item>\n"
-                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...)\">\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation6\" />\n"
-                    + "  </item>\n"
-                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...) 0\">\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                    + "  </item>\n"
-                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...) 1\">\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
-                    + "  </item>\n"
-                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...) 2\">\n"
-                    + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
-                    + "  </item>\n"
-                    + "</root>\n");
-            assertNotNull(manager);
-            ExternalAnnotationRepository.set(manager);
-
-            String source =
-                    "package test.pkg;\n" +
-                    "\n" +
-                    "public class Test {\n" +
-                    "    public void foo(int a, int[] b, int c...) {\n" +
-                    "    }\n" +
-                    "    public static class Inner {\n" +
-                    "    }\n" +
-                    "}\n";
-
-            final JavaContext context = LintUtilsTest.parse(source,
-                    new File("src/test/pkg/Test.java"));
-            assertNotNull(context);
-            Node unit = context.getCompilationUnit();
-            assertNotNull(unit);
-            unit.accept(new ForwardingAstVisitor() {
-                @Override
-                public boolean visitClassDeclaration(ClassDeclaration node) {
-                    ResolvedNode resolved = context.resolve(node);
-                    assertNotNull(resolved);
-                    assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedClass);
-                    ResolvedClass cls = (ResolvedClass) resolved;
-                    if (cls.getName().endsWith(".Inner")) {
-                        assertNull(cls.getAnnotation("android.support.annotation.Annotation1"));
-                        assertNotNull(cls.getAnnotation("android.support.annotation.Annotation2"));
-                    } else {
-                        assertNotNull(cls.getAnnotation("android.support.annotation.Annotation1"));
-                        assertNull(cls.getAnnotation("android.support.annotation.Annotation2"));
-                    }
-
-                    return super.visitClassDeclaration(node);
-                }
-
-                @Override
-                public boolean visitMethodDeclaration(MethodDeclaration node) {
-                    ResolvedNode resolved = context.resolve(node);
-                    assertNotNull(resolved);
-                    assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedMethod);
-                    ResolvedMethod method = (ResolvedMethod) resolved;
-                    assertNull(method.getAnnotation("android.support.annotation.Annotation5"));
-                    assertNotNull(method.getAnnotation("android.support.annotation.Annotation6"));
-                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 0));
-                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation4", 1));
-                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 1));
-                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation5", 2));
-
-                    return super.visitMethodDeclaration(node);
-                }
-            });
-        } finally {
-            ExternalAnnotationRepository.set(null);
-        }
-    }
-
-    public void testSdkAnnotations() throws Exception {
-        ExternalAnnotationRepository manager = getSdkAnnotations();
-        if (manager == null) {
-            // Can't find it when running from Gradle; ignore for now
-            return;
-        }
-        ResolvedMethod method = createMethod("android.view.LayoutInflater", "android.view.View",
-                "createView", "java.lang.String, java.lang.String, android.util.AttributeSet");
-        assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.NonNull"));
-    }
-
-    private static ResolvedClass createClass(String name) {
-        ResolvedClass mock = mock(ResolvedClass.class);
-        when(mock.getName()).thenReturn(name);
-        when(mock.getSignature()).thenReturn(name);
-        return mock;
-    }
-
-    private static ResolvedMethod createConstructor(String containingClass, String name,
-            String parameters) {
-        return createMethod(containingClass, null, name, parameters, true);
-    }
-
-    private static ResolvedMethod createMethod(String containingClass, String returnType,
-            String name, String parameters) {
-        return createMethod(containingClass, returnType, name, parameters, false);
-    }
-
-    private static ResolvedMethod createMethod(String containingClass, String returnType,
-            String name, String parameters, boolean isConstructor) {
-        return createMethod(containingClass, returnType, name,
-                Splitter.on(',').trimResults().split(parameters), isConstructor);
-    }
-
-    private static ResolvedMethod createMethod(String containingClass, String returnType,
-            String name, Iterable<String> parameters, boolean isConstructor) {
-        ResolvedMethod mock = mock(ResolvedMethod.class);
-        when(mock.isConstructor()).thenReturn(isConstructor);
-        when(mock.getName()).thenReturn(name);
-        if (!isConstructor) {
-            DefaultTypeDescriptor typeDescriptor = new DefaultTypeDescriptor(returnType);
-            when(mock.getReturnType()).thenReturn(typeDescriptor);
-        }
-        ResolvedClass cls = createClass(containingClass);
-        when(mock.getContainingClass()).thenReturn(cls);
-        int index = 0;
-        for (String argument : parameters) {
-            TypeDescriptor typeDescriptor = new DefaultTypeDescriptor(argument);
-            when(mock.getArgumentType(index)).thenReturn(typeDescriptor);
-            index++;
-        }
-        when(mock.getArgumentCount()).thenReturn(index);
-        return mock;
-    }
-
-    private static ResolvedField createField(String containingClass, String name) {
-        ResolvedField mock = mock(ResolvedField.class);
-        when(mock.getName()).thenReturn(name);
-        ResolvedClass cls = createClass(containingClass);
-        when(mock.getContainingClass()).thenReturn(cls);
-        return mock;
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/XmlReporterTest.java b/lint/cli/src/test/java/com/android/tools/lint/XmlReporterTest.java
deleted file mode 100644
index 68c72f8..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/XmlReporterTest.java
+++ /dev/null
@@ -1,324 +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.tools.lint;
-
-import com.android.tools.lint.checks.AbstractCheckTest;
-import com.android.tools.lint.checks.HardcodedValuesDetector;
-import com.android.tools.lint.checks.ManifestDetector;
-import com.android.tools.lint.checks.TypographyDetector;
-import com.android.tools.lint.detector.api.DefaultPosition;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.TextFormat;
-import com.android.utils.PositionXmlParser;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-@SuppressWarnings("javadoc")
-public class XmlReporterTest extends AbstractCheckTest {
-    public void test() throws Exception {
-        File file = new File(getTargetDir(), "report");
-        try {
-            LintCliClient client = new LintCliClient() {
-                @Override
-                String getRevision() {
-                    return "unittest"; // Hardcode version to keep unit test output stable
-                }
-            };
-            //noinspection ResultOfMethodCallIgnored
-            file.getParentFile().mkdirs();
-            XmlReporter reporter = new XmlReporter(client, file);
-            Project project = Project.create(client, new File("/foo/bar/Foo"),
-                    new File("/foo/bar/Foo"));
-
-            Warning warning1 = new Warning(ManifestDetector.USES_SDK,
-                    "<uses-sdk> tag should specify a target API level (the highest verified " +
-                    "version; when running on later versions, compatibility behaviors may " +
-                    "be enabled) with android:targetSdkVersion=\"?\"",
-                    Severity.WARNING, project);
-            warning1.line = 6;
-            warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
-            warning1.errorLine = "    <uses-sdk android:minSdkVersion=\"8\" />\n    ^\n";
-            warning1.path = "AndroidManifest.xml";
-            warning1.location = Location.create(warning1.file,
-                    new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
-
-            Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
-                    "[I18N] Hardcoded string \"Fooo\", should use @string resource",
-                    Severity.WARNING, project);
-            warning2.line = 11;
-            warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
-            warning2.errorLine = "        android:text=\"Fooo\" />\n" +
-                          "        ~~~~~~~~~~~~~~~~~~~\n";
-            warning2.path = "res/layout/main.xml";
-            warning2.location = Location.create(warning2.file,
-                    new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
-
-            List<Warning> warnings = new ArrayList<Warning>();
-            warnings.add(warning1);
-            warnings.add(warning2);
-
-            reporter.write(0, 2, warnings);
-
-            String report = Files.toString(file, Charsets.UTF_8);
-            assertEquals(
-                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
-                "<issues format=\"4\" by=\"lint unittest\">\n" +
-                "\n" +
-                "    <issue\n" +
-                "        id=\"UsesMinSdkAttributes\"\n" +
-                "        severity=\"Warning\"\n" +
-                "        message=\"&lt;uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=&quot;?&quot;\"\n" +
-                "        category=\"Correctness\"\n" +
-                "        priority=\"9\"\n" +
-                "        summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
-                "        explanation=\"The manifest should contain a `&lt;uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
-                "        url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
-                "        urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
-                "        errorLine1=\"    &lt;uses-sdk android:minSdkVersion=&quot;8&quot; />\"\n" +
-                "        errorLine2=\"    ^\">\n" +
-                "        <location\n" +
-                "            file=\"AndroidManifest.xml\"\n" +
-                "            line=\"7\"\n" +
-                "            column=\"5\"/>\n" +
-                "    </issue>\n" +
-                "\n" +
-                "    <issue\n" +
-                "        id=\"HardcodedText\"\n" +
-                "        severity=\"Warning\"\n" +
-                "        message=\"[I18N] Hardcoded string &quot;Fooo&quot;, should use @string resource\"\n" +
-                "        category=\"Internationalization\"\n" +
-                "        priority=\"5\"\n" +
-                "        summary=\"Hardcoded text\"\n" +
-                "        explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
-                "\n" +
-                "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
-                "\n" +
-                "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
-                "\n" +
-                "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
-                "        errorLine1=\"        android:text=&quot;Fooo&quot; />\"\n" +
-                "        errorLine2=\"        ~~~~~~~~~~~~~~~~~~~\">\n" +
-                "        <location\n" +
-                "            file=\"res/layout/main.xml\"\n" +
-                "            line=\"12\"\n" +
-                "            column=\"9\"/>\n" +
-                "    </issue>\n" +
-                "\n" +
-                "</issues>\n",
-                report);
-
-            // Make sure the XML is valid
-            Document document = new PositionXmlParser().parse(report);
-            assertNotNull(document);
-            assertEquals(2, document.getElementsByTagName("issue").getLength());
-        } finally {
-            //noinspection ResultOfMethodCallIgnored
-            file.delete();
-        }
-    }
-
-    public void testFullPaths() throws Exception {
-        File file = new File(getTargetDir(), "report");
-        try {
-            LintCliClient client = new LintCliClient() {
-                @Override
-                String getRevision() {
-                    return "unittest"; // Hardcode version to keep unit test output stable
-                }
-            };
-            client.mFlags.setFullPath(true);
-
-            //noinspection ResultOfMethodCallIgnored
-            file.getParentFile().mkdirs();
-            XmlReporter reporter = new XmlReporter(client, file);
-            Project project = Project.create(client, new File("/foo/bar/Foo"),
-                    new File("/foo/bar/Foo"));
-
-            Warning warning1 = new Warning(ManifestDetector.USES_SDK,
-                    "<uses-sdk> tag should specify a target API level (the highest verified " +
-                    "version; when running on later versions, compatibility behaviors may " +
-                    "be enabled) with android:targetSdkVersion=\"?\"",
-                    Severity.WARNING, project);
-            warning1.line = 6;
-            warning1.file = new File("/foo/bar/../Foo/AndroidManifest.xml");
-            warning1.errorLine = "    <uses-sdk android:minSdkVersion=\"8\" />\n    ^\n";
-            warning1.path = "AndroidManifest.xml";
-            warning1.location = Location.create(warning1.file,
-                    new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
-
-            Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
-                    "[I18N] Hardcoded string \"Fooo\", should use @string resource",
-                    Severity.WARNING, project);
-            warning2.line = 11;
-            warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
-            warning2.errorLine = "        android:text=\"Fooo\" />\n" +
-                          "        ~~~~~~~~~~~~~~~~~~~\n";
-            warning2.path = "res/layout/main.xml";
-            warning2.location = Location.create(warning2.file,
-                    new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
-
-            List<Warning> warnings = new ArrayList<Warning>();
-            warnings.add(warning1);
-            warnings.add(warning2);
-
-            reporter.write(0, 2, warnings);
-
-            String report = Files.toString(file, Charsets.UTF_8);
-            assertEquals(
-                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
-                "<issues format=\"4\" by=\"lint unittest\">\n" +
-                "\n" +
-                "    <issue\n" +
-                "        id=\"UsesMinSdkAttributes\"\n" +
-                "        severity=\"Warning\"\n" +
-                "        message=\"&lt;uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=&quot;?&quot;\"\n" +
-                "        category=\"Correctness\"\n" +
-                "        priority=\"9\"\n" +
-                "        summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
-                "        explanation=\"The manifest should contain a `&lt;uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
-                "        url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
-                "        urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
-                "        errorLine1=\"    &lt;uses-sdk android:minSdkVersion=&quot;8&quot; />\"\n" +
-                "        errorLine2=\"    ^\">\n" +
-                "        <location\n" +
-                "            file=\"/foo/Foo/AndroidManifest.xml\"\n" +
-                "            line=\"7\"\n" +
-                "            column=\"5\"/>\n" +
-                "    </issue>\n" +
-                "\n" +
-                "    <issue\n" +
-                "        id=\"HardcodedText\"\n" +
-                "        severity=\"Warning\"\n" +
-                "        message=\"[I18N] Hardcoded string &quot;Fooo&quot;, should use @string resource\"\n" +
-                "        category=\"Internationalization\"\n" +
-                "        priority=\"5\"\n" +
-                "        summary=\"Hardcoded text\"\n" +
-                "        explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
-                "\n" +
-                "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
-                "\n" +
-                "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
-                "\n" +
-                "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
-                "        errorLine1=\"        android:text=&quot;Fooo&quot; />\"\n" +
-                "        errorLine2=\"        ~~~~~~~~~~~~~~~~~~~\">\n" +
-                "        <location\n" +
-                "            file=\"/foo/bar/Foo/res/layout/main.xml\"\n" +
-                "            line=\"12\"\n" +
-                "            column=\"9\"/>\n" +
-                "    </issue>\n" +
-                "\n" +
-                "</issues>\n",
-                report);
-
-            // Make sure the XML is valid
-            Document document = new PositionXmlParser().parse(report);
-            assertNotNull(document);
-            assertEquals(2, document.getElementsByTagName("issue").getLength());
-        } finally {
-            //noinspection ResultOfMethodCallIgnored
-            file.delete();
-        }
-    }
-
-    public void testNonPrintableChars() throws Exception {
-        // See https://code.google.com/p/android/issues/detail?id=56205
-        File file = new File(getTargetDir(), "report");
-        try {
-            LintCliClient client = new LintCliClient() {
-                @Override
-                String getRevision() {
-                    return "unittest"; // Hardcode version to keep unit test output stable
-                }
-            };
-            //noinspection ResultOfMethodCallIgnored
-            file.getParentFile().mkdirs();
-            XmlReporter reporter = new XmlReporter(client, file);
-            Project project = Project.create(client, new File("/foo/bar/Foo"),
-                    new File("/foo/bar/Foo"));
-
-            Warning warning1 = new Warning(TypographyDetector.FRACTIONS,
-                    String.format("Use fraction character %1$c (%2$s) instead of %3$s ?",
-                            '\u00BC', "&#188;", "1/4"), Severity.WARNING, project);
-            warning1.line = 592;
-            warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
-            warning1.errorLine =
-                    "        <string name=\"user_registration_name3_3\">Register 3/3</string>\n" +
-                    "                                             ^";
-            warning1.path = "res/values-en/common_strings.xml";
-            warning1.location = Location.create(warning1.file,
-                    new DefaultPosition(592, 46, -1), null);
-
-            List<Warning> warnings = new ArrayList<Warning>();
-            warnings.add(warning1);
-
-            reporter.write(0, 2, warnings);
-
-            String report = Files.toString(file, Charsets.UTF_8);
-            assertEquals(""
-                    + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-                    + "<issues format=\"4\" by=\"lint unittest\">\n"
-                    + "\n"
-                    + "    <issue\n"
-                    + "        id=\"TypographyFractions\"\n"
-                    + "        severity=\"Warning\"\n"
-                    + "        message=\"Use fraction character ¼ (&amp;#188;) instead of 1/4 ?\"\n"
-                    + "        category=\"Usability:Typography\"\n"
-                    + "        priority=\"5\"\n"
-                    + "        summary=\"Fraction string can be replaced with fraction character\"\n"
-                    + "        explanation=\"You can replace certain strings, such as 1/2, and 1/4, with dedicated characters for these, such as ½ (&amp;#189;) and ¼ (&amp;#188;). This can help make the text more readable.\"\n"
-                    + "        url=\"http://en.wikipedia.org/wiki/Number_Forms\"\n"
-                    + "        urls=\"http://en.wikipedia.org/wiki/Number_Forms\">\n"
-                    + "        <location\n"
-                    + "            file=\"AndroidManifest.xml\"\n"
-                    + "            line=\"593\"\n"
-                    + "            column=\"47\"/>\n"
-                    + "    </issue>\n"
-                    + "\n"
-                    + "</issues>\n",
-                    report);
-
-            // Make sure the XML is valid
-            Document document = new PositionXmlParser().parse(report);
-            assertNotNull(document);
-            assertEquals(1, document.getElementsByTagName("issue").getLength());
-            String explanation =  ((Element)document.getElementsByTagName("issue").item(0)).
-                    getAttribute("explanation");
-            assertEquals(TypographyDetector.FRACTIONS.getExplanation(TextFormat.RAW),
-                    explanation);
-        } finally {
-            //noinspection ResultOfMethodCallIgnored
-            file.delete();
-        }
-    }
-
-    @Override
-    protected Detector getDetector() {
-        fail("Not used in this test");
-        return null;
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
deleted file mode 100644
index 11c0e1d..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 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.tools.lint.checks;
-
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-
-public abstract class AbstractCheckTest extends LintDetectorTest {
-    @Override
-    protected InputStream getTestResource(String relativePath, boolean expectExists) {
-        String path = "data" + File.separator + relativePath; //$NON-NLS-1$
-        InputStream stream = AbstractCheckTest.class.getResourceAsStream(path);
-        if (stream == null) {
-            File root = getRootDir();
-            assertNotNull(root);
-            String pkg = AbstractCheckTest.class.getName();
-            pkg = pkg.substring(0, pkg.lastIndexOf('.'));
-            File f = new File(root,
-                    "tools/base/lint/cli/src/test/java/".replace('/', File.separatorChar)
-                            + pkg.replace('.', File.separatorChar)
-                            + File.separatorChar + path);
-            if (f.exists()) {
-                try {
-                    return new BufferedInputStream(new FileInputStream(f));
-                } catch (FileNotFoundException e) {
-                    stream = null;
-                    if (expectExists) {
-                        fail("Could not find file " + relativePath);
-                    }
-                }
-            }
-        }
-        if (!expectExists && stream == null) {
-            return null;
-        }
-        return stream;
-    }
-
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
deleted file mode 100644
index 7da1444..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
+++ /dev/null
@@ -1,66 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-
-import java.util.List;
-
-@SuppressWarnings("javadoc")
-public class AnnotationDetectorTest extends AbstractCheckTest {
-    public void test() throws Exception {
-        assertEquals(
-            "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "    public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
-            "                                         ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @SuppressLint(\"NewApi\") // Invalid\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
-            "        @SuppressLint(\"NewApi\")\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "5 errors, 0 warnings\n",
-
-            lintProject(
-                "src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
-            ));
-    }
-
-    @Override
-    protected Detector getDetector() {
-        return new AnnotationDetector();
-    }
-
-    @Override
-    protected List<Issue> getIssues() {
-        List<Issue> issues = super.getIssues();
-
-        // Need these issues on to be found by the registry as well to look up scope
-        // in id references (these ids are referenced in the unit test java file below)
-        issues.add(ApiDetector.UNSUPPORTED);
-        issues.add(SdCardDetector.ISSUE);
-
-        return issues;
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
deleted file mode 100644
index dbd1093..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
+++ /dev/null
@@ -1,440 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-
-@SuppressWarnings("javadoc")
-public class ButtonDetectorTest extends AbstractCheckTest {
-    private static Issue sTestIssue;
-
-    @Override
-    protected boolean isEnabled(Issue issue) {
-        return super.isEnabled(issue) && sTestIssue == null || issue == sTestIssue;
-    }
-
-    @Override
-    protected Detector getDetector() {
-        return new ButtonDetector();
-    }
-
-    public void testButtonOrder() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "res/layout/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 7 warnings\n" +
-            "",
-
-            lintProject(
-                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrder2() throws Exception {
-        // If the layout is in v14, it had better have the right order
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "res/layout-v14/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v14/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v14/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v14/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v14/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v14/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v14/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 7 warnings\n" +
-            "",
-
-            lintProject(
-                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml=>res/layout-v14/buttonbar.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrder3() throws Exception {
-        // Similar to test 3, but also complain if the -v version is *higher* than 14
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "res/layout-v16/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v16/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v16/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v16/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v16/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v16/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-v16/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 7 warnings\n" +
-            "",
-
-            lintProject(
-                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml=>res/layout-v16/buttonbar.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrder4() throws Exception {
-        // Targeting 14 but using a layout that also needs to work for older platforms:
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "res/layout/buttonbar.xml:12: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:44: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:92: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:124: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:140: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:156: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:177: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 7 warnings\n" +
-            "",
-
-            lintProject(
-                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrder5() throws Exception {
-        // If the layout is in a non-ICS folder and has the wrong button order,
-        // but there is a v14 version of the layout, don't complain about the non-v14 version
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml",
-                    "res/layout/layout1.xml=>res/layout-v14/buttonbar.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testSuppressed() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar_suppressed.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrderRelativeLayout() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "No warnings.",
-
-            lintProject("res/layout/buttonbar2.xml", "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrderRelativeLayout2() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "res/layout/buttonbar3.xml:27: Warning: Cancel button should be on the left [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 1 warnings\n" +
-            "",
-
-            lintProject(
-                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar3.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonOrderRelativeLayout3() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "No warnings.",
-
-            lintProject("res/layout/buttonbar4.xml", "res/values/buttonbar-values.xml"));
-    }
-
-
-    public void testCase() throws Exception {
-        sTestIssue = ButtonDetector.CASE;
-        assertEquals(
-            "res/values/buttonbar-values.xml:9: Warning: The standard Android way to capitalize Ok is \"OK\" (tip: use @android:string/ok instead) [ButtonCase]\n" +
-            "    <string name=\"resume2\"> Ok </string>\n" +
-            "                            ^\n" +
-            "res/values/buttonbar-values.xml:10: Warning: The standard Android way to capitalize CANCEL is \"Cancel\" (tip: use @android:string/cancel instead) [ButtonCase]\n" +
-            "    <string name=\"giveup2\">\"CANCEL\"</string>\n" +
-            "                           ^\n" +
-            "0 errors, 2 warnings\n" +
-            "",
-
-            lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
-    }
-
-    public void testBack() throws Exception {
-        sTestIssue = ButtonDetector.BACK_BUTTON;
-        assertEquals(
-            "res/layout/buttonbar.xml:183: Warning: Back buttons are not standard on Android; see design guide's navigation section [BackButton]\n" +
-            "    <Button\n" +
-            "    ^\n" +
-            "0 errors, 1 warnings\n" +
-            "",
-
-            lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
-    }
-
-    public void testOldApp() throws Exception {
-        // Target SDK < 14 - no warnings on button order
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-                "No warnings.",
-
-        lintProject(
-                "minsdk5targetsdk9.xml=>AndroidManifest.xml",
-                "res/layout/buttonbar.xml",
-                "res/values/buttonbar-values.xml"));
-    }
-
-    public void testEnglishLocales() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            "res/layout-en-rGB/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-en-rGB/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-en-rGB/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-en-rGB/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-en-rGB/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-en-rGB/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-en-rGB/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 7 warnings\n" +
-            "",
-
-            lintProject(
-                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml=>res/layout-en-rGB/buttonbar.xml",
-                    "res/values/buttonbar-values.xml=>res/values-en-rGB/buttonbar-values.xml"));
-    }
-
-    public void testOtherLocales() throws Exception {
-        sTestIssue = ButtonDetector.ORDER;
-        assertEquals(
-            // Hardcoded values only
-            "res/layout-de/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout-de/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 2 warnings\n" +
-            "",
-
-            lintProject(
-                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
-                    "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
-    }
-
-    public void testOtherLocales2() throws Exception {
-        sTestIssue = ButtonDetector.CASE;
-        assertEquals(
-                "No warnings.",
-
-                lintProject("res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
-                        "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
-    }
-
-    public void testButtonStyle() throws Exception {
-        sTestIssue = ButtonDetector.STYLE;
-        assertEquals(
-            "res/layout/buttonbar.xml:12: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:17: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:28: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:33: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:44: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:49: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:60: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:65: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:76: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:81: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:92: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:97: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:108: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:113: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:124: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:129: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:140: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:145: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:156: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "res/layout/buttonbar.xml:161: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
-            "        <Button\n" +
-            "        ^\n" +
-            "0 errors, 20 warnings\n",
-
-            lintProject(
-                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml",
-                    "res/layout/buttonbar2.xml",
-                    "res/layout/buttonbar3.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testButtonStyleOldMinSdk() throws Exception {
-        sTestIssue = ButtonDetector.STYLE;
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "res/layout/buttonbar.xml",
-                    "res/layout/buttonbar2.xml",
-                    "res/layout/buttonbar3.xml",
-                    "res/values/buttonbar-values.xml"));
-    }
-
-    public void testYesNo() throws Exception {
-        sTestIssue = ButtonDetector.CASE;
-        assertEquals(""
-                + "res/layout/yesno.xml:10: Warning: @android:string/yes actually returns \"OK\", not \"Yes\"; use @android:string/ok instead or create a local string resource for Yes [ButtonCase]\n"
-                + "        android:text=\"@android:string/yes\" />\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "res/layout/yesno.xml:15: Warning: @android:string/no actually returns \"Cancel\", not \"No\"; use @android:string/cancel instead or create a local string resource for No [ButtonCase]\n"
-                + "        android:text=\"@android:string/no\" />\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 2 warnings\n",
-
-                lintProject(
-                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                        "res/layout/yesno.xml"));
-    }
-
-    public void testIssue101279() throws Exception {
-        // Regression test for https://code.google.com/p/android/issues/detail?id=101279
-        sTestIssue = ButtonDetector.STYLE;
-        assertEquals(
-                "No warnings.",
-
-                lintProject(
-                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                        "res/layout/buttonbar5.xml"));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
deleted file mode 100644
index e0c988d..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
+++ /dev/null
@@ -1,79 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-public class CallSuperDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new CallSuperDetector();
-    }
-
-    public void testCallSuper() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/CallSuperTest.java:11: Warning: Overriding method should call super.test1 [MissingSuperCall]\n"
-                + "        protected void test1() { // ERROR\n"
-                + "                       ~~~~~~~\n"
-                + "src/test/pkg/CallSuperTest.java:14: Warning: Overriding method should call super.test2 [MissingSuperCall]\n"
-                + "        protected void test2() { // ERROR\n"
-                + "                       ~~~~~~~\n"
-                + "src/test/pkg/CallSuperTest.java:17: Warning: Overriding method should call super.test3 [MissingSuperCall]\n"
-                + "        protected void test3() { // ERROR\n"
-                + "                       ~~~~~~~\n"
-                + "src/test/pkg/CallSuperTest.java:20: Warning: Overriding method should call super.test4 [MissingSuperCall]\n"
-                + "        protected void test4(int arg) { // ERROR\n"
-                + "                       ~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/CallSuperTest.java:26: Warning: Overriding method should call super.test5 [MissingSuperCall]\n"
-                + "        protected void test5(int arg1, boolean arg2, Map<List<String>,?> arg3,  // ERROR\n"
-                + "                       ^\n"
-                + "src/test/pkg/CallSuperTest.java:30: Warning: Overriding method should call super.test5 [MissingSuperCall]\n"
-                + "        protected void test5() { // ERROR\n"
-                + "                       ~~~~~~~\n"
-                + "0 errors, 6 warnings\n",
-
-                lintProject("src/test/pkg/CallSuperTest.java.txt=>src/test/pkg/CallSuperTest.java",
-                        "src/android/support/annotation/CallSuper.java.txt=>src/android/support/annotation/CallSuper.java"));
-    }
-
-    public void testDetachFromWindow() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/DetachedFromWindow.java:7: Warning: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
-                + "        protected void onDetachedFromWindow() {\n"
-                + "                       ~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/DetachedFromWindow.java:26: Warning: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
-                + "        protected void onDetachedFromWindow() {\n"
-                + "                       ~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 2 warnings\n",
-
-                lintProject("src/test/pkg/DetachedFromWindow.java.txt=>" +
-                        "src/test/pkg/DetachedFromWindow.java"));
-    }
-
-    public void testWatchFaceVisibility() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/WatchFaceTest.java:9: Warning: Overriding method should call super.onVisibilityChanged [MissingSuperCall]\n"
-                + "        public void onVisibilityChanged(boolean visible) { // ERROR: Missing super call\n"
-                + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject(
-                        "src/test/pkg/WatchFaceTest.java.txt=>src/test/pkg/WatchFaceTest.java",
-                        "stubs/WatchFaceService.java.txt=>src/android/support/wearable/watchface/WatchFaceService.java",
-                        "stubs/CanvasWatchFaceService.java.txt=>src/android/support/wearable/watchface/CanvasWatchFaceService.java"
-                ));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
deleted file mode 100644
index e09e85c..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
+++ /dev/null
@@ -1,110 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings("javadoc")
-public class DetectMissingPrefixTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new DetectMissingPrefix();
-    }
-
-    public void test() throws Exception {
-        assertEquals(
-            "res/layout/namespace.xml:2: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:other=\"http://foo.bar\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" orientation=\"true\">\n" +
-            "                                                                                                                                                                                                                                          ~~~~~~~~~~~~~~~~~~\n" +
-            "res/layout/namespace.xml:3: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "    <Button style=\"@style/setupWizardOuterFrame\" android.text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
-            "                                                 ~~~~~~~~~~~~~~~~~~~~~\n" +
-            "res/layout/namespace.xml:5: Error: Unexpected namespace prefix \"other\" found for tag LinearLayout [MissingPrefix]\n" +
-            "    <LinearLayout other:orientation=\"horizontal\"/>\n" +
-            "                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "3 errors, 0 warnings\n",
-
-            lintFiles("res/layout/namespace.xml"));
-    }
-
-    public void testCustomNamespace() throws Exception {
-        assertEquals(
-            "res/layout/namespace2.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "    orientation=\"true\">\n" +
-            "    ~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintFiles("res/layout/namespace2.xml"));
-    }
-
-    public void testManifest() throws Exception {
-        assertEquals(
-            "AndroidManifest.xml:4: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "    versionCode=\"1\"\n" +
-            "    ~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:11: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "        android.label=\"@string/app_name\" >\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:18: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "                <category name=\"android.intent.category.LAUNCHER\" />\n" +
-            "                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "3 errors, 0 warnings\n",
-
-            lintFiles("missingprefix.xml=>AndroidManifest.xml"));
-    }
-
-    public void testLayoutAttributes() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintFiles("res/layout/namespace3.xml"));
-    }
-
-    public void testLayoutAttributes2() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintFiles("res/layout/namespace4.xml"));
-    }
-
-    public void testUnusedNamespace() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintProject("res/layout/message_edit_detail.xml"));
-    }
-
-    public void testMissingLayoutAttribute() throws Exception {
-        assertEquals(
-            "res/layout/rtl.xml:7: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "        layout_gravity=\"left\"\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~\n" +
-            "res/layout/rtl.xml:8: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "        layout_alignParentLeft=\"true\"\n" +
-            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"  +
-            "res/layout/rtl.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
-            "        editable=\"false\"\n" +
-            "        ~~~~~~~~~~~~~~~~\n" +
-            "3 errors, 0 warnings\n",
-
-            lintProject(
-                    "overdraw/project.properties=>project.properties",
-                    "rtl/minsdk5targetsdk17.xml=>AndroidManifest.xml",
-                    "rtl/rtl_noprefix.xml=>res/layout/rtl.xml"
-            ));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
deleted file mode 100644
index b883a7b..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * Copyright (C) 2014 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.tools.lint.checks;
-
-import static com.android.SdkConstants.GRADLE_PLUGIN_MINIMUM_VERSION;
-import static com.android.SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION;
-import static com.android.tools.lint.checks.GradleDetector.ACCIDENTAL_OCTAL;
-import static com.android.tools.lint.checks.GradleDetector.COMPATIBILITY;
-import static com.android.tools.lint.checks.GradleDetector.DEPENDENCY;
-import static com.android.tools.lint.checks.GradleDetector.DEPRECATED;
-import static com.android.tools.lint.checks.GradleDetector.GRADLE_GETTER;
-import static com.android.tools.lint.checks.GradleDetector.GRADLE_PLUGIN_COMPATIBILITY;
-import static com.android.tools.lint.checks.GradleDetector.PATH;
-import static com.android.tools.lint.checks.GradleDetector.PLUS;
-import static com.android.tools.lint.checks.GradleDetector.REMOTE_VERSION;
-import static com.android.tools.lint.checks.GradleDetector.STRING_INTEGER;
-import static com.android.tools.lint.checks.GradleDetector.getNamedDependency;
-import static com.android.tools.lint.checks.GradleDetector.getNewValue;
-import static com.android.tools.lint.checks.GradleDetector.getOldValue;
-import static com.android.tools.lint.detector.api.TextFormat.TEXT;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-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.Dependencies;
-import com.android.builder.model.MavenCoordinates;
-import com.android.builder.model.Variant;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.DefaultPosition;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Implementation;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.utils.Pair;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.io.Files;
-
-import org.codehaus.groovy.ast.ASTNode;
-import org.codehaus.groovy.ast.CodeVisitorSupport;
-import org.codehaus.groovy.ast.GroovyCodeVisitor;
-import org.codehaus.groovy.ast.builder.AstBuilder;
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MapEntryExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
-import org.codehaus.groovy.ast.expr.TupleExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.ReturnStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * <b>NOTE</b>: Most GradleDetector unit tests are in the Studio plugin, as tests
- * for IntellijGradleDetector
- */
-public class GradleDetectorTest extends AbstractCheckTest {
-
-    private File mSdkDir;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        if (mSdkDir != null) {
-            deleteFile(mSdkDir);
-            mSdkDir = null;
-        }
-    }
-
-    /** Creates a mock SDK installation structure, containing a fixed set of dependencies */
-    private File getMockSupportLibraryInstallation() {
-        if (mSdkDir == null) {
-            // Make fake SDK "installation" such that we can predict the set
-            // of Maven repositories discovered by this test
-            mSdkDir = Files.createTempDir();
-
-            String[] paths = new String[]{
-                    // Android repository
-                    "extras/android/m2repository/com/android/support/appcompat-v7/18.0.0/appcompat-v7-18.0.0.aar",
-                    "extras/android/m2repository/com/android/support/appcompat-v7/19.0.0/appcompat-v7-19.0.0.aar",
-                    "extras/android/m2repository/com/android/support/appcompat-v7/19.0.1/appcompat-v7-19.0.1.aar",
-                    "extras/android/m2repository/com/android/support/appcompat-v7/19.1.0/appcompat-v7-19.1.0.aar",
-                    "extras/android/m2repository/com/android/support/appcompat-v7/20.0.0/appcompat-v7-20.0.0.aar",
-                    "extras/android/m2repository/com/android/support/appcompat-v7/21.0.0/appcompat-v7-21.0.0.aar",
-                    "extras/android/m2repository/com/android/support/appcompat-v7/21.0.2/appcompat-v7-21.0.2.aar",
-                    "extras/android/m2repository/com/android/support/cardview-v7/21.0.0/cardview-v7-21.0.0.aar",
-                    "extras/android/m2repository/com/android/support/cardview-v7/21.0.2/cardview-v7-21.0.2.aar",
-                    "extras/android/m2repository/com/android/support/support-v13/20.0.0/support-v13-20.0.0.aar",
-                    "extras/android/m2repository/com/android/support/support-v13/21.0.0/support-v13-21.0.0.aar",
-                    "extras/android/m2repository/com/android/support/support-v13/21.0.2/support-v13-21.0.2.aar",
-                    "extras/android/m2repository/com/android/support/support-v4/20.0.0/support-v4-20.0.0.aar",
-                    "extras/android/m2repository/com/android/support/support-v4/21.0.0/support-v4-21.0.0.aar",
-                    "extras/android/m2repository/com/android/support/support-v4/21.0.2/support-v4-21.0.2.aar",
-
-                    // Google repository
-                    "extras/google/m2repository/com/google/android/gms/play-services/3.1.36/play-services-3.1.36.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/3.1.59/play-services-3.1.59.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/3.2.25/play-services-3.2.25.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/3.2.65/play-services-3.2.65.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/4.0.30/play-services-4.0.30.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/4.1.32/play-services-4.1.32.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/4.2.42/play-services-4.2.42.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/4.3.23/play-services-4.3.23.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/4.4.52/play-services-4.4.52.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/5.0.89/play-services-5.0.89.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/6.1.11/play-services-6.1.11.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services/6.1.71/play-services-6.1.71.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services-wearable/5.0.77/play-services-wearable-5.0.77.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.11/play-services-wearable-6.1.11.aar",
-                    "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.71/play-services-wearable-6.1.71.aar",
-                    "extras/google/m2repository/com/google/android/support/wearable/1.0.0/wearable-1.0.0.aar"
-            };
-
-            for (String path : paths) {
-                File file = new File(mSdkDir, path.replace('/', File.separatorChar));
-                File parent = file.getParentFile();
-                if (!parent.exists()) {
-                    boolean ok = parent.mkdirs();
-                    assertTrue(ok);
-                }
-                try {
-                    boolean created = file.createNewFile();
-                    assertTrue(created);
-                } catch (IOException e) {
-                    fail(e.toString());
-                }
-            }
-        }
-
-        return mSdkDir;
-    }
-
-    public void testGetOldValue() {
-        assertEquals("11.0.2", getOldValue(DEPENDENCY,
-                "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
-                TEXT));
-        assertNull(getOldValue(DEPENDENCY, "Bogus", TEXT));
-        assertNull(getOldValue(DEPENDENCY, "bogus", TEXT));
-        // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
-        assertEquals("20", getOldValue(DEPENDENCY,
-                "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
-                TEXT));
-        assertEquals("'19'", getOldValue(STRING_INTEGER,
-                "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
-        assertEquals("android", getOldValue(DEPRECATED,
-                "'android' is deprecated; use 'com.android.application' instead", TEXT));
-        assertEquals("android-library", getOldValue(DEPRECATED,
-                "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
-        assertEquals("packageName", getOldValue(DEPRECATED,
-                "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
-        assertEquals("packageNameSuffix", getOldValue(DEPRECATED,
-                "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
-        assertEquals("18.0.0", getOldValue(DEPENDENCY,
-                "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
-    }
-
-    public void testGetNewValue() {
-        assertEquals("17.0.0", getNewValue(DEPENDENCY,
-                "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
-                TEXT));
-        assertNull(getNewValue(DEPENDENCY,
-                "A newer version of com.google.guava:guava than 11.0.2 is available", TEXT));
-        assertNull(getNewValue(DEPENDENCY, "bogus", TEXT));
-        // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
-        assertEquals("19", getNewValue(DEPENDENCY,
-                "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
-                TEXT));
-        assertEquals("19", getNewValue(STRING_INTEGER,
-                "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
-        assertEquals("com.android.application", getNewValue(DEPRECATED,
-                "'android' is deprecated; use 'com.android.application' instead", TEXT));
-        assertEquals("com.android.library", getNewValue(DEPRECATED,
-                "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
-        assertEquals("applicationId", getNewValue(DEPRECATED,
-                "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
-        assertEquals("applicationIdSuffix", getNewValue(DEPRECATED,
-                "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
-        assertEquals("19.1", getNewValue(DEPENDENCY,
-                "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
-    }
-
-    public void test() throws Exception {
-        mEnabled = Sets.newHashSet(COMPATIBILITY, DEPRECATED, DEPENDENCY, PLUS);
-        assertEquals(""
-            + "build.gradle:25: Error: This support library should not use a lower version (13) than the targetSdkVersion (17) [GradleCompatible]\n"
-            + "    compile 'com.android.support:appcompat-v7:13.0.0'\n"
-            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "build.gradle:1: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
-            + "apply plugin: 'android'\n"
-            + "~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
-            + "    buildToolsVersion \"19.0.0\"\n"
-            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
-            + "    freeCompile 'com.google.guava:guava:11.0.2'\n"
-            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
-            + "    compile 'com.android.support:appcompat-v7:13.0.0'\n"
-            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "build.gradle:23: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
-            + "    compile 'com.android.support:appcompat-v7:+'\n"
-            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "1 errors, 5 warnings\n",
-
-            lintProject("gradle/Dependencies.gradle=>build.gradle"));
-    }
-
-    public void testCompatibility() throws Exception {
-        mEnabled = Collections.singleton(COMPATIBILITY);
-        assertEquals(""
-                + "build.gradle:16: Error: This support library should not use a lower version (18) than the targetSdkVersion (19) [GradleCompatible]\n"
-                + "    compile 'com.android.support:support-v4:18.0.0'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-
-                lintProject("gradle/Compatibility.gradle=>build.gradle"));
-    }
-
-    public void testIncompatiblePlugin() throws Exception {
-        mEnabled = Collections.singleton(GRADLE_PLUGIN_COMPATIBILITY);
-        assertEquals(""
-                + "build.gradle:6: Error: You must use a newer version of the Android Gradle plugin. The minimum supported version is " + GRADLE_PLUGIN_MINIMUM_VERSION + " and the recommended version is " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [AndroidGradlePluginVersion]\n"
-                + "    classpath 'com.android.tools.build:gradle:0.1.0'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-
-                lintProject("gradle/IncompatiblePlugin.gradle=>build.gradle"));
-    }
-
-    public void testSetter() throws Exception {
-        mEnabled = Collections.singleton(GRADLE_GETTER);
-        assertEquals(""
-                        + "build.gradle:18: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
-                        + "        versionCode getVersionCode\n"
-                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "build.gradle:19: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
-                        + "        versionName getVersionName\n"
-                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "2 errors, 0 warnings\n",
-
-                lintProject("gradle/Setter.gradle=>build.gradle"));
-    }
-
-    public void testDependencies() throws Exception {
-        mEnabled = Collections.singleton(DEPENDENCY);
-        assertEquals(""
-                + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
-                + "    buildToolsVersion \"19.0.0\"\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
-                + "    freeCompile 'com.google.guava:guava:11.0.2'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
-                + "    compile 'com.android.support:appcompat-v7:13.0.0'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 3 warnings\n",
-
-                lintProject("gradle/Dependencies.gradle=>build.gradle"));
-    }
-
-    public void testLongHandDependencies() throws Exception {
-        mEnabled = Collections.singleton(DEPENDENCY);
-        assertEquals(""
-                + "build.gradle:9: Warning: A newer version of com.android.support:support-v4 than 19.0 is available: 21.0.2 [GradleDependency]\n"
-                + "    compile group: 'com.android.support', name: 'support-v4', version: '19.0'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject("gradle/DependenciesProps.gradle=>build.gradle"));
-    }
-
-    public void testDependenciesMinSdkVersion() throws Exception {
-        mEnabled = Collections.singleton(DEPENDENCY);
-        assertEquals(""
-                + "build.gradle:13: Warning: Using the appcompat library when minSdkVersion >= 14 and compileSdkVersion < 21 is not necessary [GradleDependency]\n"
-                + "    compile 'com.android.support:appcompat-v7:+'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject("gradle/Dependencies14.gradle=>build.gradle"));
-    }
-
-    public void testDependenciesMinSdkVersionLollipop() throws Exception {
-        mEnabled = Collections.singleton(DEPENDENCY);
-        assertEquals("No warnings.",
-                lintProject("gradle/Dependencies14_21.gradle=>build.gradle"));
-    }
-
-    public void testDependenciesNoMicroVersion() throws Exception {
-        // Regression test for https://code.google.com/p/android/issues/detail?id=77594
-        mEnabled = Collections.singleton(DEPENDENCY);
-        assertEquals(""
-                + "build.gradle:13: Warning: A newer version of com.google.code.gson:gson than 2.2 is available: 2.3 [GradleDependency]\n"
-                + "    compile 'com.google.code.gson:gson:2.2'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject("gradle/DependenciesGson.gradle=>build.gradle"));
-    }
-
-    public void testPaths() throws Exception {
-        mEnabled = Collections.singleton(PATH);
-        assertEquals(""
-                        + "build.gradle:4: Warning: Do not use Windows file separators in .gradle files; use / instead [GradlePath]\n"
-                        + "    compile files('my\\\\libs\\\\http.jar')\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "build.gradle:5: Warning: Avoid using absolute paths in .gradle files [GradlePath]\n"
-                        + "    compile files('/libs/android-support-v4.jar')\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "0 errors, 2 warnings\n",
-
-                lintProject("gradle/Paths.gradle=>build.gradle"));
-    }
-
-    public void testIdSuffix() throws Exception {
-        mEnabled = Collections.singleton(PATH);
-        assertEquals(""
-                        + "build.gradle:6: Warning: Package suffix should probably start with a \".\" [GradlePath]\n"
-                        + "            applicationIdSuffix \"debug\"\n"
-                        + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "0 errors, 1 warnings\n",
-
-                lintProject("gradle/IdSuffix.gradle=>build.gradle"));
-    }
-
-    public void testPackage() throws Exception {
-        mEnabled = Collections.singleton(DEPRECATED);
-        assertEquals(""
-                + "build.gradle:5: Warning: Deprecated: Replace 'packageName' with 'applicationId' [GradleDeprecated]\n"
-                + "        packageName 'my.pkg'\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~\n"
-                + "build.gradle:9: Warning: Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix' [GradleDeprecated]\n"
-                + "            packageNameSuffix \".debug\"\n"
-                + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 2 warnings\n",
-
-                lintProject("gradle/Package.gradle=>build.gradle"));
-    }
-
-    public void testPlus() throws Exception {
-        mEnabled = Collections.singleton(PLUS);
-        assertEquals(""
-                + "build.gradle:9: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
-                + "    compile 'com.android.support:appcompat-v7:+'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "build.gradle:10: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:support-v4:21.0.+) [GradleDynamicVersion]\n"
-                + "    compile group: 'com.android.support', name: 'support-v4', version: '21.0.+'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 2 warnings\n",
-
-                lintProject("gradle/Plus.gradle=>build.gradle"));
-    }
-
-    public void testStringInt() throws Exception {
-        mEnabled = Collections.singleton(STRING_INTEGER);
-        assertEquals(""
-                        + "build.gradle:4: Error: Use an integer rather than a string here (replace '19' with just 19) [StringShouldBeInt]\n"
-                        + "    compileSdkVersion '19'\n"
-                        + "    ~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "build.gradle:7: Error: Use an integer rather than a string here (replace '8' with just 8) [StringShouldBeInt]\n"
-                        + "        minSdkVersion '8'\n"
-                        + "        ~~~~~~~~~~~~~~~~~\n"
-                        + "build.gradle:8: Error: Use an integer rather than a string here (replace '16' with just 16) [StringShouldBeInt]\n"
-                        + "        targetSdkVersion '16'\n"
-                        + "        ~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "3 errors, 0 warnings\n",
-
-                lintProject("gradle/StringInt.gradle=>build.gradle"));
-    }
-
-    public void testSuppressLine2() throws Exception {
-        mEnabled = null;
-        assertEquals("No warnings.",
-
-                lintProject("gradle/SuppressLine2.gradle=>build.gradle"));
-    }
-
-    public void testDeprecatedPluginId() throws Exception {
-        mEnabled = Sets.newHashSet(DEPRECATED);
-        assertEquals(""
-                        + "build.gradle:4: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
-                        + "apply plugin: 'android'\n"
-                        + "~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "build.gradle:5: Warning: 'android-library' is deprecated; use 'com.android.library' instead [GradleDeprecated]\n"
-                        + "apply plugin: 'android-library'\n"
-                        + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "0 errors, 2 warnings\n",
-
-                lintProject("gradle/DeprecatedPluginId.gradle=>build.gradle"));
-    }
-
-    public void testIgnoresGStringsInDependencies() throws Exception {
-        mEnabled = null;
-        assertEquals("No warnings.",
-
-                lintProject("gradle/IgnoresGStringsInDependencies.gradle=>build.gradle"));
-    }
-
-    public void testAccidentalOctal() throws Exception {
-        mEnabled = Collections.singleton(ACCIDENTAL_OCTAL);
-        assertEquals(""
-                + "build.gradle:13: Error: The leading 0 turns this number into octal which is probably not what was intended (interpreted as 8) [AccidentalOctal]\n"
-                + "        versionCode 010\n"
-                + "        ~~~~~~~~~~~~~~~\n"
-                + "build.gradle:16: Error: The leading 0 turns this number into octal which is probably not what was intended (and it is not a valid octal number) [AccidentalOctal]\n"
-                + "        versionCode 01 // line suffix comments are not handled correctly\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "2 errors, 0 warnings\n",
-
-                lintProject("gradle/AccidentalOctal.gradle=>build.gradle"));
-    }
-
-    public void testBadPlayServicesVersion() throws Exception {
-        mEnabled = Collections.singleton(COMPATIBILITY);
-        assertEquals(""
-                + "build.gradle:5: Error: Version 5.2.08 should not be used; the app can not be published with this version. Use version 6.1.71 instead. [GradleCompatible]\n"
-                + "    compile 'com.google.android.gms:play-services:5.2.08'\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-
-                lintProject("gradle/PlayServices.gradle=>build.gradle"));
-    }
-
-    public void testRemoteVersions() throws Exception {
-        mEnabled = Collections.singleton(REMOTE_VERSION);
-        try {
-            HashMap<String, String> data = Maps.newHashMap();
-            GradleDetector.ourMockData = data;
-            data.put("http://search.maven.org/solrsearch/select?q=g:%22joda-time%22+AND+a:%22joda-time%22&core=gav&rows=1&wt=json",
-                    "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"joda-time\\\" AND a:\\\"joda-time\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":17,\"start\":0,\"docs\":[{\"id\":\"joda-time:joda-time:2.3\",\"g\":\"joda-time\",\"a\":\"joda-time\",\"v\":\"2.3\",\"p\":\"jar\",\"timestamp\":1376674285000,\"tags\":[\"replace\",\"time\",\"library\",\"date\",\"handling\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\".pom\"]}]}}");
-            data.put("http://search.maven.org/solrsearch/select?q=g:%22com.squareup.dagger%22+AND+a:%22dagger%22&core=gav&rows=1&wt=json",
-                    "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.squareup.dagger\\\" AND a:\\\"dagger\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":5,\"start\":0,\"docs\":[{\"id\":\"com.squareup.dagger:dagger:1.2.1\",\"g\":\"com.squareup.dagger\",\"a\":\"dagger\",\"v\":\"1.2.1\",\"p\":\"jar\",\"timestamp\":1392614597000,\"tags\":[\"dependency\",\"android\",\"injector\",\"java\",\"fast\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\"-tests.jar\",\".jar\",\".pom\"]}]}}");
-
-            assertEquals(""
-                    + "build.gradle:9: Warning: A newer version of joda-time:joda-time than 2.1 is available: 2.3 [NewerVersionAvailable]\n"
-                    + "    compile 'joda-time:joda-time:2.1'\n"
-                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                    + "build.gradle:10: Warning: A newer version of com.squareup.dagger:dagger than 1.2.0 is available: 1.2.1 [NewerVersionAvailable]\n"
-                    + "    compile 'com.squareup.dagger:dagger:1.2.0'\n"
-                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                    + "0 errors, 2 warnings\n",
-
-                    lintProject("gradle/RemoteVersions.gradle=>build.gradle"));
-        } finally {
-            GradleDetector.ourMockData = null;
-        }
-    }
-
-    public void testRemoteVersionsWithPreviews() throws Exception {
-        // If the most recent version is a rc version, query for all versions
-        mEnabled = Collections.singleton(REMOTE_VERSION);
-        try {
-            HashMap<String, String> data = Maps.newHashMap();
-            GradleDetector.ourMockData = data;
-            data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&rows=1&wt=json",
-                    "{\"responseHeader\":{\"status\":0,\"QTime\":0,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
-            data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&wt=json",
-                    "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0\",\"p\":\"bundle\",\"timestamp\":1398199666000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc2\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc2\",\"p\":\"bundle\",\"timestamp\":1397162341000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc1\",\"p\":\"bundle\",\"timestamp\":1396985408000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0.1\",\"p\":\"bundle\",\"timestamp\":1391467528000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0\",\"p\":\"bundle\",\"timestamp\":1389995088000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0-rc1\",\"p\":\"bundle\",\"timestamp\":1387495574000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:15.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0\",\"p\":\"bundle\",\"timestamp\":1378497169000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\",\"-cdi1.0.jar\"]},{\"id\":\"com.google.guava:guava:15.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0-rc1\",\"p\":\"bundle\",\"timestamp\":1377542588000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:14.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"14.0.1\",\"p\":\"bundle\",\"timestamp\":1363305439000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
-
-            assertEquals(""
-                    + "build.gradle:9: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 17.0 [NewerVersionAvailable]\n"
-                    + "    compile 'com.google.guava:guava:11.0.2'\n"
-                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                    + "build.gradle:10: Warning: A newer version of com.google.guava:guava than 16.0-rc1 is available: 18.0.0-rc1 [NewerVersionAvailable]\n"
-                    + "    compile 'com.google.guava:guava:16.0-rc1'\n"
-                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                    + "0 errors, 2 warnings\n",
-
-                    lintProject("gradle/RemoteVersions2.gradle=>build.gradle"));
-        } finally {
-            GradleDetector.ourMockData = null;
-        }
-    }
-
-    public void testPreviewVersions() throws Exception {
-        mEnabled = Collections.singleton(DEPENDENCY);
-        // This test only works when SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION contains
-        // a preview string:
-        if (!GRADLE_PLUGIN_RECOMMENDED_VERSION.startsWith("1.0.0-rc")) {
-            return;
-        }
-        assertEquals(""
-                        + "build.gradle:6: Warning: A newer version of com.android.tools.build:gradle than 1.0.0-rc0 is available: " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [GradleDependency]\n"
-                        + "        classpath 'com.android.tools.build:gradle:1.0.0-rc0'\n"
-                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "0 errors, 1 warnings\n",
-
-                lintProject("gradle/PreviewDependencies.gradle=>build.gradle"));
-    }
-
-
-    public void testDependenciesInVariables() throws Exception {
-        mEnabled = Collections.singleton(DEPENDENCY);
-        assertEquals(""
-                    + "build.gradle:10: Warning: A newer version of com.google.android.gms:play-services-wearable than 5.0.77 is available: 6.1.71 [GradleDependency]\n"
-                    + "    compile \"com.google.android.gms:play-services-wearable:${GPS_VERSION}\"\n"
-                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                    + "0 errors, 1 warnings\n",
-
-                lintProject("gradle/DependenciesVariable.gradle=>build.gradle"));
-    }
-
-    @Override
-    protected void checkReportedError(@NonNull Context context, @NonNull Issue issue,
-            @NonNull Severity severity, @Nullable Location location, @NonNull String message) {
-        if (issue == DEPENDENCY && message.startsWith("Using the appcompat library when ")) {
-            // No data embedded in this specific message
-            return;
-        }
-
-        // Issues we're supporting getOldFrom
-        if (issue == DEPENDENCY
-                || issue == STRING_INTEGER
-                || issue == DEPRECATED
-                || issue == PLUS) {
-            assertNotNull("Could not extract message tokens from " + message,
-                    GradleDetector.getOldValue(issue, message, TEXT));
-        }
-
-        if (issue == DEPENDENCY
-                || issue == STRING_INTEGER
-                || issue == DEPRECATED) {
-            assertNotNull("Could not extract message tokens from " + message,
-                    GradleDetector.getNewValue(issue, message, TEXT));
-        }
-
-        if (issue == COMPATIBILITY) {
-            if (message.startsWith("Version ")) {
-                assertNotNull("Could not extract message tokens from " + message,
-                        GradleDetector.getNewValue(issue, message, TEXT));
-            }
-        }
-    }
-
-    public void testGetNamedDependency() {
-        assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
-                "group: 'com.android.support', name: 'support-v4', version: '21.0.+'"
-        ));
-        assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
-                "name:'support-v4', group: \"com.android.support\", version: '21.0.+'"
-        ));
-        assertEquals("junit:junit:4.+", getNamedDependency(
-                "group: 'junit', name: 'junit', version: '4.+'"
-        ));
-        assertEquals("com.android.support:support-v4:19.0.+", getNamedDependency(
-                "group: 'com.android.support', name: 'support-v4', version: '19.0.+'"
-        ));
-        assertEquals("com.google.guava:guava:11.0.1", getNamedDependency(
-                "group: 'com.google.guava', name: 'guava', version: '11.0.1', transitive: false"
-        ));
-        assertEquals("com.google.api-client:google-api-client:1.6.0-beta", getNamedDependency(
-                "group: 'com.google.api-client', name: 'google-api-client', version: '1.6.0-beta', transitive: false"
-        ));
-        assertEquals("org.robolectric:robolectric:2.3-SNAPSHOT", getNamedDependency(
-                "group: 'org.robolectric', name: 'robolectric', version: '2.3-SNAPSHOT'"
-        ));
-    }
-
-    // -------------------------------------------------------------------------------------------
-    // Test infrastructure below here
-    // -------------------------------------------------------------------------------------------
-
-    static final Implementation IMPLEMENTATION = new Implementation(
-            GroovyGradleDetector.class,
-            Scope.GRADLE_SCOPE);
-    static {
-        for (Issue issue : new BuiltinIssueRegistry().getIssues()) {
-            if (issue.getImplementation().getDetectorClass() == GradleDetector.class) {
-                issue.setImplementation(IMPLEMENTATION);
-            }
-        }
-    }
-
-    @Override
-    protected Detector getDetector() {
-        return new GroovyGradleDetector();
-    }
-
-    private Set<Issue> mEnabled;
-
-    @Override
-    protected TestConfiguration getConfiguration(LintClient client, Project project) {
-        return new TestConfiguration(client, project, null) {
-            @Override
-            public boolean isEnabled(@NonNull Issue issue) {
-                return super.isEnabled(issue) && (mEnabled == null || mEnabled.contains(issue));
-            }
-        };
-    }
-
-    @Override
-    protected TestLintClient createClient() {
-        return new TestLintClient() {
-            @Nullable
-            @Override
-            public File getSdkHome() {
-                return getMockSupportLibraryInstallation();
-            }
-
-            @NonNull
-            @Override
-            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                if (!"testDependenciesInVariables".equals(getName())) {
-                    return super.createProject(dir, referenceDir);
-                }
-
-                return new Project(this, dir, referenceDir) {
-                    @Override
-                    public boolean isGradleProject() {
-                        return true;
-                    }
-
-                    @Nullable
-                    @Override
-                    public Variant getCurrentVariant() {
-                        /*
-                        Simulate variant which has an AndroidLibrary with
-                        resolved coordinates
-
-                        com.google.android.gms:play-services-wearable:5.0.77"
-                         */
-                        MavenCoordinates coordinates = mock(MavenCoordinates.class);
-                        when(coordinates.getGroupId()).thenReturn("com.google.android.gms");
-                        when(coordinates.getArtifactId()).thenReturn("play-services-wearable");
-                        when(coordinates.getVersion()).thenReturn("5.0.77");
-
-                        AndroidLibrary library = mock(AndroidLibrary.class);
-                        when(library.getResolvedCoordinates()).thenReturn(coordinates);
-                        List<AndroidLibrary> libraries = Collections.singletonList(library);
-
-                        Dependencies dependencies = mock(Dependencies.class);
-                        when(dependencies.getLibraries()).thenReturn(libraries);
-
-                        AndroidArtifact artifact = mock(AndroidArtifact.class);
-                        when(artifact.getDependencies()).thenReturn(dependencies);
-
-                        Variant variant = mock(Variant.class);
-                        when(variant.getMainArtifact()).thenReturn(artifact);
-                        return variant;
-                    }
-                };
-            }
-        };
-    }
-
-    // Copy of com.android.build.gradle.tasks.GroovyGradleDetector (with "static" added as
-    // a modifier, and the unused field IMPLEMENTATION removed, and with fail(t.toString())
-    // inserted into visitBuildScript's catch handler.
-    //
-    // THIS CODE DUPLICATION IS NOT AN IDEAL SITUATION! But, it's preferable to a lack of
-    // tests.
-    //
-    // A more proper fix would be to extract the groovy detector into a library shared by
-    // the testing framework and the gradle plugin.
-
-    public static class GroovyGradleDetector extends GradleDetector {
-        @Override
-        public void visitBuildScript(@NonNull final Context context, Map<String, Object> sharedData) {
-            try {
-                visitQuietly(context, sharedData);
-            } catch (Throwable t) {
-                // ignore
-                // Parsing the build script can involve class loading that we sometimes can't
-                // handle. This happens for example when running lint in build-system/tests/api/.
-                // This is a lint limitation rather than a user error, so don't complain
-                // about these. Consider reporting a Issue#LINT_ERROR.
-                fail(t.toString());
-            }
-        }
-
-        private void visitQuietly(@NonNull final Context context,
-                @SuppressWarnings("UnusedParameters") Map<String, Object> sharedData) {
-            String source = context.getContents();
-            if (source == null) {
-                return;
-            }
-
-            List<ASTNode> astNodes = new AstBuilder().buildFromString(source);
-            GroovyCodeVisitor visitor = new CodeVisitorSupport() {
-                private List<MethodCallExpression> mMethodCallStack = Lists.newArrayList();
-                @Override
-                public void visitMethodCallExpression(MethodCallExpression expression) {
-                    mMethodCallStack.add(expression);
-                    super.visitMethodCallExpression(expression);
-                    Expression arguments = expression.getArguments();
-                    String parent = expression.getMethodAsString();
-                    String parentParent = getParentParent();
-                    if (arguments instanceof ArgumentListExpression) {
-                        ArgumentListExpression ale = (ArgumentListExpression)arguments;
-                        List<Expression> expressions = ale.getExpressions();
-                        if (expressions.size() == 1 &&
-                                expressions.get(0) instanceof ClosureExpression) {
-                            if (isInterestingBlock(parent, parentParent)) {
-                                ClosureExpression closureExpression =
-                                        (ClosureExpression)expressions.get(0);
-                                Statement block = closureExpression.getCode();
-                                if (block instanceof BlockStatement) {
-                                    BlockStatement bs = (BlockStatement)block;
-                                    for (Statement statement : bs.getStatements()) {
-                                        if (statement instanceof ExpressionStatement) {
-                                            ExpressionStatement e = (ExpressionStatement)statement;
-                                            if (e.getExpression() instanceof MethodCallExpression) {
-                                                checkDslProperty(parent,
-                                                        (MethodCallExpression)e.getExpression(),
-                                                        parentParent);
-                                            }
-                                        } else if (statement instanceof ReturnStatement) {
-                                            // Single item in block
-                                            ReturnStatement e = (ReturnStatement)statement;
-                                            if (e.getExpression() instanceof MethodCallExpression) {
-                                                checkDslProperty(parent,
-                                                        (MethodCallExpression)e.getExpression(),
-                                                        parentParent);
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    } else if (arguments instanceof TupleExpression) {
-                        if (isInterestingStatement(parent, parentParent)) {
-                            TupleExpression te = (TupleExpression) arguments;
-                            Map<String, String> namedArguments = Maps.newHashMap();
-                            List<String> unnamedArguments = Lists.newArrayList();
-                            for (Expression subExpr : te.getExpressions()) {
-                                if (subExpr instanceof NamedArgumentListExpression) {
-                                    NamedArgumentListExpression nale = (NamedArgumentListExpression) subExpr;
-                                    for (MapEntryExpression mae : nale.getMapEntryExpressions()) {
-                                        namedArguments.put(mae.getKeyExpression().getText(),
-                                                mae.getValueExpression().getText());
-                                    }
-                                }
-                            }
-                            checkMethodCall(context, parent, parentParent, namedArguments, unnamedArguments, expression);
-                        }
-                    }
-                    assert !mMethodCallStack.isEmpty();
-                    assert mMethodCallStack.get(mMethodCallStack.size() - 1) == expression;
-                    mMethodCallStack.remove(mMethodCallStack.size() - 1);
-                }
-
-                private String getParentParent() {
-                    for (int i = mMethodCallStack.size() - 2; i >= 0; i--) {
-                        MethodCallExpression expression = mMethodCallStack.get(i);
-                        Expression arguments = expression.getArguments();
-                        if (arguments instanceof ArgumentListExpression) {
-                            ArgumentListExpression ale = (ArgumentListExpression)arguments;
-                            List<Expression> expressions = ale.getExpressions();
-                            if (expressions.size() == 1 &&
-                                    expressions.get(0) instanceof ClosureExpression) {
-                                return expression.getMethodAsString();
-                            }
-                        }
-                    }
-
-                    return null;
-                }
-
-                private void checkDslProperty(String parent, MethodCallExpression c,
-                        String parentParent) {
-                    String property = c.getMethodAsString();
-                    if (isInterestingProperty(property, parent, getParentParent())) {
-                        String value = getText(c.getArguments());
-                        checkDslPropertyAssignment(context, property, value, parent, parentParent, c, c);
-                    }
-                }
-
-                private String getText(ASTNode node) {
-                    String source = context.getContents();
-                    Pair<Integer, Integer> offsets = getOffsets(node, context);
-                    return source.substring(offsets.getFirst(), offsets.getSecond());
-                }
-            };
-
-            for (ASTNode node : astNodes) {
-                node.visit(visitor);
-            }
-        }
-
-        @NonNull
-        private static Pair<Integer, Integer> getOffsets(ASTNode node, Context context) {
-            if (node.getLastLineNumber() == -1 && node instanceof TupleExpression) {
-                // Workaround: TupleExpressions yield bogus offsets, so use its
-                // children instead
-                TupleExpression exp = (TupleExpression) node;
-                List<Expression> expressions = exp.getExpressions();
-                if (!expressions.isEmpty()) {
-                    return Pair.of(
-                        getOffsets(expressions.get(0), context).getFirst(),
-                        getOffsets(expressions.get(expressions.size() - 1), context).getSecond());
-                }
-            }
-            String source = context.getContents();
-            assert source != null; // because we successfully parsed
-            int start = 0;
-            int end = source.length();
-            int line = 1;
-            int startLine = node.getLineNumber();
-            int startColumn = node.getColumnNumber();
-            int endLine = node.getLastLineNumber();
-            int endColumn = node.getLastColumnNumber();
-            int column = 1;
-            for (int index = 0, len = end; index < len; index++) {
-                if (line == startLine && column == startColumn) {
-                    start = index;
-                }
-                if (line == endLine && column == endColumn) {
-                    end = index;
-                    break;
-                }
-
-                char c = source.charAt(index);
-                if (c == '\n') {
-                    line++;
-                    column = 1;
-                } else {
-                    column++;
-                }
-            }
-
-            return Pair.of(start, end);
-        }
-
-        @Override
-        protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
-            ASTNode node = (ASTNode) cookie;
-            Pair<Integer, Integer> offsets = getOffsets(node, context);
-            return offsets.getFirst();
-        }
-
-        @Override
-        protected Location createLocation(@NonNull Context context, @NonNull Object cookie) {
-            ASTNode node = (ASTNode) cookie;
-            Pair<Integer, Integer> offsets = getOffsets(node, context);
-            int fromLine = node.getLineNumber() - 1;
-            int fromColumn = node.getColumnNumber() - 1;
-            int toLine = node.getLastLineNumber() - 1;
-            int toColumn = node.getLastColumnNumber() - 1;
-            return Location.create(context.file,
-                    new DefaultPosition(fromLine, fromColumn, offsets.getFirst()),
-                    new DefaultPosition(toLine, toColumn, offsets.getSecond()));
-        }
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
deleted file mode 100644
index 631f246..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
+++ /dev/null
@@ -1,47 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings("javadoc")
-public class HandlerDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new HandlerDetector();
-    }
-
-    public void testRegistered() throws Exception {
-        assertEquals(
-            "src/test/pkg/HandlerTest.java:12: Warning: This Handler class should be static or leaks might occur (test.pkg.HandlerTest.Inner) [HandlerLeak]\n" +
-            "    public class Inner extends Handler { // ERROR\n" +
-            "                 ~~~~~\n" +
-            "src/test/pkg/HandlerTest.java:18: Warning: This Handler class should be static or leaks might occur (new android.os.Handler(){}) [HandlerLeak]\n" +
-            "        Handler anonymous = new Handler() { // ERROR\n" +
-            "                                          ^\n" +
-            "0 errors, 2 warnings\n",
-
-            lintProject(
-                "bytecode/HandlerTest.java.txt=>src/test/pkg/HandlerTest.java",
-                "bytecode/HandlerTest.class.data=>bin/classes/test/pkg/HandlerTest.class",
-                "bytecode/HandlerTest$Inner.class.data=>bin/classes/test/pkg/HandlerTest$Inner.class",
-                "bytecode/HandlerTest$StaticInner.class.data=>bin/classes/test/pkg/HandlerTest$StaticInner.class",
-                "bytecode/HandlerTest$WithArbitraryLooper.class.data=>bin/classes/test/pkg/HandlerTest$WithArbitraryLooper.class",
-                "bytecode/HandlerTest$1.class.data=>bin/classes/test/pkg/HandlerTest$1.class",
-                "bytecode/HandlerTest$2.class.data=>bin/classes/test/pkg/HandlerTest$2.class"));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
deleted file mode 100644
index e9d2e29..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
+++ /dev/null
@@ -1,812 +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.tools.lint.checks;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.build.FilterData;
-import com.android.build.OutputFile;
-import com.android.builder.model.AndroidArtifact;
-import com.android.builder.model.AndroidArtifactOutput;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ProductFlavor;
-import com.android.builder.model.ProductFlavorContainer;
-import com.android.builder.model.Variant;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.client.api.LintDriver;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Project;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import org.mockito.stubbing.OngoingStubbing;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@SuppressWarnings("javadoc")
-public class IconDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new IconDetector();
-    }
-
-    private Set<Issue> mEnabled = new HashSet<Issue>();
-    private boolean mAbbreviate;
-
-    private static final Set<Issue> ALL = new HashSet<Issue>();
-    static {
-        ALL.add(IconDetector.DUPLICATES_CONFIGURATIONS);
-        ALL.add(IconDetector.DUPLICATES_NAMES);
-        ALL.add(IconDetector.GIF_USAGE);
-        ALL.add(IconDetector.ICON_DENSITIES);
-        ALL.add(IconDetector.ICON_DIP_SIZE);
-        ALL.add(IconDetector.ICON_EXTENSION);
-        ALL.add(IconDetector.ICON_LOCATION);
-        ALL.add(IconDetector.ICON_MISSING_FOLDER);
-        ALL.add(IconDetector.ICON_NODPI);
-        ALL.add(IconDetector.ICON_COLORS);
-        ALL.add(IconDetector.ICON_XML_AND_PNG);
-        ALL.add(IconDetector.ICON_LAUNCHER_SHAPE);
-        ALL.add(IconDetector.ICON_MIX_9PNG);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mAbbreviate = true;
-    }
-
-    @Override
-    protected void configureDriver(LintDriver driver) {
-        driver.setAbbreviating(mAbbreviate);
-    }
-
-    @Override
-    protected TestConfiguration getConfiguration(LintClient client, Project project) {
-        return new TestConfiguration(client, project, null) {
-            @Override
-            public boolean isEnabled(@NonNull Issue issue) {
-                return super.isEnabled(issue) && mEnabled.contains(issue);
-            }
-        };
-    }
-
-    public void test() throws Exception {
-        mEnabled = ALL;
-        assertEquals(
-            "res/drawable-mdpi/sample_icon.gif: Warning: Using the .gif format for bitmaps is discouraged [GifUsage]\n" +
-            "res/drawable/ic_launcher.png: Warning: The ic_launcher.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable [IconDuplicatesConfig]\n" +
-            "    res/drawable-mdpi/ic_launcher.png: <No location-specific message\n" +
-            "res/drawable/ic_launcher.png: Warning: Found bitmap drawable res/drawable/ic_launcher.png in densityless folder [IconLocation]\n" +
-            "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n" +
-            "res: Warning: Missing density variation folders in res: drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
-            "0 errors, 5 warnings\n" +
-            "",
-
-            lintProject(
-                    // Use minSDK4 to ensure that we get warnings about missing drawables
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "res/drawable/ic_launcher.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
-                    "res/drawable-mdpi/sample_icon.gif",
-                    // Make a dummy file named .svn to make sure it doesn't get seen as
-                    // an icon name
-                    "res/drawable-mdpi/sample_icon.gif=>res/drawable-hdpi/.svn",
-                    "res/drawable-hdpi/ic_launcher.png"));
-    }
-
-    public void testMixed() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_XML_AND_PNG);
-        assertEquals(
-            "res/drawable/background.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/background.png, res/drawable/background.xml [IconXmlAndPng]\n" +
-            "    res/drawable-mdpi/background.png: <No location-specific message\n" +
-            "0 errors, 1 warnings\n",
-
-            lintProject(
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "apicheck/minsdk4.xml=>res/drawable/background.xml",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/background.png"));
-    }
-
-    public void testApi1() throws Exception {
-        mEnabled = ALL;
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    // manifest file which specifies uses sdk = 2
-                    "apicheck/minsdk2.xml=>AndroidManifest.xml",
-                    "res/drawable/ic_launcher.png"));
-    }
-
-    public void test2() throws Exception {
-        mEnabled = ALL;
-        assertEquals(
-            "res/drawable-hdpi/other.9.png: Warning: The following unrelated icon files have identical contents: appwidget_bg.9.png, other.9.png [IconDuplicates]\n" +
-            "    res/drawable-hdpi/appwidget_bg.9.png: <No location-specific message\n" +
-            "res/drawable-hdpi/unrelated.png: Warning: The following unrelated icon files have identical contents: ic_launcher.png, unrelated.png [IconDuplicates]\n" +
-            "    res/drawable-hdpi/ic_launcher.png: <No location-specific message\n" +
-            "res: Warning: Missing density variation folders in res: drawable-mdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
-            "0 errors, 3 warnings\n",
-
-            lintProject(
-                    "res/drawable-hdpi/unrelated.png",
-                    "res/drawable-hdpi/appwidget_bg.9.png",
-                    "res/drawable-hdpi/appwidget_bg_focus.9.png",
-                    "res/drawable-hdpi/other.9.png",
-                    "res/drawable-hdpi/ic_launcher.png"
-                    ));
-    }
-
-    public void testNoDpi() throws Exception {
-        mEnabled = ALL;
-        assertEquals(
-            "res/drawable-mdpi/frame.png: Warning: The following images appear in both -nodpi and in a density folder: frame.png [IconNoDpi]\n" +
-            "res/drawable-xlarge-nodpi-v11/frame.png: Warning: The frame.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable-nodpi, drawable-xlarge-nodpi-v11 [IconDuplicatesConfig]\n" +
-            "    res/drawable-nodpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
-            "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
-            "0 errors, 3 warnings\n" +
-            "",
-
-            lintProject(
-                "res/drawable-mdpi/frame.png",
-                "res/drawable-nodpi/frame.png",
-                "res/drawable-xlarge-nodpi-v11/frame.png"));
-    }
-
-    public void testNoDpi2() throws Exception {
-        mEnabled = ALL;
-        // Having additional icon names in the no-dpi folder should not cause any complaints
-        assertEquals(
-            "res/drawable-xxxhdpi/frame.png: Warning: The image frame.png varies significantly in its density-independent (dip) size across the various density versions: drawable-ldpi/frame.png: 629x387 dp (472x290 px), drawable-mdpi/frame.png: 472x290 dp (472x290 px), drawable-hdpi/frame.png: 315x193 dp (472x290 px), drawable-xhdpi/frame.png: 236x145 dp (472x290 px), drawable-xxhdpi/frame.png: 157x97 dp (472x290 px), drawable-xxxhdpi/frame.png: 118x73 dp (472x290 px) [IconDipSize]\n" +
-            "    res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-xhdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-hdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-ldpi/frame.png: <No location-specific message\n" +
-            "res/drawable-xxxhdpi/frame.png: Warning: The following unrelated icon files have identical contents: frame.png, frame.png, frame.png, file1.png, file2.png, frame.png, frame.png, frame.png [IconDuplicates]\n" +
-            "    res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-xhdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-nodpi/file2.png: <No location-specific message\n" +
-            "    res/drawable-nodpi/file1.png: <No location-specific message\n" +
-            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-ldpi/frame.png: <No location-specific message\n" +
-            "    res/drawable-hdpi/frame.png: <No location-specific message\n" +
-            "0 errors, 2 warnings\n" +
-            "",
-
-            lintProject(
-                    "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-hdpi/frame.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-ldpi/frame.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-xhdpi/frame.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-xxhdpi/frame.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-xxxhdpi/frame.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file1.png",
-                    "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file2.png"));
-    }
-
-    public void testNoDpiMix() throws Exception {
-        mEnabled = ALL;
-        assertEquals(
-            "res/drawable-mdpi/frame.xml: Warning: The following images appear in both -nodpi and in a density folder: frame.png, frame.xml [IconNoDpi]\n" +
-            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
-            "res/drawable-nodpi/frame.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/frame.png, res/drawable-nodpi/frame.xml [IconXmlAndPng]\n" +
-            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
-            "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
-            "0 errors, 3 warnings\n",
-
-            lintProject(
-                "res/drawable-mdpi/frame.png",
-                "res/drawable/states.xml=>res/drawable-nodpi/frame.xml"));
-    }
-
-
-    public void testMixedFormat() throws Exception {
-        mEnabled = ALL;
-        // Test having a mixture of .xml and .png resources for the same name
-        // Make sure we don't get:
-        // drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: f.png (found in drawable-mdpi)
-        // drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: f.png (found in drawable-mdpi)
-        assertEquals(
-            "res/drawable-xxxhdpi/f.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-hdpi/f.xml, res/drawable-mdpi/f.png [IconXmlAndPng]\n" +
-            "    res/drawable-xxhdpi/f.xml: <No location-specific message\n" +
-            "    res/drawable-xhdpi/f.xml: <No location-specific message\n" +
-            "    res/drawable-mdpi/f.png: <No location-specific message\n" +
-            "    res/drawable-hdpi/f.xml: <No location-specific message\n" +
-            "0 errors, 1 warnings\n",
-
-            lintProject(
-                    "res/drawable-mdpi/frame.png=>res/drawable-mdpi/f.png",
-                    "res/drawable/states.xml=>res/drawable-hdpi/f.xml",
-                    "res/drawable/states.xml=>res/drawable-xhdpi/f.xml",
-                    "res/drawable/states.xml=>res/drawable-xxhdpi/f.xml",
-                    "res/drawable/states.xml=>res/drawable-xxxhdpi/f.xml"));
-    }
-
-    public void testMisleadingFileName() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_EXTENSION);
-        assertEquals(
-            "res/drawable-mdpi/frame.gif: Warning: Misleading file extension; named .gif but the file format is png [IconExtension]\n" +
-            "res/drawable-mdpi/frame.jpg: Warning: Misleading file extension; named .jpg but the file format is png [IconExtension]\n" +
-            "res/drawable-mdpi/myjpg.png: Warning: Misleading file extension; named .png but the file format is JPEG [IconExtension]\n" +
-            "res/drawable-mdpi/sample_icon.jpeg: Warning: Misleading file extension; named .jpeg but the file format is gif [IconExtension]\n" +
-            "res/drawable-mdpi/sample_icon.jpg: Warning: Misleading file extension; named .jpg but the file format is gif [IconExtension]\n" +
-            "res/drawable-mdpi/sample_icon.png: Warning: Misleading file extension; named .png but the file format is gif [IconExtension]\n" +
-            "0 errors, 6 warnings\n",
-
-            lintProject(
-                "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpg", // VALID
-                "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpeg", // VALID
-                "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.gif",
-                "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.jpg",
-                "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.png",
-                "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpg",
-                "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpeg",
-                "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.png"));
-    }
-
-    public void testColors() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals(
-            "res/drawable-mdpi/ic_menu_my_action.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
-            "res/drawable-mdpi-v11/ic_stat_my_notification.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "res/drawable-mdpi-v9/ic_stat_my_notification2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "0 errors, 3 warnings\n",
-
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_menu_my_action.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi-v11/ic_stat_my_notification.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi-v9/ic_stat_my_notification2.png",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
-    }
-
-    public void testNotActionBarIcons() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals(
-            "No warnings.",
-
-            // No Java code designates the menu as an action bar menu
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "res/menu/menu.xml",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
-    }
-
-    public void testActionBarIcons() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals(
-            "res/drawable-mdpi/icon1.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
-            "res/drawable-mdpi/icon2.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
-            "0 errors, 2 warnings\n",
-
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "res/menu/menu.xml",
-                "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
-    }
-
-    public void testOkActionBarIcons() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "res/menu/menu.xml",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png"));
-    }
-
-    public void testNotificationIcons() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals(
-            "res/drawable-mdpi/icon1.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "res/drawable-mdpi/icon2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "res/drawable-mdpi/icon3.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "res/drawable-mdpi/icon4.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "res/drawable-mdpi/icon5.png: Warning: Notification icons must be entirely white [IconColors]\n" +
-            "0 errors, 5 warnings\n",
-
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon4.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon5.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon6.png", // not a notification
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon7.png", // ditto
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
-    }
-
-    public void testOkNotificationIcons() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon3.png",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon4.png",
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon5.png"));
-    }
-
-    public void testExpectedSize() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_EXPECTED_SIZE);
-        assertEquals(
-            "res/drawable-mdpi/ic_launcher.png: Warning: Incorrect icon size for drawable-mdpi/ic_launcher.png: expected 48x48, but was 24x24 [IconExpectedSize]\n" +
-            "res/drawable-mdpi/icon1.png: Warning: Incorrect icon size for drawable-mdpi/icon1.png: expected 32x32, but was 48x48 [IconExpectedSize]\n" +
-            "res/drawable-mdpi/icon3.png: Warning: Incorrect icon size for drawable-mdpi/icon3.png: expected 24x24, but was 48x48 [IconExpectedSize]\n" +
-            "0 errors, 3 warnings\n",
-
-            lintProject(
-                "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
-                "res/menu/menu.xml",
-                "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
-
-                // 3 wrong-sized icons:
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
-                "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/ic_launcher.png",
-
-                // OK sizes
-                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
-                "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/icon4.png",
-                "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
-            ));
-    }
-
-    public void testAbbreviate() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
-        assertEquals(
-            "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
-            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
-            "ic_launcher3.png... (6 more) [IconDensities]\n" +
-            "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
-            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
-            "ic_launcher3.png... (6 more) [IconDensities]\n" +
-            "0 errors, 2 warnings\n",
-
-            lintProject(
-                    // Use minSDK4 to ensure that we get warnings about missing drawables
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.webp",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
-            ));
-    }
-
-    public void testShowAll() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
-        mAbbreviate = false;
-        assertEquals(
-            "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
-            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
-            "ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
-            "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
-            "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
-            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png," +
-            " ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
-            "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
-            "0 errors, 2 warnings\n",
-
-            lintProject(
-                    // Use minSDK4 to ensure that we get warnings about missing drawables
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
-            ));
-    }
-
-    public void testIgnoreMissingFolders() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    // Use minSDK4 to ensure that we get warnings about missing drawables
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "ignoremissing.xml=>lint.xml",
-                    "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
-                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
-            ));
-    }
-
-    public void testSquareLauncher() throws Exception {
-        mEnabled = Collections.singleton(IconDetector.ICON_LAUNCHER_SHAPE);
-        assertEquals(
-            "res/drawable-hdpi/ic_launcher_filled.png: Warning: Launcher icons should not fill every pixel of their square region; see the design guide for details [IconLauncherShape]\n" +
-            "0 errors, 1 warnings\n",
-
-            lintProject(
-                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                    "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
-                    "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
-            ));
-    }
-
-    public void testMixNinePatch() throws Exception {
-        // https://code.google.com/p/android/issues/detail?id=43075
-        mEnabled = Collections.singleton(IconDetector.ICON_MIX_9PNG);
-        assertEquals(""
-                + "res/drawable-mdpi/ic_launcher_filled.png: Warning: The files ic_launcher_filled.png and ic_launcher_filled.9.png clash; both will map to @drawable/ic_launcher_filled [IconMixedNinePatch]\n"
-                + "    res/drawable-hdpi/ic_launcher_filled.png: <No location-specific message\n"
-                + "    res/drawable-hdpi/ic_launcher_filled.9.png: <No location-specific message\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject(
-                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                        "res/drawable-hdpi/filled.png=>res/drawable-mdpi/ic_launcher_filled.png",
-                        "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
-                        "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.9.png",
-                        "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
-                ));
-    }
-
-    public void test67486() throws Exception {
-        // Regression test for https://code.google.com/p/android/issues/detail?id=67486
-        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
-        assertEquals("No warnings.",
-
-                lintProject(
-                        "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                        "res/drawable-xhdpi/ic_stat_notify.png=>res/drawable-xhdpi/ic_stat_notify.png"
-                ));
-    }
-
-    public void testDuplicatesWithDpNames() throws Exception {
-        // Regression test for https://code.google.com/p/android/issues/detail?id=74584
-        mEnabled = Collections.singleton(IconDetector.DUPLICATES_NAMES);
-        assertEquals("No warnings.",
-
-                lintProject(
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png",
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png"
-                ));
-    }
-
-    public void testClaimedSize() throws Exception {
-        // Check that icons which declare a dp size actually correspond to that dp size
-        mEnabled = Collections.singleton(IconDetector.ICON_DIP_SIZE);
-        assertEquals(""
-                + "res/drawable-xhdpi/foo_30dp.png: Warning: Suspicious file name foo_30dp.png: The implied 30 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-xhdpi folder computes to 36\u00d736 dp) [IconDipSize]\n"
-                + "res/drawable-mdpi/foo_80dp.png: Warning: Suspicious file name foo_80dp.png: The implied 80 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-mdpi folder computes to 72\u00d772 dp) [IconDipSize]\n"
-                + "0 errors, 2 warnings\n",
-
-                lintProject(
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png", // ok
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_80dp.png", // wrong
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png",  // ok
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_35dp.png",  // ~ok
-                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_30dp.png"  // wrong
-                ));
-    }
-
-    public void testResConfigs1() throws Exception {
-        // resConfigs in the Gradle model sets up the specific set of resource configs
-        // that are included in the packaging: we use this to limit the set of required
-        // densities
-        mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
-        assertEquals(""
-                + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject(
-                        "res/drawable-mdpi/frame.png",
-                        "res/drawable-nodpi/frame.png",
-                        "res/drawable-xlarge-nodpi-v11/frame.png"));
-    }
-
-    public void testResConfigs2() throws Exception {
-        mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
-        assertEquals(""
-                + "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject(
-                        // Use minSDK4 to ensure that we get warnings about missing drawables
-                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
-                        "res/drawable/ic_launcher.png",
-                        "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
-                        "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher.png",
-                        "res/drawable-mdpi/sample_icon.gif",
-                        "res/drawable-hdpi/ic_launcher.png"));
-    }
-
-    public void testSplits1() throws Exception {
-        // splits in the Gradle model sets up the specific set of resource configs
-        // that are included in the packaging: we use this to limit the set of required
-        // densities
-        mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
-        assertEquals(""
-                        + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
-                        + "0 errors, 1 warnings\n",
-
-                lintProject(
-                        "res/drawable-mdpi/frame.png",
-                        "res/drawable-nodpi/frame.png",
-                        "res/drawable-xlarge-nodpi-v11/frame.png"));
-    }
-
-    @Override
-    protected TestLintClient createClient() {
-        String testName = getName();
-        if (testName.startsWith("testResConfigs")) {
-            return createClientForTestResConfigs();
-        } else if (testName.startsWith("testSplits")) {
-            return createClientForTestSplits();
-        } else {
-            return super.createClient();
-        }
-    }
-
-    private TestLintClient createClientForTestResConfigs() {
-
-        // Set up a mock project model for the resource configuration test(s)
-        // where we provide a subset of densities to be included
-
-        return new TestLintClient() {
-            @NonNull
-            @Override
-            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                return new Project(this, dir, referenceDir) {
-                    @Override
-                    public boolean isGradleProject() {
-                        return true;
-                    }
-
-                    @Nullable
-                    @Override
-                    public AndroidProject getGradleProjectModel() {
-                        /*
-                        Simulate variant freeBetaDebug in this setup:
-                            defaultConfig {
-                                ...
-                                resConfigs "mdpi"
-                            }
-                            flavorDimensions  "pricing", "releaseType"
-                            productFlavors {
-                                beta {
-                                    flavorDimension "releaseType"
-                                    resConfig "en"
-                                    resConfigs "nodpi", "hdpi"
-                                }
-                                normal { flavorDimension "releaseType" }
-                                free { flavorDimension "pricing" }
-                                paid { flavorDimension "pricing" }
-                            }
-                         */
-                        ProductFlavor flavorFree = mock(ProductFlavor.class);
-                        when(flavorFree.getName()).thenReturn("free");
-                        when(flavorFree.getResourceConfigurations())
-                                .thenReturn(Collections.<String>emptyList());
-
-                        ProductFlavor flavorNormal = mock(ProductFlavor.class);
-                        when(flavorNormal.getName()).thenReturn("normal");
-                        when(flavorNormal.getResourceConfigurations())
-                                .thenReturn(Collections.<String>emptyList());
-
-                        ProductFlavor flavorPaid = mock(ProductFlavor.class);
-                        when(flavorPaid.getName()).thenReturn("paid");
-                        when(flavorPaid.getResourceConfigurations())
-                                .thenReturn(Collections.<String>emptyList());
-
-                        ProductFlavor flavorBeta = mock(ProductFlavor.class);
-                        when(flavorBeta.getName()).thenReturn("beta");
-                        List<String> resConfigs = Arrays.asList("hdpi", "en", "nodpi");
-                        when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
-
-                        ProductFlavor defaultFlavor = mock(ProductFlavor.class);
-                        when(defaultFlavor.getName()).thenReturn("main");
-                        when(defaultFlavor.getResourceConfigurations()).thenReturn(
-                                Collections.singleton("mdpi"));
-
-                        ProductFlavorContainer containerBeta =
-                                mock(ProductFlavorContainer.class);
-                        when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
-
-                        ProductFlavorContainer containerFree =
-                                mock(ProductFlavorContainer.class);
-                        when(containerFree.getProductFlavor()).thenReturn(flavorFree);
-
-                        ProductFlavorContainer containerPaid =
-                                mock(ProductFlavorContainer.class);
-                        when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
-
-                        ProductFlavorContainer containerNormal =
-                                mock(ProductFlavorContainer.class);
-                        when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
-
-                        ProductFlavorContainer defaultContainer =
-                                mock(ProductFlavorContainer.class);
-                        when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
-
-                        List<ProductFlavorContainer> containers = Arrays.asList(
-                                containerPaid, containerFree, containerNormal, containerBeta
-                        );
-
-                        AndroidProject project = mock(AndroidProject.class);
-                        when(project.getProductFlavors()).thenReturn(containers);
-                        when(project.getDefaultConfig()).thenReturn(defaultContainer);
-                        return project;
-                    }
-
-                    @Nullable
-                    @Override
-                    public Variant getCurrentVariant() {
-                        List<String> productFlavorNames = Arrays.asList("free", "beta");
-                        Variant mock = mock(Variant.class);
-                        when(mock.getProductFlavors()).thenReturn(productFlavorNames);
-                        return mock;
-                    }
-                };
-            }
-        };
-    }
-
-    private TestLintClient createClientForTestSplits() {
-
-        // Set up a mock project model for the resource configuration test(s)
-        // where we provide a subset of densities to be included
-
-        return new TestLintClient() {
-            @NonNull
-            @Override
-            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                return new Project(this, dir, referenceDir) {
-                    @Override
-                    public boolean isGradleProject() {
-                        return true;
-                    }
-
-                    @Nullable
-                    @Override
-                    public AndroidProject getGradleProjectModel() {
-                        /*
-                            Simulate variant debug in this setup:
-                            splits {
-                                density {
-                                    enable true
-                                    reset()
-                                    include "mdpi", "hdpi"
-                                }
-                            }
-                         */
-
-                        ProductFlavor defaultFlavor = mock(ProductFlavor.class);
-                        when(defaultFlavor.getName()).thenReturn("main");
-                        when(defaultFlavor.getResourceConfigurations()).thenReturn(
-                                Collections.<String>emptyList());
-
-                        ProductFlavorContainer defaultContainer =
-                                mock(ProductFlavorContainer.class);
-                        when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
-
-                        AndroidProject project = mock(AndroidProject.class);
-                        when(project.getProductFlavors()).thenReturn(
-                                Collections.<ProductFlavorContainer>emptyList());
-                        when(project.getDefaultConfig()).thenReturn(defaultContainer);
-                        return project;
-                    }
-
-                    @Nullable
-                    @Override
-                    public Variant getCurrentVariant() {
-                        Collection<AndroidArtifactOutput> outputs = Lists.newArrayList();
-
-                        outputs.add(createAndroidArtifactOutput("", ""));
-                        outputs.add(createAndroidArtifactOutput("DENSITY", "mdpi"));
-                        outputs.add(createAndroidArtifactOutput("DENSITY", "hdpi"));
-
-                        AndroidArtifact mainArtifact = mock(AndroidArtifact.class);
-                        when(mainArtifact.getOutputs()).thenReturn(outputs);
-
-                        List<String> productFlavorNames = Collections.emptyList();
-                        Variant mock = mock(Variant.class);
-                        when(mock.getProductFlavors()).thenReturn(productFlavorNames);
-                        when(mock.getMainArtifact()).thenReturn(mainArtifact);
-                        return mock;
-                    }
-
-                    private AndroidArtifactOutput createAndroidArtifactOutput(
-                            @NonNull String filterType,
-                            @NonNull String identifier) {
-                        AndroidArtifactOutput artifactOutput = mock(
-                                AndroidArtifactOutput.class);
-
-                        OutputFile outputFile = mock(OutputFile.class);
-                        if (filterType.isEmpty()) {
-                            when(outputFile.getFilterTypes())
-                                    .thenReturn(Collections.<String>emptyList());
-                            when(outputFile.getFilters())
-                                    .thenReturn(Collections.<FilterData>emptyList());
-                        } else {
-                            when(outputFile.getFilterTypes())
-                                    .thenReturn(Collections.singletonList(filterType));
-                            List<FilterData> filters = Lists.newArrayList();
-                            FilterData filter = mock(FilterData.class);
-                            when(filter.getFilterType()).thenReturn(filterType);
-                            when(filter.getIdentifier()).thenReturn(identifier);
-                            filters.add(filter);
-                            when(outputFile.getFilters()).thenReturn(filters);
-                        }
-
-                        // Work around wildcard capture
-                        //when(artifactOutput.getOutputs()).thenReturn(outputFiles);
-                        List<OutputFile> outputFiles = Collections.singletonList(outputFile);
-                        OngoingStubbing<? extends Collection<? extends OutputFile>> when = when(
-                                artifactOutput.getOutputs());
-                        //noinspection unchecked
-                        ((OngoingStubbing<Collection<? extends OutputFile>>) when)
-                                .thenReturn(outputFiles);
-
-                        return artifactOutput;
-                    }
-                };
-            }
-        };
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
deleted file mode 100644
index 766546e..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
+++ /dev/null
@@ -1,778 +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.tools.lint.checks;
-
-import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ApiVersion;
-import com.android.builder.model.BuildType;
-import com.android.builder.model.BuildTypeContainer;
-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 com.android.builder.model.Variant;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Project;
-import com.google.common.collect.Lists;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@SuppressWarnings("javadoc")
-public class ManifestDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new ManifestDetector();
-    }
-
-    private Set<Issue> mEnabled = new HashSet<Issue>();
-
-    @Override
-    protected TestConfiguration getConfiguration(LintClient client, Project project) {
-        return new TestConfiguration(client, project, null) {
-            @Override
-            public boolean isEnabled(@NonNull Issue issue) {
-                return super.isEnabled(issue) && mEnabled.contains(issue);
-            }
-        };
-    }
-
-    public void testOrderOk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.ORDER);
-        assertEquals(
-                "No warnings.",
-                lintProject(
-                        "AndroidManifest.xml",
-                        "res/values/strings.xml"));
-    }
-
-    public void testBrokenOrder() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.ORDER);
-        assertEquals(
-            "AndroidManifest.xml:16: Warning: <uses-sdk> tag appears after <application> tag [ManifestOrder]\n" +
-            "   <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
-            "   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n" +
-            "",
-
-            lintProject(
-                    "broken-manifest.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testMissingUsesSdk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
-        assertEquals(
-            "AndroidManifest.xml: Warning: Manifest should specify a minimum API level with <uses-sdk android:minSdkVersion=\"?\" />; if it really supports all versions of Android set it to 1. [UsesMinSdkAttributes]\n" +
-            "0 errors, 1 warnings\n",
-            lintProject(
-                    "missingusessdk.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testMissingUsesSdkInGradle() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
-        assertEquals(""
-                + "No warnings.",
-                lintProject("missingusessdk.xml=>AndroidManifest.xml",
-                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
-    }
-
-    public void testMissingMinSdk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
-        assertEquals(
-            "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a minimum API level with android:minSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
-            "    <uses-sdk android:targetSdkVersion=\"10\" />\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n" +
-            "",
-            lintProject(
-                    "missingmin.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testMissingTargetSdk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
-        assertEquals(
-            "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
-            "    <uses-sdk android:minSdkVersion=\"10\" />\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n",
-            lintProject(
-                    "missingtarget.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testOldTargetSdk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.TARGET_NEWER);
-        assertEquals(
-            "AndroidManifest.xml:7: Warning: Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details. [OldTargetApi]\n" +
-            "    <uses-sdk android:minSdkVersion=\"10\" android:targetSdkVersion=\"14\" />\n" +
-            "                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n",
-            lintProject(
-                    "oldtarget.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testMultipleSdk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.MULTIPLE_USES_SDK);
-        assertEquals(
-            "AndroidManifest.xml:8: Error: There should only be a single <uses-sdk> element in the manifest: merge these together [MultipleUsesSdk]\n" +
-            "    <uses-sdk android:targetSdkVersion=\"14\" />\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "    AndroidManifest.xml:7: Also appears here\n" +
-            "    AndroidManifest.xml:9: Also appears here\n" +
-            "1 errors, 0 warnings\n",
-
-            lintProject(
-                    "multiplesdk.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testWrongLocation() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.WRONG_PARENT);
-        assertEquals(
-            "AndroidManifest.xml:8: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:9: Error: The <uses-permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <uses-permission />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:10: Error: The <permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <permission />\n" +
-            "       ~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:11: Error: The <permission-tree> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <permission-tree />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:12: Error: The <permission-group> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <permission-group />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:14: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <uses-sdk />\n" +
-            "       ~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:15: Error: The <uses-configuration> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <uses-configuration />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:16: Error: The <uses-feature> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <uses-feature />\n" +
-            "       ~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:17: Error: The <supports-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <supports-screens />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:18: Error: The <compatible-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <compatible-screens />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:19: Error: The <supports-gl-texture> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
-            "       <supports-gl-texture />\n" +
-            "       ~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:24: Error: The <uses-library> element must be a direct child of the <application> element [WrongManifestParent]\n" +
-            "   <uses-library />\n" +
-            "   ~~~~~~~~~~~~~~~~\n" +
-            "AndroidManifest.xml:25: Error: The <activity> element must be a direct child of the <application> element [WrongManifestParent]\n" +
-            "   <activity android:name=\".HelloWorld\"\n" +
-            "   ^\n" +
-            "13 errors, 0 warnings\n" +
-            "",
-
-            lintProject("broken-manifest2.xml=>AndroidManifest.xml"));
-    }
-
-    public void testDuplicateActivity() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
-        assertEquals(
-            "AndroidManifest.xml:16: Error: Duplicate registration for activity com.example.helloworld.HelloWorld [DuplicateActivity]\n" +
-            "       <activity android:name=\"com.example.helloworld.HelloWorld\"\n" +
-            "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n" +
-            "",
-
-            lintProject(
-                    "duplicate-manifest.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testDuplicateActivityAcrossSourceSets() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
-        File master = getProjectDir("MasterProject",
-                // Master project
-                "AndroidManifest.xml=>AndroidManifest.xml",
-                "multiproject/main-merge.properties=>project.properties",
-                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
-        );
-        File library = getProjectDir("LibraryProject",
-                // Library project
-                "AndroidManifest.xml=>AndroidManifest.xml",
-                "multiproject/library.properties=>project.properties",
-                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
-                "multiproject/strings.xml=>res/values/strings.xml"
-        );
-        assertEquals("No warnings.",
-                checkLint(Arrays.asList(master, library)));
-    }
-
-    public void testIgnoreDuplicateActivity() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "duplicate-manifest-ignore.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testAllowBackup() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
-        assertEquals(
-                "AndroidManifest.xml:9: Warning: Should explicitly set android:allowBackup to " +
-                "true or false (it's true by default, and that can have some security " +
-                "implications for the application's data) [AllowBackup]\n" +
-                "    <application\n" +
-                "    ^\n" +
-                "0 errors, 1 warnings\n",
-                lintProject(
-                        "AndroidManifest.xml",
-                        "apicheck/minsdk14.xml=>AndroidManifest.xml",
-                        "res/values/strings.xml"));
-    }
-
-    public void testAllowBackupOk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
-        assertEquals(
-                "No warnings.",
-                lintProject(
-                        "allowbackup.xml=>AndroidManifest.xml",
-                        "res/values/strings.xml"));
-    }
-
-    public void testAllowBackupOk2() throws Exception {
-        // Requires build api >= 4
-        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
-        assertEquals(
-                "No warnings.",
-                lintProject(
-                        "apicheck/minsdk1.xml=>AndroidManifest.xml",
-                        "res/values/strings.xml"));
-    }
-
-    public void testAllowBackupOk3() throws Exception {
-        // Not flagged in library projects
-        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
-        assertEquals(
-                "No warnings.",
-                lintProject(
-                        "AndroidManifest.xml",
-                        "multiproject/library.properties=>project.properties",
-                        "res/values/strings.xml"));
-    }
-
-    public void testAllowIgnore() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
-        assertEquals(
-                "No warnings.",
-                lintProject(
-                        "allowbackup_ignore.xml=>AndroidManifest.xml",
-                        "res/values/strings.xml"));
-    }
-
-    public void testDuplicatePermissions() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
-        assertEquals(
-                "AndroidManifest.xml:12: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
-                "    <permission android:name=\"bar.permission.SEND_SMS\"\n" +
-                "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-                "    AndroidManifest.xml:9: Previous permission here\n" +
-                "1 errors, 0 warnings\n",
-
-                lintProject(
-                        "duplicate_permissions1.xml=>AndroidManifest.xml",
-                        "res/values/strings.xml"));
-    }
-
-    public void testDuplicatePermissionsMultiProject() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
-
-        File master = getProjectDir("MasterProject",
-                // Master project
-                "duplicate_permissions2.xml=>AndroidManifest.xml",
-                "multiproject/main-merge.properties=>project.properties",
-                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
-        );
-        File library = getProjectDir("LibraryProject",
-                // Library project
-                "duplicate_permissions3.xml=>AndroidManifest.xml",
-                "multiproject/library.properties=>project.properties",
-                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
-                "multiproject/strings.xml=>res/values/strings.xml"
-        );
-        assertEquals(
-                "LibraryProject/AndroidManifest.xml:9: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
-                "    <permission android:name=\"bar.permission.SEND_SMS\"\n" +
-                "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-                "1 errors, 0 warnings\n",
-
-           checkLint(Arrays.asList(master, library)));
-    }
-
-    public void testMissingVersion() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
-        assertEquals(""
-            + "AndroidManifest.xml:2: Warning: Should set android:versionCode to specify the application version [MissingVersion]\n"
-            + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
-            + "^\n"
-            + "AndroidManifest.xml:2: Warning: Should set android:versionName to specify the application version [MissingVersion]\n"
-            + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
-            + "^\n"
-            + "0 errors, 2 warnings\n",
-            lintProject("no_version.xml=>AndroidManifest.xml"));
-    }
-
-    public void testVersionNotMissingInGradleProjects() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
-        assertEquals(""
-            + "No warnings.",
-            lintProject("no_version.xml=>AndroidManifest.xml",
-                    "multiproject/library.properties=>build.gradle")); // dummy; only name counts
-    }
-
-    public void testIllegalReference() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.ILLEGAL_REFERENCE);
-        assertEquals(""
-            + "AndroidManifest.xml:4: Warning: The android:versionCode cannot be a resource url, it must be a literal integer [IllegalResourceRef]\n"
-            + "    android:versionCode=\"@dimen/versionCode\"\n"
-            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "AndroidManifest.xml:7: Warning: The android:minSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
-            + "    <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
-            + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "AndroidManifest.xml:7: Warning: The android:targetSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
-            + "    <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
-            + "                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "0 errors, 3 warnings\n",
-
-            lintProject("illegal_version.xml=>AndroidManifest.xml"));
-    }
-
-    public void testDuplicateUsesFeature() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
-        assertEquals(
-            "AndroidManifest.xml:11: Warning: Duplicate declaration of uses-feature android.hardware.camera [DuplicateUsesFeature]\n" +
-            "    <uses-feature android:name=\"android.hardware.camera\"/>\n" +
-            "                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 1 warnings\n",
-            lintProject(
-                    "duplicate_uses_feature.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testDuplicateUsesFeatureOk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
-        assertEquals(
-            "No warnings.",
-            lintProject(
-                    "duplicate_uses_feature_ok.xml=>AndroidManifest.xml",
-                    "res/values/strings.xml"));
-    }
-
-    public void testMissingApplicationIcon() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
-        assertEquals(
-            "AndroidManifest.xml:9: Warning: Should explicitly set android:icon, there is no default [MissingApplicationIcon]\n" +
-            "    <application\n" +
-            "    ^\n" +
-            "0 errors, 1 warnings\n",
-            lintProject(
-                "missing_application_icon.xml=>AndroidManifest.xml",
-                "res/values/strings.xml"));
-    }
-
-    public void testMissingApplicationIconInLibrary() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
-        assertEquals(
-            "No warnings.",
-            lintProject(
-                "missing_application_icon.xml=>AndroidManifest.xml",
-                "multiproject/library.properties=>project.properties",
-                "res/values/strings.xml"));
-    }
-
-    public void testMissingApplicationIconOk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
-        assertEquals(
-            "No warnings.",
-            lintProject(
-                "AndroidManifest.xml",
-                "res/values/strings.xml"));
-    }
-
-    public void testDeviceAdmin() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.DEVICE_ADMIN);
-        assertEquals(""
-                + "AndroidManifest.xml:31: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
-                + "            <meta-data android:name=\"android.app.device_admin\"\n"
-                + "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "AndroidManifest.xml:44: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
-                + "            <meta-data android:name=\"android.app.device_admin\"\n"
-                + "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "AndroidManifest.xml:56: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
-                + "            <meta-data android:name=\"android.app.device_admin\"\n"
-                + "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 3 warnings\n",
-                lintProject("deviceadmin.xml=>AndroidManifest.xml"));
-    }
-
-    public void testMockLocations() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
-        assertEquals(""
-                + "AndroidManifest.xml:9: Error: Mock locations should only be requested in a test or debug-specific manifest file (typically src/debug/AndroidManifest.xml) [MockLocation]\n"
-                + "    <uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\" /> \n"
-                + "                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-                lintProject(
-                        "mock_location.xml=>AndroidManifest.xml",
-                        "mock_location.xml=>debug/AndroidManifest.xml",
-                        "mock_location.xml=>test/AndroidManifest.xml",
-                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
-        // TODO: When we have an instantiatable gradle model, test with real model and verify
-        // that a manifest file in a debug build type does not get flagged.
-    }
-
-    public void testMockLocationsOk() throws Exception {
-        // Not a Gradle project
-        mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
-        assertEquals(""
-                + "No warnings.",
-                lintProject(
-                        "mock_location.xml=>AndroidManifest.xml"));
-    }
-
-    public void testGradleOverrides() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
-        assertEquals(""
-                + "AndroidManifest.xml:4: Warning: This versionCode value (1) is not used; it is always overridden by the value specified in the Gradle build script (2) [GradleOverrides]\n"
-                + "    android:versionCode=\"1\"\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "AndroidManifest.xml:5: Warning: This versionName value (1.0) is not used; it is always overridden by the value specified in the Gradle build script (MyName) [GradleOverrides]\n"
-                + "    android:versionName=\"1.0\" >\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "AndroidManifest.xml:7: Warning: This minSdkVersion value (14) is not used; it is always overridden by the value specified in the Gradle build script (5) [GradleOverrides]\n"
-                + "    <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
-                + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "AndroidManifest.xml:7: Warning: This targetSdkVersion value (17) is not used; it is always overridden by the value specified in the Gradle build script (16) [GradleOverrides]\n"
-                + "    <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
-                + "                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 4 warnings\n",
-                lintProject(
-                        "gradle_override.xml=>AndroidManifest.xml",
-                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
-    }
-
-    public void testGradleOverridesOk() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
-        // (See custom logic in #createClient which returns -1/null for the merged flavor
-        // from this test, and not from testGradleOverrides)
-        assertEquals(""
-                + "No warnings.",
-                lintProject(
-                        "gradle_override.xml=>AndroidManifest.xml",
-                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
-    }
-
-    public void testManifestPackagePlaceholder() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
-        assertEquals(""
-                + "AndroidManifest.xml:3: Warning: Cannot use placeholder for the package in the manifest; set applicationId in build.gradle instead [GradleOverrides]\n"
-                + "    package=\"${packageName}\" >\n"
-                + "    ~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-
-                lintProject(
-                        "gradle_override_placeholder.xml=>AndroidManifest.xml",
-                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
-    }
-
-    public void testMipMap() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
-        assertEquals("No warnings.",
-
-                lintProject(
-                        "mipmap.xml=>AndroidManifest.xml"));
-    }
-
-    public void testMipMapWithDensityFiltering() throws Exception {
-        mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
-        assertEquals(""
-                        + "AndroidManifest.xml:9: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
-                        + "        android:icon=\"@drawable/ic_launcher\"\n"
-                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "AndroidManifest.xml:14: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
-                        + "            android:icon=\"@drawable/activity1\"\n"
-                        + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                        + "0 errors, 2 warnings\n",
-
-                lintProject(
-                        "mipmap.xml=>AndroidManifest.xml"));
-    }
-
-    // Custom project which locates all manifest files in the project rather than just
-    // being hardcoded to the root level
-
-    @Override
-    protected TestLintClient createClient() {
-        if ("testMipMapWithDensityFiltering".equals(getName())) {
-            // Set up a mock project model for the resource configuration test(s)
-            // where we provide a subset of densities to be included
-            return new TestLintClient() {
-                @NonNull
-                @Override
-                protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                    return new Project(this, dir, referenceDir) {
-                        @Override
-                        public boolean isGradleProject() {
-                            return true;
-                        }
-
-                        @Nullable
-                        @Override
-                        public AndroidProject getGradleProjectModel() {
-                            /*
-                            Simulate variant freeBetaDebug in this setup:
-                                defaultConfig {
-                                    ...
-                                    resConfigs "cs"
-                                }
-                                flavorDimensions  "pricing", "releaseType"
-                                productFlavors {
-                                    beta {
-                                        flavorDimension "releaseType"
-                                        resConfig "en", "de"
-                                        resConfigs "nodpi", "hdpi"
-                                    }
-                                    normal { flavorDimension "releaseType" }
-                                    free { flavorDimension "pricing" }
-                                    paid { flavorDimension "pricing" }
-                                }
-                             */
-                            ProductFlavor flavorFree = mock(ProductFlavor.class);
-                            when(flavorFree.getName()).thenReturn("free");
-                            when(flavorFree.getResourceConfigurations())
-                                    .thenReturn(Collections.<String>emptyList());
-
-                            ProductFlavor flavorNormal = mock(ProductFlavor.class);
-                            when(flavorNormal.getName()).thenReturn("normal");
-                            when(flavorNormal.getResourceConfigurations())
-                                    .thenReturn(Collections.<String>emptyList());
-
-                            ProductFlavor flavorPaid = mock(ProductFlavor.class);
-                            when(flavorPaid.getName()).thenReturn("paid");
-                            when(flavorPaid.getResourceConfigurations())
-                                    .thenReturn(Collections.<String>emptyList());
-
-                            ProductFlavor flavorBeta = mock(ProductFlavor.class);
-                            when(flavorBeta.getName()).thenReturn("beta");
-                            List<String> resConfigs = Arrays.asList("hdpi", "en", "de", "nodpi");
-                            when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
-
-                            ProductFlavor defaultFlavor = mock(ProductFlavor.class);
-                            when(defaultFlavor.getName()).thenReturn("main");
-                            when(defaultFlavor.getResourceConfigurations()).thenReturn(
-                                    Collections.singleton("cs"));
-
-                            ProductFlavorContainer containerBeta =
-                                    mock(ProductFlavorContainer.class);
-                            when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
-
-                            ProductFlavorContainer containerFree =
-                                    mock(ProductFlavorContainer.class);
-                            when(containerFree.getProductFlavor()).thenReturn(flavorFree);
-
-                            ProductFlavorContainer containerPaid =
-                                    mock(ProductFlavorContainer.class);
-                            when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
-
-                            ProductFlavorContainer containerNormal =
-                                    mock(ProductFlavorContainer.class);
-                            when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
-
-                            ProductFlavorContainer defaultContainer =
-                                    mock(ProductFlavorContainer.class);
-                            when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
-
-                            List<ProductFlavorContainer> containers = Arrays.asList(
-                                    containerPaid, containerFree, containerNormal, containerBeta
-                            );
-
-                            AndroidProject project = mock(AndroidProject.class);
-                            when(project.getProductFlavors()).thenReturn(containers);
-                            when(project.getDefaultConfig()).thenReturn(defaultContainer);
-                            return project;
-                        }
-
-                        @Nullable
-                        @Override
-                        public Variant getCurrentVariant() {
-                            List<String> productFlavorNames = Arrays.asList("free", "beta");
-                            Variant mock = mock(Variant.class);
-                            when(mock.getProductFlavors()).thenReturn(productFlavorNames);
-                            return mock;
-                        }
-                    };
-                }
-            };
-        }
-        if (mEnabled.contains(ManifestDetector.MOCK_LOCATION)) {
-            return new TestLintClient() {
-                @NonNull
-                @Override
-                protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                    return new Project(this, dir, referenceDir) {
-                        @NonNull
-                        @Override
-                        public List<File> getManifestFiles() {
-                            if (mManifestFiles == null) {
-                                mManifestFiles = Lists.newArrayList();
-                                addManifestFiles(mDir);
-                            }
-
-                            return mManifestFiles;
-                        }
-
-                        private void addManifestFiles(File dir) {
-                            if (dir.getName().equals(ANDROID_MANIFEST_XML)) {
-                                mManifestFiles.add(dir);
-                            } else if (dir.isDirectory()) {
-                                File[] files = dir.listFiles();
-                                if (files != null) {
-                                    for (File file : files) {
-                                        addManifestFiles(file);
-                                    }
-                                }
-                            }
-                        }
-
-                        @NonNull SourceProvider createSourceProvider(File manifest) {
-                            SourceProvider provider = mock(SourceProvider.class);
-                            when(provider.getManifestFile()).thenReturn(manifest);
-                            return provider;
-                        }
-
-                        @Nullable
-                        @Override
-                        public AndroidProject getGradleProjectModel() {
-                            if (!isGradleProject()) {
-                                return null;
-                            }
-
-                            File main = new File(mDir, ANDROID_MANIFEST_XML);
-                            File debug = new File(mDir, "debug" + File.separator + ANDROID_MANIFEST_XML);
-                            File test = new File(mDir, "test" + File.separator + ANDROID_MANIFEST_XML);
-
-                            SourceProvider defaultSourceProvider = createSourceProvider(main);
-                            SourceProvider debugSourceProvider = createSourceProvider(debug);
-                            SourceProvider testSourceProvider = createSourceProvider(test);
-
-                            ProductFlavorContainer defaultConfig = mock(ProductFlavorContainer.class);
-                            when(defaultConfig.getSourceProvider()).thenReturn(defaultSourceProvider);
-
-                            BuildType buildType = mock(BuildType.class);
-                            when(buildType.isDebuggable()).thenReturn(true);
-
-                            BuildTypeContainer buildTypeContainer = mock(BuildTypeContainer.class);
-                            when(buildTypeContainer.getBuildType()).thenReturn(buildType);
-                            when(buildTypeContainer.getSourceProvider()).thenReturn(debugSourceProvider);
-                            List<BuildTypeContainer> buildTypes = Lists.newArrayList(buildTypeContainer);
-
-                            SourceProviderContainer extraProvider = mock(SourceProviderContainer.class);
-                            when(extraProvider.getArtifactName()).thenReturn(AndroidProject.ARTIFACT_ANDROID_TEST);
-                            when(extraProvider.getSourceProvider()).thenReturn(testSourceProvider);
-                            List<SourceProviderContainer> extraProviders = Lists.newArrayList(extraProvider);
-
-                            ProductFlavorContainer productFlavorContainer = mock(ProductFlavorContainer.class);
-                            when(productFlavorContainer.getExtraSourceProviders()).thenReturn(extraProviders);
-                            List<ProductFlavorContainer> productFlavors = Lists.newArrayList(productFlavorContainer);
-
-                            AndroidProject project = mock(AndroidProject.class);
-                            when(project.getDefaultConfig()).thenReturn(defaultConfig);
-                            when(project.getBuildTypes()).thenReturn(buildTypes);
-                            when(project.getProductFlavors()).thenReturn(productFlavors);
-                            return project;
-                        }
-                    };
-                }
-            };
-        } else if (mEnabled.contains(ManifestDetector.GRADLE_OVERRIDES)) {
-            return new TestLintClient() {
-                @NonNull
-                @Override
-                protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                    return new Project(this, dir, referenceDir) {
-                        @Override
-                        public boolean isGradleProject() {
-                            return true;
-                        }
-
-                        @Nullable
-                        @Override
-                        public Variant getCurrentVariant() {
-                            ProductFlavor flavor = mock(ProductFlavor.class);
-                            if (getName().equals("ManifestDetectorTest_testGradleOverridesOk") ||
-                                    getName().equals(
-                                        "ManifestDetectorTest_testManifestPackagePlaceholder")) {
-                                when(flavor.getMinSdkVersion()).thenReturn(null);
-                                when(flavor.getTargetSdkVersion()).thenReturn(null);
-                                when(flavor.getVersionCode()).thenReturn(null);
-                                when(flavor.getVersionName()).thenReturn(null);
-                            } else {
-                                assertEquals(getName(), "ManifestDetectorTest_testGradleOverrides");
-
-                                ApiVersion apiMock = mock(ApiVersion.class);
-                                when(apiMock.getApiLevel()).thenReturn(5);
-                                when(apiMock.getApiString()).thenReturn("5");
-                                when(flavor.getMinSdkVersion()).thenReturn(apiMock);
-
-                                apiMock = mock(ApiVersion.class);
-                                when(apiMock.getApiLevel()).thenReturn(16);
-                                when(apiMock.getApiString()).thenReturn("16");
-                                when(flavor.getTargetSdkVersion()).thenReturn(apiMock);
-
-                                when(flavor.getVersionCode()).thenReturn(2);
-                                when(flavor.getVersionName()).thenReturn("MyName");
-                            }
-
-                            Variant mock = mock(Variant.class);
-                            when(mock.getMergedFlavor()).thenReturn(flavor);
-                            return mock;
-                        }
-                    };
-                }
-            };
-
-        }
-        return super.createClient();
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
deleted file mode 100644
index bf183da..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
+++ /dev/null
@@ -1,215 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings("javadoc")
-public class NamespaceDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new NamespaceDetector();
-    }
-
-    public void testCustom() throws Exception {
-        assertEquals(
-            "res/layout/customview.xml:5: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
-            "    xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n" +
-            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintProject(
-                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                    "multiproject/library.properties=>project.properties",
-                    "res/layout/customview.xml"
-            ));
-    }
-
-    public void testGradle() throws Exception {
-        assertEquals(""
-                + "res/layout/customview.xml:5: Error: In Gradle projects, always use http://schemas.android.com/apk/res-auto for custom attributes [ResAuto]\n"
-                + "    xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n"
-                + "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "1 errors, 0 warnings\n",
-
-                lintProject(
-                        "multiproject/library.properties=>build.gradle", // dummy; only name counts
-                        "res/layout/customview.xml"
-                ));
-    }
-
-    public void testCustomOk() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
-
-                    // Use a standard project properties instead: no warning since it's
-                    // not a library project:
-                    //"multiproject/library.properties=>project.properties",
-
-                    "res/layout/customview.xml"
-            ));
-    }
-
-    public void testCustomOk2() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                    "multiproject/library.properties=>project.properties",
-                    // This project already uses the res-auto package
-                    "res/layout/customview2.xml"
-            ));
-    }
-
-    public void testTypo() throws Exception {
-        assertEquals(
-            "res/layout/wrong_namespace.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/andriod, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
-            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/andriod\"\n" +
-            "                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintProject("res/layout/wrong_namespace.xml"));
-    }
-
-    public void testTypo2() throws Exception {
-        assertEquals(
-            "res/layout/wrong_namespace2.xml:2: Error: URI is case sensitive: was \"http://schemas.android.com/apk/res/Android\", expected \"http://schemas.android.com/apk/res/android\" [NamespaceTypo]\n" +
-            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/Android\"\n" +
-            "                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintProject("res/layout/wrong_namespace2.xml"));
-    }
-
-    public void testTypo3() throws Exception {
-        assertEquals(
-            "res/layout/wrong_namespace3.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/androi, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
-            "<LinearLayout xmlns:a=\"http://schemas.android.com/apk/res/androi\"\n" +
-            "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintProject("res/layout/wrong_namespace3.xml"));
-    }
-
-    public void testTypo4() throws Exception {
-        assertEquals(
-            "res/layout/wrong_namespace5.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n" +
-            "    xmlns:noturi=\"tp://schems.android.com/apk/res/com.my.package\"\n" +
-            "                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "res/layout/wrong_namespace5.xml:3: Error: Possible typo in URL: was \"http://schems.android.com/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
-            "    xmlns:typo1=\"http://schems.android.com/apk/res/com.my.package\"\n" +
-            "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "res/layout/wrong_namespace5.xml:4: Error: Possible typo in URL: was \"http://schems.android.comm/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
-            "    xmlns:typo2=\"http://schems.android.comm/apk/res/com.my.package\"\n" +
-            "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "3 errors, 0 warnings\n",
-
-            lintProject("res/layout/wrong_namespace5.xml"));
-    }
-
-    public void testTypoOk() throws Exception {
-        assertEquals(
-                "No warnings.",
-
-                lintProject("res/layout/wrong_namespace4.xml"));
-    }
-
-    public void testUnused() throws Exception {
-        assertEquals(
-            "res/layout/unused_namespace.xml:3: Warning: Unused namespace unused1 [UnusedNamespace]\n" +
-            "    xmlns:unused1=\"http://schemas.android.com/apk/res/unused1\"\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "res/layout/unused_namespace.xml:4: Warning: Unused namespace unused2 [UnusedNamespace]\n" +
-            "    xmlns:unused2=\"http://schemas.android.com/apk/res/unused1\"\n" +
-            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "0 errors, 2 warnings\n" +
-            "",
-
-            lintProject("res/layout/unused_namespace.xml"));
-    }
-
-    public void testUnusedOk() throws Exception {
-        assertEquals(
-                "No warnings.",
-
-                lintProject("res/layout/layout1.xml"));
-    }
-
-    public void testLayoutAttributesOk() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintFiles("res/layout/namespace3.xml"));
-    }
-
-    public void testLayoutAttributesOk2() throws Exception {
-        assertEquals(
-            "No warnings.",
-
-            lintFiles("res/layout/namespace4.xml"));
-    }
-
-    public void testLayoutAttributes() throws Exception {
-        assertEquals(
-            "res/layout/namespace3.xml:2: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
-            "    xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
-            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintFiles("res/layout/namespace3.xml",
-                      "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                      "multiproject/library.properties=>project.properties"));
-    }
-
-    public void testLayoutAttributes2() throws Exception {
-        assertEquals(
-            "res/layout/namespace4.xml:3: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
-            "    xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
-            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintFiles("res/layout/namespace4.xml",
-                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                    "multiproject/library.properties=>project.properties"));
-    }
-
-    public void testWrongResAutoUrl() throws Exception {
-        assertEquals(
-            "res/layout/namespace5.xml:3: Error: Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto? [ResAuto]\n" +
-            "    xmlns:app=\"http://schemas.android.com/apk/auto-res/\"\n" +
-            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-            "1 errors, 0 warnings\n",
-
-            lintFiles("res/layout/namespace5.xml",
-                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                    "multiproject/library.properties=>project.properties"));
-    }
-
-    public void testWrongResUrl() throws Exception {
-        assertEquals(""
-            + "AndroidManifest.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n"
-            + "<manifest xmlns:android=\"https://schemas.android.com/apk/res/android\"\n"
-            + "                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-            + "1 errors, 0 warnings\n",
-
-            lintFiles("https_namespace.xml=>AndroidManifest.xml"));
-    }
-}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
deleted file mode 100644
index 45df3bf..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
+++ /dev/null
@@ -1,215 +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.tools.lint.checks;
-
-import static com.android.SdkConstants.FN_PUBLIC_TXT;
-import static com.android.SdkConstants.FN_RESOURCE_TEXT;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-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.Dependencies;
-import com.android.builder.model.Variant;
-import com.android.builder.model.Version;
-import com.android.testutils.TestUtils;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Project;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import org.mockito.stubbing.OngoingStubbing;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-
-@SuppressWarnings("javadoc")
-public class PrivateResourceDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new PrivateResourceDetector();
-    }
-
-    public void testPrivateInXml() throws Exception {
-        assertEquals(""
-                + "res/layout/private.xml:11: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
-                + "            android:text=\"@string/my_private_string\" />\n"
-                + "                          ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-                lintProject(xml("res/layout/private.xml", ""
-                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
-                        + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
-                        + "              android:id=\"@+id/newlinear\"\n"
-                        + "              android:orientation=\"vertical\"\n"
-                        + "              android:layout_width=\"match_parent\"\n"
-                        + "              android:layout_height=\"match_parent\">\n"
-                        + "\n"
-                        + "    <TextView\n"
-                        + "            android:layout_width=\"wrap_content\"\n"
-                        + "            android:layout_height=\"wrap_content\"\n"
-                        + "            android:text=\"@string/my_private_string\" />\n"
-                        + "\n"
-                        + "    <TextView\n"
-                        + "            android:layout_width=\"wrap_content\"\n"
-                        + "            android:layout_height=\"wrap_content\"\n"
-                        + "            android:text=\"@string/my_public_string\" />\n"
-                        + "</LinearLayout>\n")));
-    }
-
-    public void testPrivateInJava() throws Exception {
-        assertEquals(""
-                + ""
-                + "src/test/pkg/Private.java:3: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
-                + "        int x = R.string.my_private_string; // ERROR\n"
-                + "                ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 1 warnings\n",
-                lintProject(java("src/test/pkg/Private.java", ""
-                        + "public class PrivateResourceDetectorTest {\n"
-                        + "    void test() {\n"
-                        + "        int x = R.string.my_private_string; // ERROR\n"
-                        + "        int y = R.string.my_public_string; // OK\n"
-                        + "        int y = android.R.string.my_private_string; // OK (not in project namespace)\n"
-                        + "    }\n"
-                        + "}\n")));
-    }
-
-    public void testOverride() throws Exception {
-        assertEquals(""
-                + "res/layout/my_private_layout.xml: Warning: Overriding @layout/my_private_layout which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
-                + "res/values/strings.xml:5: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
-                + "    <string name=\"my_private_string\">String 1</string>\n"
-                + "                  ~~~~~~~~~~~~~~~~~\n"
-                + "res/values/strings.xml:9: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
-                + "    <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
-                + "                              ~~~~~~~~~~~~~~~~~\n"
-                + "res/values/strings.xml:12: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
-                + "    <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
-                + "                                         ~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 4 warnings\n",
-                lintProject(xml("res/values/strings.xml", ""
-                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
-                        + "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n"
-                        + "\n"
-                        + "    <string name=\"app_name\">LibraryProject</string>\n"
-                        + "    <string name=\"my_private_string\">String 1</string>\n"
-                        + "    <string name=\"my_public_string\">String 2</string>\n"
-                        + "    <string name=\"string3\"> @my_private_string </string>\n"
-                        + "    <string name=\"string4\"> @my_public_string </string>\n"
-                        + "    <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
-                        + "    <dimen name=\"my_private_string\">String 1</dimen>\n" // unrelated
-                        + "    <string tools:ignore=\"PrivateResource\" name=\"my_private_string\">String 2</string>\n"
-                        + "    <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
-                        + "    <string tools:override=\"true\" name=\"my_private_string\">String 2</string>\n"
-                        + "\n"
-                        + "</resources>\n"),
-                        xml("res/layout/my_private_layout.xml", "<LinearLayout/>"),
-                        xml("res/layout/my_public_layout.xml", "<LinearLayout/>")));
-    }
-
-    @Override
-    protected TestLintClient createClient() {
-        return new TestLintClient() {
-            @NonNull
-            @Override
-            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
-                return new Project(this, dir, referenceDir) {
-                    @Override
-                    public boolean isGradleProject() {
-                        return true;
-                    }
-
-                    @Nullable
-                    @Override
-                    public AndroidProject getGradleProjectModel() {
-                        return createMockProject(Version.ANDROID_GRADLE_PLUGIN_VERSION,
-                                                 Version.BUILDER_MODEL_API_VERSION);
-                    }
-
-                    @Nullable
-                    @Override
-                    public Variant getCurrentVariant() {
-                        try {
-                            AndroidLibrary library = createMockLibrary(
-                                    ""
-                                            + "int string my_private_string 0x7f040000\n"
-                                            + "int string my_public_string 0x7f040001\n"
-                                            + "int layout my_private_layout 0x7f040002\n",
-                                    ""
-                                            + ""
-                                            + "string my_public_string\n",
-                                    Collections.<AndroidLibrary>emptyList()
-                            );
-                            AndroidArtifact artifact = createMockArtifact(
-                                    Collections.singletonList(library));
-
-                            Variant variant = mock(Variant.class);
-                            when(variant.getMainArtifact()).thenReturn(artifact);
-                            return variant;
-                        } catch (Exception e) {
-                            fail(e.toString());
-                            return null;
-                        }
-                    }
-                };
-            }
-        };
-    }
-
-    public static AndroidProject createMockProject(String modelVersion, int apiVersion) {
-        AndroidProject project = mock(AndroidProject.class);
-        when(project.getApiVersion()).thenReturn(apiVersion);
-        when(project.getModelVersion()).thenReturn(modelVersion);
-
-        return project;
-    }
-
-    public static AndroidArtifact createMockArtifact(List<AndroidLibrary> libraries) {
-        Dependencies dependencies = mock(Dependencies.class);
-        when(dependencies.getLibraries()).thenReturn(libraries);
-
-        AndroidArtifact artifact = mock(AndroidArtifact.class);
-        when(artifact.getDependencies()).thenReturn(dependencies);
-
-        return artifact;
-    }
-
-    public static AndroidLibrary createMockLibrary(String allResources, String publicResources,
-            List<AndroidLibrary> dependencies)
-            throws IOException {
-        final File tempDir = TestUtils.createTempDirDeletedOnExit();
-
-        Files.write(allResources, new File(tempDir, FN_RESOURCE_TEXT), Charsets.UTF_8);
-        File publicTxtFile = new File(tempDir, FN_PUBLIC_TXT);
-        if (publicResources != null) {
-            Files.write(publicResources, publicTxtFile, Charsets.UTF_8);
-        }
-        AndroidLibrary library = mock(AndroidLibrary.class);
-        when(library.getPublicResources()).thenReturn(publicTxtFile);
-
-        // Work around wildcard capture
-        //when(mock.getLibraryDependencies()).thenReturn(dependencies);
-        List libraryDependencies = library.getLibraryDependencies();
-        OngoingStubbing<List> setter = when(libraryDependencies);
-        setter.thenReturn(dependencies);
-        return library;
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
deleted file mode 100644
index 9b60532..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
+++ /dev/null
@@ -1,125 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings("javadoc")
-public class RequiredAttributeDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new RequiredAttributeDetector();
-    }
-
-    public void test() throws Exception {
-        // Simple: Only consider missing attributes in the layout xml file
-        // (though skip warnings on <merge> tags and under <GridLayout>
-        assertEquals(
-            "res/layout/size.xml:13: Error: The required layout_height attribute is missing [RequiredSize]\n" +
-            "    <RadioButton\n" +
-            "    ^\n" +
-            "res/layout/size.xml:18: Error: The required layout_width attribute is missing [RequiredSize]\n" +
-            "    <EditText\n" +
-            "    ^\n" +
-            "res/layout/size.xml:23: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
-            "    <EditText\n" +
-            "    ^\n" +
-            "3 errors, 0 warnings\n",
-
-            lintProject("res/layout/size.xml"));
-    }
-
-    public void test2() throws Exception {
-        // Consider styles (specifying sizes) and includes (providing sizes for the root tags)
-        assertEquals(
-            "res/layout/size2.xml:9: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
-            "    <Button\n" +
-            "    ^\n" +
-            "res/layout/size2.xml:18: Error: The required layout_height attribute is missing [RequiredSize]\n" +
-            "    <Button\n" +
-            "    ^\n" +
-            "2 errors, 0 warnings\n",
-
-            lintProject(
-                    "res/layout/size2.xml",
-                    "res/layout/sizeincluded.xml",
-                    "res/values/sizestyles.xml"
-                    ));
-    }
-
-    public void testInflaters() throws Exception {
-        // Consider java inflation
-        assertEquals(
-            "res/layout/size5.xml:2: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
-            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
-            "^\n" +
-            "1 errors, 0 warnings\n",
-
-            lintProject(
-                    "src/test/pkg/InflaterTest.java.txt=>src/test/pkg/InflaterTest.java",
-                    "res/layout/sizeincluded.xml=>res/layout/size1.xml",
-                    "res/layout/sizeincluded.xml=>res/layout/size2.xml",
-                    "res/layout/sizeincluded.xml=>res/layout/size3.xml",
-                    "res/layout/sizeincluded.xml=>res/layout/size4.xml",
-                    "res/layout/sizeincluded.xml=>res/layout/size5.xml",
-                    "res/layout/sizeincluded.xml=>res/layout/size6.xml",
-                    "res/layout/sizeincluded.xml=>res/layout/size7.xml"
-            ));
-    }
-
-    public void testRequestFocus() throws Exception {
-        // See http://code.google.com/p/android/issues/detail?id=38700
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "res/layout/edit_type.xml"
-            ));
-    }
-
-    public void testFrameworkStyles() throws Exception {
-        // See http://code.google.com/p/android/issues/detail?id=38958
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "res/layout/listseparator.xml"
-            ));
-    }
-
-    public void testThemeStyles() throws Exception {
-        // Check that we don't complain about cases where the size is defined in a theme
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "res/layout/size.xml",
-                    "res/values/themes.xml"
-            ));
-    }
-
-    public void testThemeStyles2() throws Exception {
-        // Check that we don't complain about cases where the size is defined in a theme
-        assertEquals(
-            "No warnings.",
-
-            lintProject(
-                    "res/layout/size.xml",
-                    "res/values/themes2.xml"
-            ));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
deleted file mode 100644
index 39f9007..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2015 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.tools.lint.checks;
-
-import com.android.tools.lint.ExternalAnnotationRepository;
-import com.android.tools.lint.detector.api.Detector;
-
-public class SupportAnnotationDetectorTest extends AbstractCheckTest {
-    private static final boolean SDK_ANNOTATIONS_AVAILABLE =
-            new SupportAnnotationDetectorTest().createClient().findResource(
-            ExternalAnnotationRepository.SDK_ANNOTATIONS_PATH) != null;
-
-    @Override
-    protected Detector getDetector() {
-        return new SupportAnnotationDetector();
-    }
-
-    public void testRange() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/RangeTest.java:32: Error: Expected length 5 (was 4) [Range]\n"
-                + "        printExact(\"1234\"); // ERROR\n"
-                + "                   ~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:34: Error: Expected length 5 (was 6) [Range]\n"
-                + "        printExact(\"123456\"); // ERROR\n"
-                + "                   ~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:36: Error: Expected length ≥ 5 (was 4) [Range]\n"
-                + "        printMin(\"1234\"); // ERROR\n"
-                + "                 ~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:43: Error: Expected length ≤ 8 (was 9) [Range]\n"
-                + "        printMax(\"123456789\"); // ERROR\n"
-                + "                 ~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:45: Error: Expected length ≥ 4 (was 3) [Range]\n"
-                + "        printRange(\"123\"); // ERROR\n"
-                + "                   ~~~~~\n"
-                + "src/test/pkg/RangeTest.java:49: Error: Expected length ≤ 6 (was 7) [Range]\n"
-                + "        printRange(\"1234567\"); // ERROR\n"
-                + "                   ~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:53: Error: Expected size 5 (was 4) [Range]\n"
-                + "        printExact(new int[]{1, 2, 3, 4}); // ERROR\n"
-                + "                   ~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:55: Error: Expected size 5 (was 6) [Range]\n"
-                + "        printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR\n"
-                + "                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:57: Error: Expected size ≥ 5 (was 4) [Range]\n"
-                + "        printMin(new int[]{1, 2, 3, 4}); // ERROR\n"
-                + "                 ~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:65: Error: Expected size ≤ 8 (was 9) [Range]\n"
-                + "        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR\n"
-                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:67: Error: Expected size ≥ 4 (was 3) [Range]\n"
-                + "        printRange(new int[] {1,2,3}); // ERROR\n"
-                + "                   ~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:71: Error: Expected size ≤ 6 (was 7) [Range]\n"
-                + "        printRange(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
-                + "                   ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:74: Error: Expected size to be a multiple of 3 (was 4 and should be either 3 or 6) [Range]\n"
-                + "        printMultiple(new int[] {1,2,3,4}); // ERROR\n"
-                + "                      ~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:75: Error: Expected size to be a multiple of 3 (was 5 and should be either 3 or 6) [Range]\n"
-                + "        printMultiple(new int[] {1,2,3,4,5}); // ERROR\n"
-                + "                      ~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:77: Error: Expected size to be a multiple of 3 (was 7 and should be either 6 or 9) [Range]\n"
-                + "        printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
-                + "                      ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:80: Error: Expected size ≥ 4 (was 3) [Range]\n"
-                + "        printMinMultiple(new int[]{1, 2, 3}); // ERROR\n"
-                + "                         ~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/RangeTest.java:84: Error: Value must be ≥ 4 (was 3) [Range]\n"
-                + "        printAtLeast(3); // ERROR\n"
-                + "                     ~\n"
-                + "src/test/pkg/RangeTest.java:91: Error: Value must be ≤ 7 (was 8) [Range]\n"
-                + "        printAtMost(8); // ERROR\n"
-                + "                    ~\n"
-                + "src/test/pkg/RangeTest.java:93: Error: Value must be ≥ 4 (was 3) [Range]\n"
-                + "        printBetween(3); // ERROR\n"
-                + "                     ~\n"
-                + "src/test/pkg/RangeTest.java:98: Error: Value must be ≤ 7 (was 8) [Range]\n"
-                + "        printBetween(8); // ERROR\n"
-                + "                     ~\n"
-                + "src/test/pkg/RangeTest.java:102: Error: Value must be ≥ 2.5 (was 2.49) [Range]\n"
-                + "        printAtLeastInclusive(2.49f); // ERROR\n"
-                + "                              ~~~~~\n"
-                + "src/test/pkg/RangeTest.java:106: Error: Value must be > 2.5 (was 2.49) [Range]\n"
-                + "        printAtLeastExclusive(2.49f); // ERROR\n"
-                + "                              ~~~~~\n"
-                + "src/test/pkg/RangeTest.java:107: Error: Value must be > 2.5 (was 2.5) [Range]\n"
-                + "        printAtLeastExclusive(2.5f); // ERROR\n"
-                + "                              ~~~~\n"
-                + "src/test/pkg/RangeTest.java:113: Error: Value must be ≤ 7.0 (was 7.1) [Range]\n"
-                + "        printAtMostInclusive(7.1f); // ERROR\n"
-                + "                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:117: Error: Value must be < 7.0 (was 7.0) [Range]\n"
-                + "        printAtMostExclusive(7.0f); // ERROR\n"
-                + "                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:118: Error: Value must be < 7.0 (was 7.1) [Range]\n"
-                + "        printAtMostExclusive(7.1f); // ERROR\n"
-                + "                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:120: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
-                + "        printBetweenFromInclusiveToInclusive(2.4f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:124: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
-                + "        printBetweenFromInclusiveToInclusive(5.1f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:126: Error: Value must be > 2.5 (was 2.4) [Range]\n"
-                + "        printBetweenFromExclusiveToInclusive(2.4f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:127: Error: Value must be > 2.5 (was 2.5) [Range]\n"
-                + "        printBetweenFromExclusiveToInclusive(2.5f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:129: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
-                + "        printBetweenFromExclusiveToInclusive(5.1f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:131: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
-                + "        printBetweenFromInclusiveToExclusive(2.4f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:135: Error: Value must be < 5.0 (was 5.0) [Range]\n"
-                + "        printBetweenFromInclusiveToExclusive(5.0f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:137: Error: Value must be > 2.5 (was 2.4) [Range]\n"
-                + "        printBetweenFromExclusiveToExclusive(2.4f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:138: Error: Value must be > 2.5 (was 2.5) [Range]\n"
-                + "        printBetweenFromExclusiveToExclusive(2.5f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "src/test/pkg/RangeTest.java:141: Error: Value must be < 5.0 (was 5.0) [Range]\n"
-                + "        printBetweenFromExclusiveToExclusive(5.0f); // ERROR\n"
-                + "                                             ~~~~\n"
-                + "36 errors, 0 warnings\n",
-
-                lintProject("src/test/pkg/RangeTest.java.txt=>src/test/pkg/RangeTest.java",
-                        "src/android/support/annotation/Size.java.txt=>src/android/support/annotation/Size.java",
-                        "src/android/support/annotation/IntRange.java.txt=>src/android/support/annotation/IntRange.java",
-                        "src/android/support/annotation/FloatRange.java.txt=>src/android/support/annotation/FloatRange.java"
-                ));
-    }
-
-    public void testTypeDef() throws Exception {
-        assertEquals(""
-                + "src/test/pkg/IntDefTest.java:31: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setStyle(0, 0); // ERROR\n"
-                + "                 ~\n"
-                + "src/test/pkg/IntDefTest.java:32: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setStyle(-1, 0); // ERROR\n"
-                + "                 ~~\n"
-                + "src/test/pkg/IntDefTest.java:33: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setStyle(UNRELATED, 0); // ERROR\n"
-                + "                 ~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:34: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setStyle(IntDefTest.UNRELATED, 0); // ERROR\n"
-                + "                 ~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:35: Error: Flag not allowed here [WrongConstant]\n"
-                + "        setStyle(IntDefTest.STYLE_NORMAL|STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
-                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:36: Error: Flag not allowed here [WrongConstant]\n"
-                + "        setStyle(~STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
-                + "                 ~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:55: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", UNRELATED); // ERROR\n"
-                + "                     ~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:56: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", UNRELATED|STYLE_NO_TITLE); // ERROR\n"
-                + "                     ~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:57: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", STYLE_NORMAL|STYLE_NO_TITLE|UNRELATED); // ERROR\n"
-                + "                                                 ~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:58: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", 1); // ERROR\n"
-                + "                     ~\n"
-                + "src/test/pkg/IntDefTest.java:59: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", arg < 0 ? STYLE_NORMAL : UNRELATED); // ERROR\n"
-                + "                                              ~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:60: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", arg < 0 ? UNRELATED : STYLE_NORMAL); // ERROR\n"
-                + "                               ~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:79: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
-                + "        setTitle(\"\", UNRELATED_TYPE); // ERROR\n"
-                + "                     ~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:80: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
-                + "        setTitle(\"\", \"type2\"); // ERROR\n"
-                + "                     ~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:87: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
-                + "        setTitle(\"\", type); // ERROR\n"
-                + "                     ~~~~\n"
-                + "src/test/pkg/IntDefTest.java:92: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
-                + "        setFlags(\"\", flag); // ERROR\n"
-                + "                     ~~~~\n"
-                + (SDK_ANNOTATIONS_AVAILABLE ?
-                "src/test/pkg/IntDefTest.java:99: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCAL [WrongConstant]\n"
-                + "        view.setLayoutDirection(View.TEXT_DIRECTION_LTR); // ERROR\n"
-                + "                                ~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:100: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCAL [WrongConstant]\n"
-                + "        view.setLayoutDirection(0); // ERROR\n"
-                + "                                ~\n"
-                + "src/test/pkg/IntDefTest.java:101: Error: Flag not allowed here [WrongConstant]\n"
-                + "        view.setLayoutDirection(View.LAYOUT_DIRECTION_LTR|View.LAYOUT_DIRECTION_RTL); // ERROR\n"
-                + "                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/IntDefTest.java:102: Error: Must be one of: Context.POWER_SERVICE, Context.WINDOW_SERVICE, Context.LAYOUT_INFLATER_SERVICE, Context.ACCOUNT_SERVICE, Context.ACTIVITY_SERVICE, Context.ALARM_SERVICE, Context.NOTIFICATION_SERVICE, Context.ACCESSIBILITY_SERVICE, Context.CAPTIONING_SERVICE, Context.KEYGUARD_SERVICE, Context.LOCATION_SERVICE, Context.SEARCH_SERVICE, Context.SENSOR_SERVICE, Context.STORAGE_SERVICE, Context.WALLPAPER_SERVICE, Context.VIBRATOR_SERVICE, Context.CONNECTIVITY_SERVICE, Context.WIFI_SERVICE, Context.WIFI_P2P_SERVICE, Context.NSD_SERVICE, Context.AUDIO_SERVICE, Context.MEDIA_ROUTER_SERVICE, Context.TELEPHONY_SERVICE, Context.TELECOM_SERVICE, Context.CLIPBOARD_SERVICE, Context.INPUT_METHOD_SERVICE, Context.TEXT_SERVICES_MANAGER_SERVICE, Context.APPWIDGET_SERVICE, Context.DROPBOX_SERVICE, Context.DEVICE_POLICY_SERVICE, Context.UI_MODE_SERVICE, Context.DOWNLOAD_SERVICE, Context.NFC_SERVICE, Context.BLUETOOTH_SERVICE, Context.USB_SERVICE, Context.LAUNCHER_APPS_SERVICE, Context.INPUT_SERVICE, Context.DISPLAY_SERVICE, Context.USER_SERVICE, Context.RESTRICTIONS_SERVICE, Context.APP_OPS_SERVICE, Context.CAMERA_SERVICE, Context.PRINT_SERVICE, Context.CONSUMER_IR_SERVICE, Context.TV_INPUT_SERVICE, Context.MEDIA_SESSION_SERVICE, Context.BATTERY_SERVICE, Context.JOB_SCHEDULER_SERVICE, Context.MEDIA_PROJECTION_SERVIC [WrongConstant]\n"
-                + "        context.getSystemService(TYPE_1); // ERROR\n"
-                + "                                 ~~~~~~\n"
-                + "20 errors, 0 warnings\n" :
-                "16 errors, 0 warnings\n"),
-
-                lintProject("src/test/pkg/IntDefTest.java.txt=>src/test/pkg/IntDefTest.java",
-                        "src/android/support/annotation/IntDef.java.txt=>src/android/support/annotation/IntDef.java",
-                        "src/android/support/annotation/StringDef.java.txt=>src/android/support/annotation/StringDef.java"
-                ));
-    }
-
-    public void testColorInt() throws Exception {
-        // Needs updated annotations!
-        assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
-                + "src/test/pkg/WrongColor.java:9: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
-                + "        paint2.setColor(R.color.blue);\n"
-                + "                        ~~~~~~~~~~~~\n"
-                + "src/test/pkg/WrongColor.java:11: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.red) [ResourceAsColor]\n"
-                + "        textView.setTextColor(R.color.red);\n"
-                + "                              ~~~~~~~~~~~\n"
-                + "src/test/pkg/WrongColor.java:12: Error: Should pass resolved color instead of resource id here: getResources().getColor(android.R.color.black) [ResourceAsColor]\n"
-                + "        textView.setTextColor(android.R.color.black);\n"
-                + "                              ~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
-                + "        textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
-                + "                                                        ~~~~~~~~~~~~\n"
-                + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.green) [ResourceAsColor]\n"
-                + "        textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
-                + "                                        ~~~~~~~~~~~~~\n" : "")
-                + "src/test/pkg/WrongColor.java:21: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
-                + "        foo2(R.color.blue);\n"
-                + "             ~~~~~~~~~~~~\n"
-                + "src/test/pkg/WrongColor.java:20: Error: Expected resource of type color [ResourceType]\n"
-                + "        foo1(0xffff0000);\n"
-                + "             ~~~~~~~~~~\n"
-                + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "2 errors, 0 warnings\n"),
-
-                lintProject(
-                        "src/test/pkg/WrongColor.java.txt=>src/test/pkg/WrongColor.java",
-                        "src/android/support/annotation/ColorInt.java.txt=>src/android/support/annotation/ColorInt.java"
-                ));
-    }
-
-    public void testResourceType() throws Exception {
-        assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
-                + "src/p1/p2/Flow.java:13: Error: Expected resource of type drawable [ResourceType]\n"
-                + "        resources.getDrawable(10); // ERROR\n"
-                + "                              ~~\n"
-                + "src/p1/p2/Flow.java:18: Error: Expected resource of type drawable [ResourceType]\n"
-                + "        resources.getDrawable(R.string.my_string); // ERROR\n"
-                + "                              ~~~~~~~~~~~~~~~~~~\n" : "")
-                + "src/p1/p2/Flow.java:22: Error: Expected resource of type drawable [ResourceType]\n"
-                + "        myMethod(R.string.my_string); // ERROR\n"
-                + "                 ~~~~~~~~~~~~~~~~~~\n"
-                + "src/p1/p2/Flow.java:32: Error: Expected resource identifier (R.type.name) [ResourceType]\n"
-                + "        myAnyResMethod(50); // ERROR\n"
-                + "                       ~~\n"
-                + "src/p1/p2/Flow.java:68: Error: Expected resource of type drawable [ResourceType]\n"
-                + "        myMethod(z); // ERROR\n"
-                + "                 ~\n"
-                + (SDK_ANNOTATIONS_AVAILABLE ? "5 errors, 0 warnings\n" : "3 errors, 0 warnings\n"),
-
-                lintProject("src/p1/p2/Flow.java.txt=>src/p1/p2/Flow.java",
-                        "src/android/support/annotation/DrawableRes.java.txt=>src/android/support/annotation/DrawableRes.java"));
-    }
-
-    public void testColorAsDrawable() throws Exception {
-        assertEquals(
-                "No warnings.",
-
-                lintProject("src/p1/p2/ColorAsDrawable.java.txt=>src/p1/p2/ColorAsDrawable.java"));
-    }
-
-    public void testCheckResult() throws Exception {
-        if (!SDK_ANNOTATIONS_AVAILABLE) {
-            // Currently only tests @CheckResult on SDK annotations
-            return;
-        }
-        assertEquals(""
-                + "src/test/pkg/CheckPermissions.java:22: Warning: The result of extractAlpha is not used [CheckResult]\n"
-                + "        bitmap.extractAlpha(); // WARNING\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/CheckPermissions.java:10: Warning: The result of checkCallingOrSelfPermission is not used; did you mean to call #enforceCallingOrSelfPermission(String,String? [UseCheckPermission]\n"
-                + "        context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // WRONG\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "src/test/pkg/CheckPermissions.java:11: Warning: The result of checkPermission is not used; did you mean to call #enforcePermission(String,int,int,String? [UseCheckPermission]\n"
-                + "        context.checkPermission(Manifest.permission.INTERNET, 1, 1);\n"
-                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
-                + "0 errors, 3 warnings\n",
-
-                lintProject("src/test/pkg/CheckPermissions.java.txt=>src/test/pkg/CheckPermissions.java"));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
deleted file mode 100644
index 2e2bcd4..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
+++ /dev/null
@@ -1,327 +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.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-
-import java.io.File;
-import java.util.Arrays;
-
-@SuppressWarnings("javadoc")
-public class UnusedResourceDetectorTest extends AbstractCheckTest {
-    private boolean mEnableIds = false;
-
-    @Override
-    protected Detector getDetector() {
-        return new UnusedResourceDetector();
-    }
-
-    @Override
-    protected boolean isEnabled(Issue issue) {
-        if (issue == UnusedResourceDetector.ISSUE_IDS) {
-            return mEnableIds;
-        } else {
-            return true;
-        }
-    }
-
-    public void testUnused() throws Exception {
-        mEnableIds = false;
-        assertEquals(
-           "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
-           "res/layout/main.xml: Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
-           "res/layout/other.xml: Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
-           "res/values/strings2.xml:3: Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
-           "    <string name=\"hello\">Hello</string>\n" +
-           "            ~~~~~~~~~~~~\n" +
-           "0 errors, 4 warnings\n" +
-           "",
-
-            lintProject(
-                "res/values/strings2.xml",
-                "res/layout/layout1.xml=>res/layout/main.xml",
-                "res/layout/layout1.xml=>res/layout/other.xml",
-
-                // Rename .txt files to .java
-                "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
-                "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
-                "AndroidManifest.xml",
-                "res/layout/accessibility.xml"));
-    }
-
-    public void testUnusedIds() throws Exception {
-        mEnableIds = true;
-
-        assertEquals(
-           "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
-           "Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
-           "Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
-           "Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
-           "res/layout/accessibility.xml:2: Warning: The resource R.id.newlinear appears to be unused [UnusedIds]\n" +
-           "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\">\n" +
-           "                                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-           "res/layout/accessibility.xml:3: Warning: The resource R.id.button1 appears to be unused [UnusedIds]\n" +
-           "    <Button android:text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
-           "                                  ~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-           "res/layout/accessibility.xml:4: Warning: The resource R.id.android_logo appears to be unused [UnusedIds]\n" +
-           "    <ImageView android:id=\"@+id/android_logo\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
-           "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-           "res/layout/accessibility.xml:5: Warning: The resource R.id.android_logo2 appears to be unused [UnusedIds]\n" +
-           "    <ImageButton android:importantForAccessibility=\"yes\" android:id=\"@+id/android_logo2\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
-           "                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
-           "Warning: The resource R.id.imageView1 appears to be unused [UnusedIds]\n" +
-           "Warning: The resource R.id.include1 appears to be unused [UnusedIds]\n" +
-           "Warning: The resource R.id.linearLayout2 appears to be unused [UnusedIds]\n" +
-           "0 errors, 11 warnings\n",
-
-            lintProject(
-                // Rename .txt files to .java
-                "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
-                "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
-                "AndroidManifest.xml",
-                "res/layout/accessibility.xml"));
-    }
-
-    public void testArrayReference() throws Exception {
-        assertEquals(
-           "res/values/arrayusage.xml:3: Warning: The resource R.array.my_array appears to be unused [UnusedResources]\n" +
-           "<string-array name=\"my_array\">\n" +
-           "              ~~~~~~~~~~~~~~~\n" +
-           "0 errors, 1 warnings\n" +
-           "",
-
-            lintProject(
-                "AndroidManifest.xml",
-                "res/values/arrayusage.xml"));
-    }
-
-    public void testAttrs() throws Exception {
-        assertEquals(
-           "res/layout/customattrlayout.xml: Warning: The resource R.layout.customattrlayout appears to be unused [UnusedResources]\n" +
-           "0 errors, 1 warnings\n" +
-           "",
-
-            lintProject(
-                "res/values/customattr.xml",
-                "res/layout/customattrlayout.xml",
-                "unusedR.java.txt=>gen/my/pkg/R.java",
-                "AndroidManifest.xml"));
-    }
-
-    public void testMultiProjectIgnoreLibraries() throws Exception {
-        assertEquals(
-           "No warnings.",
-
-            lintProject(
-                // Master project
-                "multiproject/main-manifest.xml=>AndroidManifest.xml",
-                "multiproject/main.properties=>project.properties",
-                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java",
-
-                // Library project
-                "multiproject/library-manifest.xml=>../LibraryProject/AndroidManifest.xml",
-                "multiproject/library.properties=>../LibraryProject/project.properties",
-                "multiproject/LibraryCode.java.txt=>../LibraryProject/src/foo/library/LibraryCode.java",
-                "multiproject/strings.xml=>../LibraryProject/res/values/strings.xml"
-            ));
-    }
-
-    public void testMultiProject() throws Exception {
-        File master = getProjectDir("MasterProject",
-                // Master project
-                "multiproject/main-manifest.xml=>AndroidManifest.xml",
-                "multiproject/main.properties=>project.properties",
-                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
-        );
-        File library = getProjectDir("LibraryProject",
-                // Library project
-                "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                "multiproject/library.properties=>project.properties",
-                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
-                "multiproject/strings.xml=>res/values/strings.xml"
-        );
-        assertEquals(
-           // string1 is defined and used in the library project
-           // string2 is defined in the library project and used in the master project
-           // string3 is defined in the library project and not used anywhere
-           "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
-           "    <string name=\"string3\">String 3</string>\n" +
-           "            ~~~~~~~~~~~~~~\n" +
-           "0 errors, 1 warnings\n",
-
-           checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
-    }
-
-    public void testFqcnReference() throws Exception {
-        assertEquals(
-           "No warnings.",
-
-            lintProject(
-                "res/layout/layout1.xml=>res/layout/main.xml",
-                "src/test/pkg/UnusedReference.java.txt=>src/test/pkg/UnusedReference.java",
-                "AndroidManifest.xml"));
-    }
-
-    public void testIgnoreXmlDrawable() throws Exception {
-        assertEquals(
-           "No warnings.",
-
-            lintProject(
-                    "res/drawable/ic_menu_help.xml",
-                    "gen/my/pkg/R2.java.txt=>gen/my/pkg/R.java"
-            ));
-    }
-
-    public void testPlurals() throws Exception {
-        assertEquals(
-           "res/values/plurals.xml:3: Warning: The resource R.plurals.my_plural appears to be unused [UnusedResources]\n" +
-           "    <plurals name=\"my_plural\">\n" +
-           "             ~~~~~~~~~~~~~~~~\n" +
-           "0 errors, 1 warnings\n" +
-           "",
-
-            lintProject(
-                "res/values/strings4.xml",
-                "res/values/plurals.xml",
-                "AndroidManifest.xml"));
-    }
-
-    public void testNoMerging() throws Exception {
-        // http://code.google.com/p/android/issues/detail?id=36952
-
-        File master = getProjectDir("MasterProject",
-                // Master project
-                "multiproject/main-manifest.xml=>AndroidManifest.xml",
-                "multiproject/main.properties=>project.properties",
-                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
-        );
-        File library = getProjectDir("LibraryProject",
-                // Library project
-                "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                "multiproject/library.properties=>project.properties",
-                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
-                "multiproject/strings.xml=>res/values/strings.xml"
-        );
-        assertEquals(
-           // The strings are all referenced in the library project's manifest file
-           // which in this project is merged in
-           "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
-           "    <string name=\"string3\">String 3</string>\n" +
-           "            ~~~~~~~~~~~~~~\n" +
-           "0 errors, 1 warnings\n",
-
-           checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
-    }
-
-    public void testLibraryMerging() throws Exception {
-        // http://code.google.com/p/android/issues/detail?id=36952
-        File master = getProjectDir("MasterProject",
-                // Master project
-                "multiproject/main-manifest.xml=>AndroidManifest.xml",
-                "multiproject/main-merge.properties=>project.properties",
-                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
-        );
-        File library = getProjectDir("LibraryProject",
-                // Library project
-                "multiproject/library-manifest.xml=>AndroidManifest.xml",
-                "multiproject/library.properties=>project.properties",
-                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
-                "multiproject/strings.xml=>res/values/strings.xml"
-        );
-        assertEquals(
-           // The strings are all referenced in the library project's manifest file
-           // which in this project is merged in
-           "No warnings.",
-
-           checkLint(Arrays.asList(master, library)));
-    }
-
-    public void testCornerCase() throws Exception {
-        // See http://code.google.com/p/projectlombok/issues/detail?id=415
-        mEnableIds = true;
-        assertEquals(
-            "No warnings.",
-
-             lintProject(
-                 "src/test/pkg/Foo.java.txt=>src/test/pkg/Foo.java",
-                 "AndroidManifest.xml"));
-    }
-
-    public void testAnalytics() throws Exception {
-        // See http://code.google.com/p/android/issues/detail?id=42565
-        mEnableIds = false;
-        assertEquals(
-                "No warnings.",
-
-                lintProject(
-                        "res/values/analytics.xml"
-        ));
-    }
-
-    public void testIntegers() throws Exception {
-        // See https://code.google.com/p/android/issues/detail?id=53995
-        mEnableIds = true;
-        assertEquals(
-                "No warnings.",
-
-                lintProject(
-                        "res/values/integers.xml",
-                        "res/anim/slide_in_out.xml"
-                ));
-    }
-
-    public void testIntegerArrays() throws Exception {
-        // See http://code.google.com/p/android/issues/detail?id=59761
-        mEnableIds = false;
-        assertEquals(
-                "No warnings.",
-                lintProject("res/values/integer_arrays.xml=>res/values/integer_arrays.xml"));
-    }
-
-    public void testUnitTestReferences() throws Exception {
-        // Make sure that we pick up references in unit tests as well
-        // Regression test for
-        // https://code.google.com/p/android/issues/detail?id=79066
-        mEnableIds = false;
-        assertEquals("No warnings.",
-
-                lintProject(
-                        copy("res/values/strings2.xml"),
-                        copy("res/layout/layout1.xml", "res/layout/main.xml"),
-                        copy("res/layout/layout1.xml", "res/layout/other.xml"),
-
-                        copy("src/my/pkg/Test.java.txt", "src/my/pkg/Test.java"),
-                        copy("gen/my/pkg/R.java.txt", "gen/my/pkg/R.java"),
-                        copy("AndroidManifest.xml"),
-                        copy("res/layout/accessibility.xml"),
-
-                        // Add unit test source which references resources which would otherwise
-                        // be marked as unused
-                        java("test/my/pkg/MyTest.java", ""
-                                + "package my.pkg;\n"
-                                + "class MyTest {\n"
-                                + "    public void test() {\n"
-                                + "        System.out.println(R.layout.accessibility);\n"
-                                + "        System.out.println(R.layout.main);\n"
-                                + "        System.out.println(R.layout.other);\n"
-                                + "        System.out.println(R.string.hello);\n"
-                                + "    }\n"
-                                + "}\n")
-                        ));
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle b/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
deleted file mode 100644
index 7281b50..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
-    compileSdkVersion 19
-    buildToolsVersion "19.0.0"
-
-    defaultConfig {
-        minSdkVersion 7
-        targetSdkVersion 19
-        versionCode 1
-        versionName "1.0"
-    }
-}
-
-dependencies {
-    compile 'com.android.support:support-v4:18.0.0'
-
-    // Suppressed:
-    //noinspection GradleCompatible
-    compile 'com.android.support:support-v4:18.0.0'
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
deleted file mode 100644
index 06d1c6b..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-package android.support.annotation;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-@Retention(CLASS)
-@Target({CONSTRUCTOR,METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
-public @interface IntRange {
-    long from() default Long.MIN_VALUE;
-    long to() default Long.MAX_VALUE;
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
deleted file mode 100644
index dbd5a47..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
+++ /dev/null
@@ -1,125 +0,0 @@
-import android.content.res.Resources;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.StringRes;
-import android.support.annotation.StyleRes;
-
-import java.util.Random;
-
-@SuppressWarnings("UnusedDeclaration")
-public class Flow {
-    public void testLiterals(Resources resources) {
-        resources.getDrawable(0); // OK
-        resources.getDrawable(-1); // OK
-        resources.getDrawable(10); // ERROR
-    }
-
-    public void testConstants(Resources resources) {
-        resources.getDrawable(R.drawable.my_drawable); // OK
-        resources.getDrawable(R.string.my_string); // ERROR
-    }
-
-    public void testLocalAnnotation() {
-        myMethod(R.string.my_string); // ERROR
-    }
-
-    private void myMethod(@DrawableRes int arg) {
-        resources.getDrawable(R.string.my_string); // ERROR
-    }
-
-    private void testAnyRes() {
-        myAnyResMethod(R.drawable.my_drawable); // OK
-        myAnyResMethod(R.string.my_string); // OK
-        myAnyResMethod(50); // ERROR
-    }
-
-    private void myAnyResMethod(@android.support.annotation.AnyRes int arg) {
-    }
-
-    public void testFields(String fileExt, Resources resources) {
-        int mimeIconId = MimeTypes.styleAndDrawable;
-        resources.getDrawable(mimeIconId); // OK
-
-        int s1 = MimeTypes.style;
-        resources.getDrawable(s1); // ERROR
-        int s2 = MimeTypes.styleAndDrawable;
-        resources.getDrawable(s2); // OK
-        int w3 = MimeTypes.drawable;
-        resources.getDrawable(w3); // OK
-
-        // Direct reference
-        resources.getDrawable(MimeTypes.style); // ERROR
-        resources.getDrawable(MimeTypes.styleAndDrawable); // OK
-        resources.getDrawable(MimeTypes.drawable); // OK
-    }
-
-    public void testCalls(String fileExt, Resources resources) {
-        int mimeIconId = MimeTypes.getIconForExt(fileExt);
-        resources.getDrawable(mimeIconId); // OK
-        resources.getDrawable(MimeTypes.getInferredString()); // OK (wrong but can't infer type)
-        resources.getDrawable(MimeTypes.getInferredDrawable()); // OK
-        resources.getDrawable(MimeTypes.getAnnotatedString()); // Error
-        resources.getDrawable(MimeTypes.getAnnotatedDrawable()); // OK
-        resources.getDrawable(MimeTypes.getUnknownType()); // OK (unknown/uncertain)
-    }
-
-    public void testFlow() {
-        int x = R.string.my_string;
-        int z = x;
-        myMethod(z); // ERROR
-
-        int w = MY_RESOURCE;
-        myMethod(w); // ERROR
-    }
-
-    private static final int MY_RESOURCE = R.string.my_string;
-
-    private static class MimeTypes {
-        @android.support.annotation.StyleRes
-        @android.support.annotation.DrawableRes
-        public static int styleAndDrawable;
-
-        @android.support.annotation.StyleRes
-        public static int style;
-
-        @android.support.annotation.DrawableRes
-        public static int drawable;
-
-        @android.support.annotation.DrawableRes
-        public static int getIconForExt(String ext) {
-            return R.drawable.my_drawable;
-        }
-
-        public static int getInferredString() {
-            // Implied string - can we handle this?
-            return R.string.my_string;
-        }
-
-        public static int getInferredDrawable() {
-            // Implied drawable - can we handle this?
-            return R.drawable.my_drawable;
-        }
-
-        @android.support.annotation.StringRes
-        public static int getAnnotatedString() {
-            return R.string.my_string;
-        }
-
-        @android.support.annotation.DrawableRes
-        public static int getAnnotatedDrawable() {
-            return R.drawable.my_drawable;
-        }
-
-        public static int getUnknownType() {
-            return new Random(1000).nextInt();
-        }
-    }
-
-    public static final class R {
-        public static final class drawable {
-            public static final int my_drawable =0x7f020057;
-        }
-        public static final class string {
-            public static final int my_string =0x7f0a000e;
-        }
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
deleted file mode 100644
index bc148c7..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
+++ /dev/null
@@ -1,143 +0,0 @@
-package test.pkg;
-
-import android.support.annotation.FloatRange;
-import android.support.annotation.IntRange;
-import android.support.annotation.Size;
-
-@SuppressWarnings("UnusedDeclaration")
-public class RangeTest {
-    public void printExact(@Size(5) String arg) { System.out.println(arg); }
-    public void printMin(@Size(min=5) String arg) { }
-    public void printMax(@Size(max=8) String arg) { }
-    public void printRange(@Size(min=4,max=6) String arg) { }
-    public void printExact(@Size(5) int[] arg) { }
-    public void printMin(@Size(min=5) int[] arg) { }
-    public void printMax(@Size(max=8) int[] arg) { }
-    public void printRange(@Size(min=4,max=6) int[] arg) { }
-    public void printMultiple(@Size(multiple=3) int[] arg) { }
-    public void printMinMultiple(@Size(min=4,multiple=3) int[] arg) { }
-    public void printAtLeast(@IntRange(from=4) int arg) { }
-    public void printAtMost(@IntRange(to=7) int arg) { }
-    public void printBetween(@IntRange(from=4,to=7) int arg) { }
-    public void printAtLeastInclusive(@FloatRange(from=2.5) float arg) { }
-    public void printAtLeastExclusive(@FloatRange(from=2.5,fromInclusive=false) float arg) { }
-    public void printAtMostInclusive(@FloatRange(to=7) double arg) { }
-    public void printAtMostExclusive(@FloatRange(to=7,toInclusive=false) double arg) { }
-    public void printBetweenFromInclusiveToInclusive(@FloatRange(from=2.5,to=5.0) float arg) { }
-    public void printBetweenFromExclusiveToInclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false) float arg) { }
-    public void printBetweenFromInclusiveToExclusive(@FloatRange(from=2.5,to=5.0,toInclusive=false) float arg) { }
-    public void printBetweenFromExclusiveToExclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false,toInclusive=false) float arg) { }
-
-    public void testLength() {
-        printExact("1234"); // ERROR
-        printExact("12345"); // OK
-        printExact("123456"); // ERROR
-
-        printMin("1234"); // ERROR
-        printMin("12345"); // OK
-        printMin("123456"); // OK
-
-        printMax("123456"); // OK
-        printMax("1234567"); // OK
-        printMax("12345678"); // OK
-        printMax("123456789"); // ERROR
-
-        printRange("123"); // ERROR
-        printRange("1234"); // OK
-        printRange("12345"); // OK
-        printRange("123456"); // OK
-        printRange("1234567"); // ERROR
-    }
-
-    public void testSize() {
-        printExact(new int[]{1, 2, 3, 4}); // ERROR
-        printExact(new int[]{1, 2, 3, 4, 5}); // OK
-        printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR
-
-        printMin(new int[]{1, 2, 3, 4}); // ERROR
-        printMin(new int[]{1, 2, 3, 4, 5}); // OK
-        printMin(new int[]{1, 2, 3, 4, 5, 6}); // OK
-
-        printMax(new int[]{1, 2, 3, 4, 5, 6}); // OK
-        printMax(new int[]{1, 2, 3, 4, 5, 6, 7}); // OK
-        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
-        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
-        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR
-
-        printRange(new int[] {1,2,3}); // ERROR
-        printRange(new int[] {1,2,3,4}); // OK
-        printRange(new int[] {1,2,3,4,5}); // OK
-        printRange(new int[] {1,2,3,4,5,6}); // OK
-        printRange(new int[] {1,2,3,4,5,6,7}); // ERROR
-
-        printMultiple(new int[] {1,2,3}); // OK
-        printMultiple(new int[] {1,2,3,4}); // ERROR
-        printMultiple(new int[] {1,2,3,4,5}); // ERROR
-        printMultiple(new int[] {1,2,3,4,5,6}); // OK
-        printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR
-
-        printMinMultiple(new int[] {1,2,3,4,5,6}); // OK
-        printMinMultiple(new int[]{1, 2, 3}); // ERROR
-    }
-
-    public void testIntRange() {
-        printAtLeast(3); // ERROR
-        printAtLeast(4); // OK
-        printAtLeast(5); // OK
-
-        printAtMost(5); // OK
-        printAtMost(6); // OK
-        printAtMost(7); // OK
-        printAtMost(8); // ERROR
-
-        printBetween(3); // ERROR
-        printBetween(4); // OK
-        printBetween(5); // OK
-        printBetween(6); // OK
-        printBetween(7); // OK
-        printBetween(8); // ERROR
-    }
-
-    public void testFloatRange() {
-        printAtLeastInclusive(2.49f); // ERROR
-        printAtLeastInclusive(2.5f); // OK
-        printAtLeastInclusive(2.6f); // OK
-
-        printAtLeastExclusive(2.49f); // ERROR
-        printAtLeastExclusive(2.5f); // ERROR
-        printAtLeastExclusive(2.501f); // OK
-
-        printAtMostInclusive(6.8f); // OK
-        printAtMostInclusive(6.9f); // OK
-        printAtMostInclusive(7.0f); // OK
-        printAtMostInclusive(7.1f); // ERROR
-
-        printAtMostExclusive(6.9f); // OK
-        printAtMostExclusive(6.99f); // OK
-        printAtMostExclusive(7.0f); // ERROR
-        printAtMostExclusive(7.1f); // ERROR
-
-        printBetweenFromInclusiveToInclusive(2.4f); // ERROR
-        printBetweenFromInclusiveToInclusive(2.5f); // OK
-        printBetweenFromInclusiveToInclusive(3f); // OK
-        printBetweenFromInclusiveToInclusive(5.0f); // OK
-        printBetweenFromInclusiveToInclusive(5.1f); // ERROR
-
-        printBetweenFromExclusiveToInclusive(2.4f); // ERROR
-        printBetweenFromExclusiveToInclusive(2.5f); // ERROR
-        printBetweenFromExclusiveToInclusive(5.0f); // OK
-        printBetweenFromExclusiveToInclusive(5.1f); // ERROR
-
-        printBetweenFromInclusiveToExclusive(2.4f); // ERROR
-        printBetweenFromInclusiveToExclusive(2.5f); // OK
-        printBetweenFromInclusiveToExclusive(3f); // OK
-        printBetweenFromInclusiveToExclusive(4.99f); // OK
-        printBetweenFromInclusiveToExclusive(5.0f); // ERROR
-
-        printBetweenFromExclusiveToExclusive(2.4f); // ERROR
-        printBetweenFromExclusiveToExclusive(2.5f); // ERROR
-        printBetweenFromExclusiveToExclusive(2.51f); // OK
-        printBetweenFromExclusiveToExclusive(4.99f); // OK
-        printBetweenFromExclusiveToExclusive(5.0f); // ERROR
-    }
-}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/cli/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
deleted file mode 100644
index 312f31f..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
+++ /dev/null
@@ -1,520 +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.tools.lint.detector.api;
-
-import static com.android.tools.lint.detector.api.LintUtils.computeResourceName;
-import static com.android.tools.lint.detector.api.LintUtils.convertVersion;
-import static com.android.tools.lint.detector.api.LintUtils.escapePropertyValue;
-import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
-import static com.android.tools.lint.detector.api.LintUtils.getFormattedParameters;
-import static com.android.tools.lint.detector.api.LintUtils.getLocaleAndRegion;
-import static com.android.tools.lint.detector.api.LintUtils.isImported;
-import static com.android.tools.lint.detector.api.LintUtils.splitPath;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ApiVersion;
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.IAndroidTarget;
-import com.android.tools.lint.EcjParser;
-import com.android.tools.lint.LintCliClient;
-import com.android.tools.lint.checks.BuiltinIssueRegistry;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.LintDriver;
-import com.google.common.collect.Iterables;
-
-import junit.framework.TestCase;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStreamWriter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Locale;
-
-import lombok.ast.Node;
-
-@SuppressWarnings("javadoc")
-public class LintUtilsTest extends TestCase {
-    public void testPrintList() throws Exception {
-        assertEquals("foo, bar, baz",
-                LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 3));
-        assertEquals("foo, bar, baz",
-                LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 5));
-
-        assertEquals("foo, bar, baz... (3 more)",
-                LintUtils.formatList(
-                        Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3));
-        assertEquals("foo... (5 more)",
-                LintUtils.formatList(
-                        Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1));
-        assertEquals("foo, bar, baz",
-                LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 0));
-    }
-
-    public void testEndsWith() throws Exception {
-        assertTrue(LintUtils.endsWith("Foo", ""));
-        assertTrue(LintUtils.endsWith("Foo", "o"));
-        assertTrue(LintUtils.endsWith("Foo", "oo"));
-        assertTrue(LintUtils.endsWith("Foo", "Foo"));
-        assertTrue(LintUtils.endsWith("Foo", "FOO"));
-        assertTrue(LintUtils.endsWith("Foo", "fOO"));
-
-        assertFalse(LintUtils.endsWith("Foo", "f"));
-    }
-
-    public void testStartsWith() throws Exception {
-        assertTrue(LintUtils.startsWith("FooBar", "Bar", 3));
-        assertTrue(LintUtils.startsWith("FooBar", "BAR", 3));
-        assertTrue(LintUtils.startsWith("FooBar", "Foo", 0));
-        assertFalse(LintUtils.startsWith("FooBar", "Foo", 2));
-    }
-
-    public void testIsXmlFile() throws Exception {
-        assertTrue(LintUtils.isXmlFile(new File("foo.xml")));
-        assertTrue(LintUtils.isXmlFile(new File("foo.Xml")));
-        assertTrue(LintUtils.isXmlFile(new File("foo.XML")));
-
-        assertFalse(LintUtils.isXmlFile(new File("foo.png")));
-        assertFalse(LintUtils.isXmlFile(new File("xml")));
-        assertFalse(LintUtils.isXmlFile(new File("xml.png")));
-    }
-
-    public void testGetBasename() throws Exception {
-        assertEquals("foo", LintUtils.getBaseName("foo.png"));
-        assertEquals("foo", LintUtils.getBaseName("foo.9.png"));
-        assertEquals(".foo", LintUtils.getBaseName(".foo"));
-    }
-
-    public void testEditDistance() {
-        assertEquals(0, LintUtils.editDistance("kitten", "kitten"));
-
-        // editing kitten to sitting has edit distance 3:
-        //   replace k with s
-        //   replace e with i
-        //   append g
-        assertEquals(3, LintUtils.editDistance("kitten", "sitting"));
-
-        assertEquals(3, LintUtils.editDistance("saturday", "sunday"));
-        assertEquals(1, LintUtils.editDistance("button", "bitton"));
-        assertEquals(6, LintUtils.editDistance("radiobutton", "bitton"));
-    }
-
-    public void testSplitPath() throws Exception {
-        assertTrue(Arrays.equals(new String[] { "/foo", "/bar", "/baz" },
-                Iterables.toArray(splitPath("/foo:/bar:/baz"), String.class)));
-
-        assertTrue(Arrays.equals(new String[] { "/foo", "/bar" },
-                Iterables.toArray(splitPath("/foo;/bar"), String.class)));
-
-        assertTrue(Arrays.equals(new String[] { "/foo", "/bar:baz" },
-                Iterables.toArray(splitPath("/foo;/bar:baz"), String.class)));
-
-        assertTrue(Arrays.equals(new String[] { "\\foo\\bar", "\\bar\\foo" },
-                Iterables.toArray(splitPath("\\foo\\bar;\\bar\\foo"), String.class)));
-
-        assertTrue(Arrays.equals(new String[] { "${sdk.dir}\\foo\\bar", "\\bar\\foo" },
-                Iterables.toArray(splitPath("${sdk.dir}\\foo\\bar;\\bar\\foo"),
-                        String.class)));
-
-        assertTrue(Arrays.equals(new String[] { "${sdk.dir}/foo/bar", "/bar/foo" },
-                Iterables.toArray(splitPath("${sdk.dir}/foo/bar:/bar/foo"),
-                        String.class)));
-
-        assertTrue(Arrays.equals(new String[] { "C:\\foo", "/bar" },
-                Iterables.toArray(splitPath("C:\\foo:/bar"), String.class)));
-    }
-
-    public void testCommonParen1() {
-        assertEquals(new File("/a"), (LintUtils.getCommonParent(
-                new File("/a/b/c/d/e"), new File("/a/c"))));
-        assertEquals(new File("/a"), (LintUtils.getCommonParent(
-                new File("/a/c"), new File("/a/b/c/d/e"))));
-
-        assertEquals(new File("/"), LintUtils.getCommonParent(
-                new File("/foo/bar"), new File("/bar/baz")));
-        assertEquals(new File("/"), LintUtils.getCommonParent(
-                new File("/foo/bar"), new File("/")));
-        assertNull(LintUtils.getCommonParent(
-               new File("C:\\Program Files"), new File("F:\\")));
-        assertNull(LintUtils.getCommonParent(
-                new File("C:/Program Files"), new File("F:/")));
-
-        assertEquals(new File("/foo/bar/baz"), LintUtils.getCommonParent(
-                new File("/foo/bar/baz"), new File("/foo/bar/baz")));
-        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
-                new File("/foo/bar/baz"), new File("/foo/bar")));
-        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
-                new File("/foo/bar/baz"), new File("/foo/bar/foo")));
-        assertEquals(new File("/foo"), LintUtils.getCommonParent(
-                new File("/foo/bar"), new File("/foo/baz")));
-        assertEquals(new File("/foo"), LintUtils.getCommonParent(
-                new File("/foo/bar"), new File("/foo/baz")));
-        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
-                new File("/foo/bar"), new File("/foo/bar/baz")));
-    }
-
-    public void testCommonParent2() {
-        assertEquals(new File("/"), LintUtils.getCommonParent(
-                Arrays.asList(new File("/foo/bar"), new File("/bar/baz"))));
-        assertEquals(new File("/"), LintUtils.getCommonParent(
-                Arrays.asList(new File("/foo/bar"), new File("/"))));
-        assertNull(LintUtils.getCommonParent(
-                Arrays.asList(new File("C:\\Program Files"), new File("F:\\"))));
-        assertNull(LintUtils.getCommonParent(
-                Arrays.asList(new File("C:/Program Files"), new File("F:/"))));
-
-        assertEquals(new File("/foo"), LintUtils.getCommonParent(
-                Arrays.asList(new File("/foo/bar"), new File("/foo/baz"))));
-        assertEquals(new File("/foo"), LintUtils.getCommonParent(
-                Arrays.asList(new File("/foo/bar"), new File("/foo/baz"),
-                        new File("/foo/baz/f"))));
-        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
-                Arrays.asList(new File("/foo/bar"), new File("/foo/bar/baz"),
-                        new File("/foo/bar/foo2/foo3"))));
-    }
-
-    public void testStripIdPrefix() throws Exception {
-        assertEquals("foo", LintUtils.stripIdPrefix("@+id/foo"));
-        assertEquals("foo", LintUtils.stripIdPrefix("@id/foo"));
-        assertEquals("foo", LintUtils.stripIdPrefix("foo"));
-    }
-
-    public void testIdReferencesMatch() throws Exception {
-        assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo"));
-        assertTrue(LintUtils.idReferencesMatch("@id/foo", "@id/foo"));
-        assertTrue(LintUtils.idReferencesMatch("@id/foo", "@+id/foo"));
-        assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@id/foo"));
-
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
-        assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/bar"));
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/bar"));
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
-
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo1"));
-        assertFalse(LintUtils.idReferencesMatch("@id/foo", "@id/foo1"));
-        assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/foo1"));
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/foo1"));
-
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@+id/foo"));
-        assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@id/foo"));
-        assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@+id/foo"));
-        assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@id/foo"));
-    }
-
-    private static void checkEncoding(String encoding, boolean writeBom, String lineEnding)
-            throws Exception {
-        @SuppressWarnings("StringBufferReplaceableByString")
-        StringBuilder sb = new StringBuilder();
-
-        // Norwegian extra vowel characters such as "latin small letter a with ring above"
-        String value = "\u00e6\u00d8\u00e5";
-        String expected = "First line." + lineEnding + "Second line." + lineEnding
-                + "Third line." + lineEnding + value + lineEnding;
-        sb.append(expected);
-        File file = File.createTempFile("getEncodingTest" + encoding + writeBom, ".txt");
-        file.deleteOnExit();
-        BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
-        OutputStreamWriter writer = new OutputStreamWriter(stream, encoding);
-
-        if (writeBom) {
-            String normalized = encoding.toLowerCase(Locale.US).replace("-", "_");
-            if (normalized.equals("utf_8")) {
-                stream.write(0xef);
-                stream.write(0xbb);
-                stream.write(0xbf);
-            } else if (normalized.equals("utf_16")) {
-                stream.write(0xfe);
-                stream.write(0xff);
-            } else if (normalized.equals("utf_16le")) {
-                stream.write(0xff);
-                stream.write(0xfe);
-            } else if (normalized.equals("utf_32")) {
-                stream.write(0x0);
-                stream.write(0x0);
-                stream.write(0xfe);
-                stream.write(0xff);
-            } else if (normalized.equals("utf_32le")) {
-                stream.write(0xff);
-                stream.write(0xfe);
-                stream.write(0x0);
-                stream.write(0x0);
-            } else {
-                fail("Can't write BOM for encoding " + encoding);
-            }
-        }
-        writer.write(sb.toString());
-        writer.close();
-
-        String s = LintUtils.getEncodedString(new LintCliClient(), file);
-        assertEquals(expected, s);
-    }
-
-    public void testGetEncodedString() throws Exception {
-        checkEncoding("utf-8", false /*bom*/, "\n");
-        checkEncoding("UTF-8", false /*bom*/, "\n");
-        checkEncoding("UTF_16", false /*bom*/, "\n");
-        checkEncoding("UTF-16", false /*bom*/, "\n");
-        checkEncoding("UTF_16LE", false /*bom*/, "\n");
-
-        // Try BOM's
-        checkEncoding("utf-8", true /*bom*/, "\n");
-        checkEncoding("UTF-8", true /*bom*/, "\n");
-        checkEncoding("UTF_16", true /*bom*/, "\n");
-        checkEncoding("UTF-16", true /*bom*/, "\n");
-        checkEncoding("UTF_16LE", true /*bom*/, "\n");
-        checkEncoding("UTF_32", true /*bom*/, "\n");
-        checkEncoding("UTF_32LE", true /*bom*/, "\n");
-
-        // Make sure this works for \r and \r\n as well
-        checkEncoding("UTF-16", false /*bom*/, "\r");
-        checkEncoding("UTF_16LE", false /*bom*/, "\r");
-        checkEncoding("UTF-16", false /*bom*/, "\r\n");
-        checkEncoding("UTF_16LE", false /*bom*/, "\r\n");
-        checkEncoding("UTF-16", true /*bom*/, "\r");
-        checkEncoding("UTF_16LE", true /*bom*/, "\r");
-        checkEncoding("UTF_32", true /*bom*/, "\r");
-        checkEncoding("UTF_32LE", true /*bom*/, "\r");
-        checkEncoding("UTF-16", true /*bom*/, "\r\n");
-        checkEncoding("UTF_16LE", true /*bom*/, "\r\n");
-        checkEncoding("UTF_32", true /*bom*/, "\r\n");
-        checkEncoding("UTF_32LE", true /*bom*/, "\r\n");
-    }
-
-    public void testGetLocaleAndRegion() throws Exception {
-        assertNull(getLocaleAndRegion(""));
-        assertNull(getLocaleAndRegion("values"));
-        assertNull(getLocaleAndRegion("values-xlarge-port"));
-        assertEquals("en", getLocaleAndRegion("values-en"));
-        assertEquals("pt-rPT", getLocaleAndRegion("values-pt-rPT-nokeys"));
-        assertEquals("b+pt+PT", getLocaleAndRegion("values-b+pt+PT-nokeys"));
-        assertEquals("zh-rCN", getLocaleAndRegion("values-zh-rCN-keyshidden"));
-        assertEquals("ms", getLocaleAndRegion("values-ms-keyshidden"));
-    }
-
-    public void testIsImported() throws Exception {
-        assertFalse(isImported(getCompilationUnit(
-                "package foo.bar;\n" +
-                "class Foo {\n" +
-                "}\n"),
-                "android.app.Activity"));
-
-        assertTrue(isImported(getCompilationUnit(
-                "package foo.bar;\n" +
-                "import foo.bar.*;\n" +
-                "import android.app.Activity;\n" +
-                "import foo.bar.Baz;\n" +
-                "class Foo {\n" +
-                "}\n"),
-                "android.app.Activity"));
-
-        assertTrue(isImported(getCompilationUnit(
-                "package foo.bar;\n" +
-                "import android.app.Activity;\n" +
-                "class Foo {\n" +
-                "}\n"),
-                "android.app.Activity"));
-
-        assertTrue(isImported(getCompilationUnit(
-                "package foo.bar;\n" +
-                "import android.app.*;\n" +
-                "class Foo {\n" +
-                "}\n"),
-                "android.app.Activity"));
-
-        assertFalse(isImported(getCompilationUnit(
-                "package foo.bar;\n" +
-                "import android.app.*;\n" +
-                "import foo.bar.Activity;\n" +
-                "class Foo {\n" +
-                "}\n"),
-                "android.app.Activity"));
-    }
-
-    public void testComputeResourceName() {
-        assertEquals("", computeResourceName("", ""));
-        assertEquals("foo", computeResourceName("", "foo"));
-        assertEquals("foo", computeResourceName("foo", ""));
-        assertEquals("prefix_name", computeResourceName("prefix_", "name"));
-        assertEquals("prefixName", computeResourceName("prefix", "name"));
-    }
-
-    public static Node getCompilationUnit(String javaSource) {
-        return getCompilationUnit(javaSource, new File("test"));
-    }
-
-
-    public static Node getCompilationUnit(final String javaSource, final File relativePath) {
-        JavaContext context = parse(javaSource, relativePath);
-        return context.getCompilationUnit();
-    }
-
-    public static JavaContext parse(final String javaSource, final File relativePath) {
-        File dir = new File("projectDir");
-        final File fullPath = new File(dir, relativePath.getPath());
-        LintCliClient client = new LintCliClient() {
-            @NonNull
-            @Override
-            public String readFile(@NonNull File file) {
-                if (file.getPath().equals(fullPath.getPath())) {
-                    return javaSource;
-                }
-                return super.readFile(file);
-            }
-
-            @Nullable
-            @Override
-            public IAndroidTarget getCompileTarget(@NonNull Project project) {
-                IAndroidTarget[] targets = getTargets();
-                for (int i = targets.length - 1; i >= 0; i--) {
-                    IAndroidTarget target = targets[i];
-                    if (target.isPlatform()) {
-                        return target;
-                    }
-                }
-
-                return super.getCompileTarget(project);
-            }
-        };
-        Project project = client.getProject(dir, dir);
-
-        LintDriver driver = new LintDriver(new BuiltinIssueRegistry(),
-                new LintCliClient());
-        driver.setScope(Scope.JAVA_FILE_SCOPE);
-        TestContext context = new TestContext(driver, client, project, javaSource, fullPath);
-        JavaParser parser = new EcjParser(client, project);
-        parser.prepareJavaParse(Collections.<JavaContext>singletonList(context));
-        Node compilationUnit = parser.parseJava(context);
-        assertNotNull(javaSource, compilationUnit);
-        context.setCompilationUnit(compilationUnit);
-        return context;
-    }
-
-    public void testConvertVersion() {
-        assertEquals(new AndroidVersion(5, null), convertVersion(new DefaultApiVersion(5, null),
-                null));
-        assertEquals(new AndroidVersion(19, null), convertVersion(new DefaultApiVersion(19, null),
-                null));
-        //noinspection SpellCheckingInspection
-        assertEquals(new AndroidVersion(18, "KITKAT"), // a preview platform API level is not final
-                convertVersion(new DefaultApiVersion(0, "KITKAT"),
-                null));
-    }
-
-    public void testIsModelOlderThan() throws Exception {
-        AndroidProject project = mock(AndroidProject.class);
-        when(project.getModelVersion()).thenReturn("0.10.4");
-
-        assertTrue(LintUtils.isModelOlderThan(project, 0, 10, 5));
-        assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 0));
-        assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 4));
-        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
-
-        project = mock(AndroidProject.class);
-        when(project.getModelVersion()).thenReturn("0.11.0");
-
-        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
-        assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
-        assertFalse(LintUtils.isModelOlderThan(project, 0, 10, 4));
-
-        project = mock(AndroidProject.class);
-        when(project.getModelVersion()).thenReturn("0.11.5");
-
-        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
-        assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
-
-        project = mock(AndroidProject.class);
-        when(project.getModelVersion()).thenReturn("1.0.0");
-
-        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 1));
-        assertFalse(LintUtils.isModelOlderThan(project, 1, 0, 0));
-        assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
-    }
-
-    private static final class DefaultApiVersion implements ApiVersion {
-        private final int mApiLevel;
-        private final String mCodename;
-
-        public DefaultApiVersion(int apiLevel, @Nullable String codename) {
-            mApiLevel = apiLevel;
-            mCodename = codename;
-        }
-
-        @Override
-        public int getApiLevel() {
-            return mApiLevel;
-        }
-
-        @Nullable
-        @Override
-        public String getCodename() {
-            return mCodename;
-        }
-
-        @NonNull
-        @Override
-        public String getApiString() {
-            fail("Not needed in this test");
-            return "<invalid>";
-        }
-    }
-
-    public void testFindSubstring() {
-       assertEquals("foo", findSubstring("foo", null, null));
-       assertEquals("foo", findSubstring("foo  ", null, "  "));
-       assertEquals("foo", findSubstring("  foo", "  ", null));
-       assertEquals("foo", findSubstring("[foo]", "[", "]"));
-    }
-
-    public void testGetFormattedParameters() {
-        assertEquals(Arrays.asList("foo","bar"),
-                getFormattedParameters("Prefix %1$s Divider %2$s Suffix",
-                        "Prefix foo Divider bar Suffix"));
-    }
-
-    public void testEscapePropertyValue() throws Exception {
-        assertEquals("foo", escapePropertyValue("foo"));
-        assertEquals("\\  foo  ", escapePropertyValue("  foo  "));
-        assertEquals("c\\:/foo/bar", escapePropertyValue("c:/foo/bar"));
-        assertEquals("\\!\\#\\:\\\\a\\\\b\\\\c", escapePropertyValue("!#:\\a\\b\\c"));
-        assertEquals(
-                "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo\\#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo",
-                escapePropertyValue("foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo"));
-    }
-
-    private static class TestContext extends JavaContext {
-        private final String mJavaSource;
-        public TestContext(LintDriver driver, LintCliClient client, Project project,
-                String javaSource, File file) {
-            //noinspection ConstantConditions
-            super(driver, project,
-                    null, file, client.getJavaParser(null));
-
-            mJavaSource = javaSource;
-        }
-
-        @Override
-        @Nullable
-        public String getContents() {
-            return mJavaSource;
-        }
-    }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java b/lint/cli/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
deleted file mode 100644
index a4eaf5b..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2014 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.tools.lint.detector.api;
-
-import static com.android.SdkConstants.AUTO_URI;
-import static com.android.tools.lint.detector.api.TextFormat.HTML;
-import static com.android.tools.lint.detector.api.TextFormat.RAW;
-import static com.android.tools.lint.detector.api.TextFormat.TEXT;
-
-import junit.framework.TestCase;
-
-public class TextFormatTest extends TestCase {
-    private static String convertMarkup(String raw, TextFormat to) {
-        return RAW.convertTo(raw, to);
-    }
-
-    public void testConvertMarkup() throws Exception {
-            assertEquals("", convertMarkup("", HTML));
-
-            // Normal escapes
-            assertEquals("foo bar", convertMarkup("foo bar", HTML));
-            assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
-            assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
-            assertEquals("&lt;&amp;>'\"", convertMarkup("<&>'\"", HTML));
-
-            // HTML Formatting
-            assertEquals("<code>@TargetApi(11)</code>, ", convertMarkup("`@TargetApi(11)`, ",
-                    HTML));
-            assertEquals("with <code>getArguments()</code>.",
-                    convertMarkup("with `getArguments()`.", HTML));
-            assertEquals("(<code>dip</code>)", convertMarkup("(`dip`)", HTML));
-            assertEquals(" <code>0dp</code> ", convertMarkup(" `0dp` ", HTML));
-            assertEquals(
-                "resources under <code>$ANDROID_SK/platforms/android-$VERSION/data/res/.</code>",
-                    convertMarkup(
-                            "resources under `$ANDROID_SK/platforms/android-$VERSION/data/res/.`",
-                            HTML));
-            assertEquals("wrong format. Instead of <code>-keepclasseswithmembernames</code> use ",
-                    convertMarkup("wrong format. Instead of `-keepclasseswithmembernames` use ",
-                            HTML));
-            assertEquals("<code>exported=false</code>)", convertMarkup("`exported=false`)",
-                    HTML));
-            assertEquals("by setting <code>inputType=\"text\"</code>.",
-                    convertMarkup("by setting `inputType=\"text\"`.", HTML));
-            assertEquals("* <code>View(Context context)</code><br/>\n",
-                    convertMarkup("* `View(Context context)`\n", HTML));
-            assertEquals("The <code>@+id/</code> syntax", convertMarkup("The `@+id/` syntax",
-                    HTML));
-            assertEquals("", convertMarkup("", HTML));
-            assertEquals("", convertMarkup("", HTML));
-            assertEquals("This is <b>bold</b>", convertMarkup("This is *bold*", HTML));
-            assertEquals("Visit <a href=\"http://google.com\">http://google.com</a>.",
-                    convertMarkup("Visit http://google.com.", HTML));
-            assertEquals("This is <code>monospace</code>!", convertMarkup("This is `monospace`!",
-                    HTML));
-            assertEquals(
-                    "See <a href=\"http://developer.android.com/reference/android/view/" +
-                    "WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON\">http://developer." +
-                    "android.com/reference/android/view/WindowManager.LayoutParams.html#" +
-                    "FLAG_KEEP_SCREEN_ON</a>.",
-                convertMarkup(
-                  "See http://developer.android.com/reference/android/view/WindowManager.Layout" +
-                  "Params.html#FLAG_KEEP_SCREEN_ON.", HTML));
-
-            // Text formatting
-            assertEquals("@TargetApi(11), ", convertMarkup("`@TargetApi(11)`, ", TEXT));
-            assertEquals("with getArguments().", convertMarkup("with `getArguments()`.", TEXT));
-            assertEquals("bold", convertMarkup("*bold*", TEXT));
-            assertEquals("Visit http://google.com.", convertMarkup("Visit http://google.com.",
-                    TEXT));
-
-            // Corners (match at the beginning and end)
-            assertEquals("<b>bold</b>", convertMarkup("*bold*", HTML));
-            assertEquals("<code>monospace</code>!", convertMarkup("`monospace`!", HTML));
-
-            // Not formatting
-            assertEquals("a*b", convertMarkup("a*b", HTML));
-            assertEquals("a* b*", convertMarkup("a* b*", HTML));
-            assertEquals("*a *b", convertMarkup("*a *b", HTML));
-            assertEquals("Prefix is http:// ", convertMarkup("Prefix is http:// ", HTML));
-            assertEquals("", convertMarkup("", HTML));
-            assertEquals("", convertMarkup("", HTML));
-            assertEquals("", convertMarkup("", HTML));
-            assertEquals("", convertMarkup("", HTML));
-            assertEquals("This is * not * bold", convertMarkup("This is * not * bold", HTML));
-            assertEquals("* List item 1<br/>\n* List Item 2",
-                    convertMarkup("* List item 1\n* List Item 2", HTML));
-            assertEquals("myhttp://foo.bar", convertMarkup("myhttp://foo.bar", HTML));
-        }
-
-    public void testConvertMarkup2() throws Exception {
-        // http at the end:
-        // Explanation from ManifestDetector#TARGET_NEWER
-        String explanation =
-            "When your application runs on a version of Android that is more recent than your " +
-            "targetSdkVersion specifies that it has been tested with, various compatibility " +
-            "modes kick in. This ensures that your application continues to work, but it may " +
-            "look out of place. For example, if the targetSdkVersion is less than 14, your " +
-            "app may get an option button in the UI.\n" +
-            "\n" +
-            "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
-            "test your app to make sure everything works correctly. You may want to consult " +
-            "the compatibility notes to see what changes apply to each version you are adding " +
-            "support for: " +
-            "http://developer.android.com/reference/android/os/Build.VERSION_CODES.html";
-
-        assertEquals(
-            "When your application runs on a version of Android that is more recent than your " +
-            "targetSdkVersion specifies that it has been tested with, various compatibility " +
-            "modes kick in. This ensures that your application continues to work, but it may " +
-            "look out of place. For example, if the targetSdkVersion is less than 14, your " +
-            "app may get an option button in the UI.<br/>\n" +
-            "<br/>\n" +
-            "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
-            "test your app to make sure everything works correctly. You may want to consult " +
-            "the compatibility notes to see what changes apply to each version you are adding " +
-            "support for: " +
-            "<a href=\"http://developer.android.com/reference/android/os/Build.VERSION_CODES." +
-            "html\">http://developer.android.com/reference/android/os/Build.VERSION_CODES.html" +
-            "</a>",
-            convertMarkup(explanation, HTML));
-    }
-
-    public void testConvertMarkup3() throws Exception {
-        // embedded http markup test
-        // Explanation from NamespaceDetector#CUSTOMVIEW
-        String explanation =
-            "When using a custom view with custom attributes in a library project, the layout " +
-            "must use the special namespace " + AUTO_URI + " instead of a URI which includes " +
-            "the library project's own package. This will be used to automatically adjust the " +
-            "namespace of the attributes when the library resources are merged into the " +
-            "application project.";
-        assertEquals(
-            "When using a custom view with custom attributes in a library project, the layout " +
-            "must use the special namespace " +
-            "<a href=\"http://schemas.android.com/apk/res-auto\">" +
-            "http://schemas.android.com/apk/res-auto</a> " +
-            "instead of a URI which includes the library project's own package. " +
-            "This will be used to automatically adjust the namespace of the attributes when " +
-            "the library resources are merged into the application project.",
-            convertMarkup(explanation, HTML));
-    }
-
-    public void testConvertMarkup4() throws Exception {
-        // monospace test
-        String explanation =
-            "The manifest should contain a `<uses-sdk>` element which defines the " +
-            "minimum minimum API Level required for the application to run, " +
-            "as well as the target version (the highest API level you have tested " +
-            "the version for.)";
-
-        assertEquals(
-            "The manifest should contain a <code>&lt;uses-sdk></code> element which defines the " +
-            "minimum minimum API Level required for the application to run, " +
-            "as well as the target version (the highest API level you have tested " +
-            "the version for.)",
-            convertMarkup(explanation, HTML));
-    }
-
-    public void testConvertMarkup5() throws Exception {
-        // monospace and bold test
-        // From ManifestDetector#MULTIPLE_USES_SDK
-        String explanation =
-            "The `<uses-sdk>` element should appear just once; the tools will *not* merge the " +
-            "contents of all the elements so if you split up the attributes across multiple " +
-            "elements, only one of them will take effect. To fix this, just merge all the " +
-            "attributes from the various elements into a single <uses-sdk> element.";
-
-        assertEquals(
-            "The <code>&lt;uses-sdk></code> element should appear just once; the tools " +
-            "will <b>not</b> merge the " +
-            "contents of all the elements so if you split up the attributes across multiple " +
-            "elements, only one of them will take effect. To fix this, just merge all the " +
-            "attributes from the various elements into a single &lt;uses-sdk> element.",
-            convertMarkup(explanation, HTML));
-        }
-
-    public void testConvertMarkup6() throws Exception {
-        // Embedded code next to attributes
-        // From AlwaysShowActionDetector#ISSUE
-        String explanation =
-            "Using `showAsAction=\"always\"` in menu XML, or `MenuItem.SHOW_AS_ACTION_ALWAYS` in "+
-            "Java code is usually a deviation from the user interface style guide." +
-            "Use `ifRoom` or the corresponding `MenuItem.SHOW_AS_ACTION_IF_ROOM` instead.\n" +
-            "\n" +
-            "If `always` is used sparingly there are usually no problems and behavior is " +
-            "roughly equivalent to `ifRoom` but with preference over other `ifRoom` " +
-            "items. Using it more than twice in the same menu is a bad idea.\n" +
-            "\n" +
-            "This check looks for menu XML files that contain more than two `always` " +
-            "actions, or some `always` actions and no `ifRoom` actions. In Java code, " +
-            "it looks for projects that contain references to `MenuItem.SHOW_AS_ACTION_ALWAYS` " +
-            "and no references to `MenuItem.SHOW_AS_ACTION_IF_ROOM`.";
-
-        assertEquals(
-            "Using <code>showAsAction=\"always\"</code> in menu XML, or " +
-            "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
-            "from the user interface style guide.Use <code>ifRoom</code> or the " +
-            "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
-            "<br/>\n" +
-            "If <code>always</code> is used sparingly there are usually no problems and " +
-            "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
-            "other <code>ifRoom</code> items. Using it more than twice in the same menu " +
-            "is a bad idea.<br/>\n" +
-            "<br/>\n" +
-            "This check looks for menu XML files that contain more than two <code>always</code> " +
-            "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
-            "In Java code, it looks for projects that contain references to " +
-            "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
-            "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
-            convertMarkup(explanation, HTML));
-    }
-
-    public void testConvertSelf() throws Exception {
-        // No changes
-        assertEquals("`foo`<b>", RAW.convertTo("`foo`<b>", RAW));
-        assertEquals("`foo`<b>", TEXT.convertTo("`foo`<b>", TEXT));
-        assertEquals("`foo`<b>", HTML.convertTo("`foo`<b>", HTML));
-    }
-
-    public void testConvertFromHtml() throws Exception {
-        assertEquals(""
-                        + "Line 1\n"
-                        + "Line 2 <div>\n",
-                HTML.convertTo("<html>Line 1<br>Line 2\n<!-- comment -->&lt;div&gt;</html>",
-                        TEXT));
-    }
-
-    public void testConvertFromHtml2() throws Exception {
-        assertEquals(""
-                        + "Using showAsAction=\"always\" in menu XML, or\n"
-                        + "MenuItem.SHOW_AS_ACTION_ALWAYS in Java code is usually a\n"
-                        + "deviation from the user interface style guide.Use ifRoom or\n"
-                        + "the corresponding MenuItem.SHOW_AS_ACTION_IF_ROOM instead.\n"
-                        + "If always is used sparingly there are usually no problems\n"
-                        + "and behavior is roughly equivalent to ifRoom but with\n"
-                        + "preference over other ifRoom items. Using it more than twice\n"
-                        + "in the same menu is a bad idea. This check looks for menu\n"
-                        + "XML files that contain more than two always actions, or some\n"
-                        + "always actions and no ifRoom actions. In Java code, it looks\n"
-                        + "for projects that contain references to\n"
-                        + "MenuItem.SHOW_AS_ACTION_ALWAYS and no references to\n"
-                        + "MenuItem.SHOW_AS_ACTION_IF_ROOM.\n",
-                HTML.convertTo(
-                        "Using <code>showAsAction=\"always\"</code> in       menu XML, or " +
-                        "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
-                        "from the user interface style guide.Use <code>ifRoom</code> or the " +
-                        "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
-                        "<br/>\n" +
-                        "If <code>always</code> is used sparingly there are usually no problems and " +
-                        "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
-                        "other <code>ifRoom</code> items.    Using it more than twice in the same menu " +
-                        "is a bad idea.<br/>\n" +
-                        "<br/>\n" +
-                        "This check looks for menu XML files that contain more than two <code>always</code> " +
-                        "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
-                        "In Java code, it looks for projects that contain references to " +
-                        "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
-                        "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
-                TEXT));
-    }
-
-    public void testNbsp() throws Exception {
-        assertEquals("&nbsp;&nbsp;text", RAW.convertTo("\u00a0\u00A0text", HTML));
-    }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-api/build.gradle b/lint/libs/lint-api/build.gradle
index 9a810ee..df9d553 100644
--- a/lint/libs/lint-api/build.gradle
+++ b/lint/libs/lint-api/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools.lint'
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java
index ca7e76e..79a4a39 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java
@@ -267,7 +267,7 @@
         }, mClient);
         mClient.report(new Context(driver, mProject, mProject, mConfigFile),
                 IssueRegistry.LINT_ERROR,
-                mProject.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
+                mProject.getConfiguration(driver).getSeverity(IssueRegistry.LINT_ERROR),
                 Location.create(mConfigFile), message, TextFormat.RAW);
     }
 
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
index 97917e9..f150eb2 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
@@ -190,10 +190,15 @@
         /** If the type is not primitive, returns the class of the type if known */
         @Nullable
         public abstract ResolvedClass getTypeClass();
+
+        @Override
+        public abstract boolean equals(Object o);
+
     }
 
     /** Convenience implementation of {@link TypeDescriptor} */
     public static class DefaultTypeDescriptor extends TypeDescriptor {
+
         private String mName;
 
         public DefaultTypeDescriptor(String name) {
@@ -232,6 +237,26 @@
         public ResolvedClass getTypeClass() {
             return null;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            DefaultTypeDescriptor that = (DefaultTypeDescriptor) o;
+
+            return !(mName != null ? !mName.equals(that.mName) : that.mName != null);
+
+        }
+
+        @Override
+        public int hashCode() {
+            return mName != null ? mName.hashCode() : 0;
+        }
     }
 
     /** A resolved declaration from an AST Node reference */
@@ -269,6 +294,18 @@
 
             return null;
         }
+
+        /**
+         * Returns true if this element is in the given package (or optionally, in one of its sub
+         * packages)
+         *
+         * @param pkg                the package name
+         * @param includeSubPackages whether to include subpackages
+         * @return true if the element is in the given package
+         */
+        public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+            return getSignature().startsWith(pkg);
+        }
     }
 
     /** A resolved class declaration (class, interface, enumeration or annotation) */
@@ -327,9 +364,31 @@
         @NonNull
         public abstract Iterable<ResolvedMethod> getMethods(@NonNull String name, boolean includeInherited);
 
+        /** Returns the fields defined in this class, and optionally any fields declared in any superclasses as well */
+        @NonNull
+        public abstract Iterable<ResolvedField> getFields(boolean includeInherited);
+
         /** Returns the named field defined in this class, or optionally inherited from a superclass */
         @Nullable
         public abstract ResolvedField getField(@NonNull String name, boolean includeInherited);
+
+        /** Returns the package containing this class */
+        @Nullable
+        public abstract ResolvedPackage getPackage();
+
+        @Override
+        public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+            String packageName = getPackageName();
+
+            //noinspection SimplifiableIfStatement
+            if (pkg.equals(packageName)) {
+                return true;
+            }
+
+            return includeSubPackages && packageName.length() > pkg.length() &&
+                    packageName.charAt(pkg.length()) == '.' &&
+                    packageName.startsWith(pkg);
+        }
     }
 
     /** A method or constructor declaration */
@@ -410,6 +469,20 @@
 
             return null;
         }
+
+        @Override
+        public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+            String packageName = getContainingClass().getPackageName();
+
+            //noinspection SimplifiableIfStatement
+            if (pkg.equals(packageName)) {
+                return true;
+            }
+
+            return includeSubPackages && packageName.length() > pkg.length() &&
+                    packageName.charAt(pkg.length()) == '.' &&
+                    packageName.startsWith(pkg);
+        }
     }
 
     /** A field declaration */
@@ -433,6 +506,20 @@
         public String getContainingClassName() {
             return getContainingClass().getName();
         }
+
+        @Override
+        public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+            String packageName = getContainingClass().getPackageName();
+
+            //noinspection SimplifiableIfStatement
+            if (pkg.equals(packageName)) {
+                return true;
+            }
+
+            return includeSubPackages && packageName.length() > pkg.length() &&
+                   packageName.charAt(pkg.length()) == '.' &&
+                   packageName.startsWith(pkg);
+        }
     }
 
     /**
@@ -469,7 +556,14 @@
         public abstract List<Value> getValues();
 
         @Nullable
-        public abstract Object getValue(@NonNull String name);
+        public Object getValue(@NonNull String name) {
+            for (Value value : getValues()) {
+                if (name.equals(value.name)) {
+                    return value.value;
+                }
+            }
+            return null;
+        }
 
         @Nullable
         public Object getValue() {
@@ -483,6 +577,15 @@
         }
     }
 
+    /** A package declaration */
+    public abstract static class ResolvedPackage extends ResolvedNode {
+        @NonNull
+        @Override
+        public Iterable<ResolvedAnnotation> getAnnotations() {
+            return Collections.emptyList();
+        }
+    }
+
     /** A local variable or parameter declaration */
     public abstract static class ResolvedVariable extends ResolvedNode {
         @Override
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java
index 0980af2..fbce577 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaVisitor.java
@@ -151,6 +151,15 @@
     private final Map<String, List<VisitingDetector>> mSuperClassDetectors =
             new HashMap<String, List<VisitingDetector>>();
 
+    /**
+     * Number of fatal exceptions (internal errors, usually from ECJ) we've
+     * encountered; we don't log each and every one to avoid massive log spam
+     * in code which triggers this condition
+     */
+    private static int sExceptionCount;
+    /** Max number of logs to include */
+    private static final int MAX_REPORTED_CRASHES = 20;
+
     JavaVisitor(@NonNull JavaParser parser, @NonNull List<Detector> detectors) {
         mParser = parser;
         mAllDetectors = new ArrayList<VisitingDetector>(detectors.size());
@@ -269,6 +278,43 @@
             for (VisitingDetector v : mAllDetectors) {
                 v.getDetector().afterCheckFile(context);
             }
+        } catch (RuntimeException e) {
+            if (sExceptionCount++ > MAX_REPORTED_CRASHES) {
+                // No need to keep spamming the user that a lot of the files
+                // are tripping up ECJ, they get the picture.
+                return;
+            }
+
+            // Work around ECJ bugs; see https://code.google.com/p/android/issues/detail?id=172268
+            // Don't allow lint bugs to take down the whole build. TRY to log this as a
+            // lint error instead!
+            StringBuilder sb = new StringBuilder(100);
+            sb.append("Unexpected failure during lint analysis of ");
+            sb.append(context.file.getName());
+            sb.append(" (this is a bug in lint or one of the libraries it depends on)\n");
+
+            StackTraceElement[] stackTrace = e.getStackTrace();
+            int count = 0;
+            for (StackTraceElement frame : stackTrace) {
+                if (count > 0) {
+                    sb.append("->");
+                }
+
+                String className = frame.getClassName();
+                sb.append(className.substring(className.lastIndexOf('.') + 1));
+                sb.append('.').append(frame.getMethodName());
+                sb.append('(');
+                sb.append(frame.getFileName()).append(':').append(frame.getLineNumber());
+                sb.append(')');
+                count++;
+                // Only print the top 3-4 frames such that we can identify the bug
+                if (count == 4) {
+                    break;
+                }
+            }
+            Throwable throwable = null; // NOT e: this makes for very noisy logs
+            //noinspection ConstantConditions
+            context.log(throwable, sb.toString());
         } finally {
             if (compilationUnit != null) {
                 mParser.dispose(context, compilationUnit);
@@ -343,13 +389,10 @@
             ResolvedClass resolvedClass = (ResolvedClass) resolved;
             ResolvedClass cls = resolvedClass;
             while (cls != null) {
-                String fqcn = cls.getSignature();
-                if (fqcn != null) {
-                    List<VisitingDetector> list = mSuperClassDetectors.get(fqcn);
-                    if (list != null) {
-                        for (VisitingDetector v : list) {
-                            v.getJavaScanner().checkClass(mContext, node, node, resolvedClass);
-                        }
+                List<VisitingDetector> list = mSuperClassDetectors.get(cls.getName());
+                if (list != null) {
+                    for (VisitingDetector v : list) {
+                        v.getJavaScanner().checkClass(mContext, node, node, resolvedClass);
                     }
                 }
 
@@ -374,14 +417,11 @@
                 ResolvedClass resolvedClass = (ResolvedClass) resolved;
                 ResolvedClass cls = resolvedClass;
                 while (cls != null) {
-                    String fqcn = cls.getSignature();
-                    if (fqcn != null) {
-                        List<VisitingDetector> list = mSuperClassDetectors.get(fqcn);
-                        if (list != null) {
-                            for (VisitingDetector v : list) {
-                                v.getJavaScanner().checkClass(mContext, null, anonymous,
-                                        resolvedClass);
-                            }
+                    List<VisitingDetector> list = mSuperClassDetectors.get(cls.getName());
+                    if (list != null) {
+                        for (VisitingDetector v : list) {
+                            v.getJavaScanner().checkClass(mContext, null, anonymous,
+                                    resolvedClass);
                         }
                     }
 
@@ -1331,7 +1371,7 @@
                             ResolvedNode resolved = mContext.resolve(node);
                             if (resolved instanceof ResolvedMethod) {
                                 ResolvedMethod method = (ResolvedMethod) resolved;
-                                String type = method.getContainingClass().getSignature();
+                                String type = method.getContainingClass().getName();
                                 List<VisitingDetector> list = mConstructorDetectors.get(type);
                                 if (list != null) {
                                     for (VisitingDetector v : list) {
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java
index fcb4dfe..473c95e 100755
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java
@@ -28,6 +28,8 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.Variant;
 import com.android.ide.common.repository.ResourceVisibilityLookup;
 import com.android.ide.common.res2.AbstractResourceRepository;
 import com.android.ide.common.res2.ResourceItem;
@@ -87,15 +89,17 @@
      * By default this method returns a {@link DefaultConfiguration}.
      *
      * @param project the project to obtain a configuration for
+     * @param driver the current driver, if any
      * @return a configuration, never null.
      */
-    public Configuration getConfiguration(@NonNull Project project) {
+    @NonNull
+    public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
         return DefaultConfiguration.create(this, project, null);
     }
 
     /**
      * Report the given issue. This method will only be called if the configuration
-     * provided by {@link #getConfiguration(Project)} has reported the corresponding
+     * provided by {@link #getConfiguration(Project,LintDriver)} has reported the corresponding
      * issue as enabled and has not filtered out the issue with its
      * {@link Configuration#ignore(Context,Issue,Location,String)} method.
      * <p>
@@ -920,10 +924,41 @@
     @SuppressWarnings("MethodMayBeStatic") // Intentionally instance method so it can be overridden
     @NonNull
     public List<File> findRuleJars(@NonNull Project project) {
-        if (project.getDir().getPath().endsWith(DOT_AAR)) {
-            File lintJar = new File(project.getDir(), "lint.jar"); //$NON-NLS-1$
-            if (lintJar.exists()) {
-                return Collections.singletonList(lintJar);
+        if (project.isGradleProject()) {
+            if (project.isLibrary()) {
+                AndroidLibrary model = project.getGradleLibraryModel();
+                if (model != null) {
+                    File lintJar = model.getLintJar();
+                    if (lintJar.exists()) {
+                        return Collections.singletonList(lintJar);
+                    }
+                }
+            } else if (project.getSubset() != null) {
+                // Probably just analyzing a single file: we still want to look for custom
+                // rules applicable to the file
+                List<File> rules = null;
+                final Variant variant = project.getCurrentVariant();
+                if (variant != null) {
+                    Collection<AndroidLibrary> libraries = variant.getMainArtifact()
+                      .getDependencies().getLibraries();
+                    for (AndroidLibrary library : libraries) {
+                        File lintJar = library.getLintJar();
+                        if (lintJar.exists()) {
+                            if (rules == null) {
+                                rules = Lists.newArrayListWithExpectedSize(4);
+                            }
+                            rules.add(lintJar);
+                        }
+                    }
+                    if (rules != null) {
+                        return rules;
+                    }
+                }
+            } else if (project.getDir().getPath().endsWith(DOT_AAR)) {
+                File lintJar = new File(project.getDir(), "lint.jar"); //$NON-NLS-1$
+                if (lintJar.exists()) {
+                    return Collections.singletonList(lintJar);
+                }
             }
         }
 
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java
index d248236..b30cdb8 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java
@@ -103,7 +103,6 @@
 import lombok.ast.AnnotationElement;
 import lombok.ast.AnnotationValue;
 import lombok.ast.ArrayInitializer;
-import lombok.ast.ClassDeclaration;
 import lombok.ast.ConstructorDeclaration;
 import lombok.ast.Expression;
 import lombok.ast.MethodDeclaration;
@@ -111,6 +110,7 @@
 import lombok.ast.Node;
 import lombok.ast.StrictListAccessor;
 import lombok.ast.StringLiteral;
+import lombok.ast.TypeDeclaration;
 import lombok.ast.TypeReference;
 import lombok.ast.VariableDefinition;
 
@@ -409,7 +409,7 @@
             return;
         }
 
-        registerCustomRules(projects);
+        registerCustomDetectors(projects);
 
         if (mScope == null) {
             mScope = Scope.infer(projects);
@@ -441,7 +441,21 @@
         fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null);
     }
 
-    private void registerCustomRules(Collection<Project> projects) {
+    @Nullable
+    private Set<Issue> myCustomIssues;
+
+    /**
+     * Returns true if the given issue is an issue that was loaded as a custom rule
+     * (e.g. a 3rd-party library provided the detector, it's not built in)
+     *
+     * @param issue the issue to be looked up
+     * @return true if this is a custom (non-builtin) check
+     */
+    public boolean isCustomIssue(@NonNull Issue issue) {
+        return myCustomIssues != null && myCustomIssues.contains(issue);
+    }
+
+    private void registerCustomDetectors(Collection<Project> projects) {
         // Look at the various projects, and if any of them provide a custom
         // lint jar, "add" them (this will replace the issue registry with
         // a CompositeIssueRegistry containing the original issue registry
@@ -449,6 +463,9 @@
         Set<File> jarFiles = Sets.newHashSet();
         for (Project project : projects) {
             jarFiles.addAll(mClient.findRuleJars(project));
+            for (Project library : project.getAllLibraries()) {
+                jarFiles.addAll(mClient.findRuleJars(library));
+            }
         }
 
         jarFiles.addAll(mClient.findGlobalRuleJars());
@@ -458,7 +475,12 @@
             registries.add(mRegistry);
             for (File jarFile : jarFiles) {
                 try {
-                    registries.add(JarFileIssueRegistry.get(mClient, jarFile));
+                    IssueRegistry registry = JarFileIssueRegistry.get(mClient, jarFile);
+                    if (myCustomIssues == null) {
+                        myCustomIssues = Sets.newHashSet();
+                    }
+                    myCustomIssues.addAll(registry.getIssues());
+                    registries.add(registry);
                 } catch (Throwable e) {
                     mClient.log(e, "Could not load custom rule jar file %1$s", jarFile);
                 }
@@ -545,7 +567,7 @@
         // and simultaneously build up the detectorToScope map which tracks
         // the scopes each detector is affected by (this is used to populate
         // the mScopeDetectors map which is used during iteration).
-        Configuration configuration = project.getConfiguration();
+        Configuration configuration = project.getConfiguration(this);
         for (Detector detector : detectors) {
             Class<? extends Detector> detectorClass = detector.getClass();
             Collection<Issue> detectorIssues = issueMap.get(detectorClass);
@@ -602,7 +624,7 @@
         mCurrentFolderType = null;
         mCurrentVisitor = null;
 
-        Configuration configuration = project.getConfiguration();
+        Configuration configuration = project.getConfiguration(this);
         mScopeDetectors = new EnumMap<Scope, List<Detector>>(Scope.class);
         mApplicableDetectors = mRegistry.createDetectors(mClient, configuration,
                 mScope, mScopeDetectors);
@@ -1244,7 +1266,7 @@
             Location location = Location.create(project.getDir());
             mClient.report(new Context(this, project, main, project.getDir()),
                     IssueRegistry.LINT_ERROR,
-                    project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
+                    project.getConfiguration(this).getSeverity(IssueRegistry.LINT_ERROR),
                     location, message, TextFormat.RAW);
             classEntries = Collections.emptyList();
         } else {
@@ -1848,11 +1870,11 @@
 
         @Override
         @NonNull
-        public Configuration getConfiguration(@NonNull Project project) {
-            return mDelegate.getConfiguration(project);
+        public Configuration getConfiguration(@NonNull Project project,
+          @Nullable LintDriver driver) {
+            return mDelegate.getConfiguration(project, driver);
         }
 
-
         @Override
         public void log(@NonNull Severity severity, @Nullable Throwable exception,
                 @Nullable String format, @Nullable Object... args) {
@@ -2374,9 +2396,9 @@
                 if (isSuppressed(issue, declaration.astModifiers())) {
                     return true;
                 }
-            } else if (type == ClassDeclaration.class) {
-                // Class
-                ClassDeclaration declaration = (ClassDeclaration) scope;
+            } else if (TypeDeclaration.class.isAssignableFrom(type)) {
+                // Class, annotation, enum, interface
+                TypeDeclaration declaration = (TypeDeclaration) scope;
                 if (isSuppressed(issue, declaration.astModifiers())) {
                     return true;
                 }
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
index 3a0685a..4550cc5 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
@@ -419,6 +419,9 @@
             }
         }
 
+        // TODO: Check for MethodInvocation and perform some common operations -
+        // Math.* methods, String utility methods like notNullize, etc
+
         return null;
     }
 
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java
index 3a04d71..6eca816 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java
@@ -103,7 +103,7 @@
         mDriver = driver;
         mProject = project;
         mMainProject = main;
-        mConfiguration = project.getConfiguration();
+        mConfiguration = project.getConfiguration(driver);
     }
 
     /**
@@ -263,7 +263,7 @@
         if (location != null && location.getFile() != null) {
             Project project = mDriver.findProjectFor(location.getFile());
             if (project != null) {
-                configuration = project.getConfiguration();
+                configuration = project.getConfiguration(mDriver);
             }
         }
 
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java
index 5000c26..58f66be 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/JavaContext.java
@@ -25,12 +25,15 @@
 import com.android.tools.lint.client.api.JavaParser;
 import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
 import com.android.tools.lint.client.api.LintDriver;
+import com.google.common.collect.Iterators;
 
 import java.io.File;
 import java.util.Iterator;
 
 import lombok.ast.ClassDeclaration;
 import lombok.ast.ConstructorDeclaration;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.EnumConstant;
 import lombok.ast.Expression;
 import lombok.ast.MethodDeclaration;
 import lombok.ast.MethodInvocation;
@@ -228,6 +231,45 @@
         return mParser.getType(this, node);
     }
 
+    @Nullable
+    public static String getMethodName(@NonNull Node call) {
+        if (call instanceof MethodInvocation) {
+            return ((MethodInvocation)call).astName().astValue();
+        } else if (call instanceof ConstructorInvocation) {
+            return ((ConstructorInvocation)call).astTypeReference().getTypeName();
+        } else if (call instanceof EnumConstant) {
+            return ((EnumConstant)call).astName().astValue();
+        } else {
+            return null;
+        }
+    }
+
+    @NonNull
+    public static Iterator<Expression> getParameters(@NonNull Node call) {
+        if (call instanceof MethodInvocation) {
+            return ((MethodInvocation) call).astArguments().iterator();
+        } else if (call instanceof ConstructorInvocation) {
+            return ((ConstructorInvocation) call).astArguments().iterator();
+        } else if (call instanceof EnumConstant) {
+            return ((EnumConstant) call).astArguments().iterator();
+        } else {
+            return Iterators.emptyIterator();
+        }
+    }
+
+    @Nullable
+    public static Node getParameter(@NonNull Node call, int parameter) {
+        Iterator<Expression> iterator = getParameters(call);
+
+        for (int i = 0; i < parameter - 1; i++) {
+            if (!iterator.hasNext()) {
+                return null;
+            }
+            iterator.next();
+        }
+        return iterator.hasNext() ? iterator.next() : null;
+    }
+
     /**
      * Returns true if the given method invocation node corresponds to a call on a
      * {@code android.content.Context}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java
index fb9d42e..284e41b 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java
@@ -18,6 +18,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.ide.common.res2.ResourceFile;
 import com.android.ide.common.res2.ResourceItem;
 import com.google.common.annotations.Beta;
@@ -189,6 +190,31 @@
     }
 
     /**
+     * Creates a new location for the given file and SourcePosition.
+     *
+     * @param file the file containing the positions
+     * @param position the source position
+     * @return a new location
+     */
+    @NonNull
+    public static Location create(
+            @NonNull File file,
+            @NonNull SourcePosition position) {
+        if (position.equals(SourcePosition.UNKNOWN)) {
+            return new Location(file, null /*start*/, null /*end*/);
+        }
+        return new Location(file,
+                new DefaultPosition(
+                        position.getStartLine(),
+                        position.getStartColumn(),
+                        position.getStartOffset()),
+                new DefaultPosition(
+                        position.getEndLine(),
+                        position.getEndColumn(),
+                        position.getEndOffset()));
+    }
+
+    /**
      * Creates a new location for the given file and starting and ending
      * positions.
      *
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java
index 57476ed..3831284 100755
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java
@@ -55,6 +55,7 @@
 import com.android.tools.lint.client.api.CircularDependencyException;
 import com.android.tools.lint.client.api.Configuration;
 import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.client.api.LintDriver;
 import com.android.tools.lint.client.api.SdkInfo;
 import com.google.common.annotations.Beta;
 import com.google.common.base.CharMatcher;
@@ -564,12 +565,13 @@
     /**
      * Gets the configuration associated with this project
      *
+     * @param driver the current driver, if any
      * @return the configuration associated with this project
      */
     @NonNull
-    public Configuration getConfiguration() {
+    public Configuration getConfiguration(@Nullable LintDriver driver) {
         if (mConfiguration == null) {
-            mConfiguration = mClient.getConfiguration(this);
+            mConfiguration = mClient.getConfiguration(this, driver);
         }
         return mConfiguration;
     }
@@ -1353,4 +1355,24 @@
 
         return mResourceVisibility;
     }
+
+    /**
+     * Returns the associated client
+     *
+     * @return the client
+     */
+    @NonNull
+    public LintClient getClient() {
+        return mClient;
+    }
+
+    /**
+     * Returns the compile target to use for this project
+     *
+     * @return the compile target to use to build this project
+     */
+    @Nullable
+    public IAndroidTarget getCompileTarget() {
+        return mClient.getCompileTarget(this);
+    }
 }
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java
index ed1bd3c..a66b326 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java
@@ -203,7 +203,7 @@
         int n = text.length();
         for (int i = 0; i < n; i++) {
             char c = text.charAt(i);
-            if ((c == '*' || c == '`' && i < n - 1)) {
+            if ((c == '*' || c == '`') && i < n - 1) {
                 // Scout ahead for range end
                 if (!Character.isLetterOrDigit(prev)
                         && !Character.isWhitespace(text.charAt(i + 1))) {
diff --git a/lint/libs/lint-checks/build.gradle b/lint/libs/lint-checks/build.gradle
index 568c721..b71cb35 100644
--- a/lint/libs/lint-checks/build.gradle
+++ b/lint/libs/lint-checks/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools.lint'
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
index 66ece7e..9cd147f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
@@ -16,35 +16,49 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.SdkConstants.ATTR_VALUE;
 import static com.android.SdkConstants.FQCN_SUPPRESS_LINT;
+import static com.android.SdkConstants.INT_DEF_ANNOTATION;
 import static com.android.SdkConstants.SUPPRESS_LINT;
+import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
+import static com.android.tools.lint.detector.api.JavaContext.findSurroundingClass;
+import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Location;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.Speed;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import java.io.File;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import lombok.ast.Annotation;
+import lombok.ast.AnnotationDeclaration;
 import lombok.ast.AnnotationElement;
 import lombok.ast.AnnotationValue;
 import lombok.ast.ArrayInitializer;
 import lombok.ast.AstVisitor;
 import lombok.ast.Block;
+import lombok.ast.ClassDeclaration;
 import lombok.ast.ConstructorDeclaration;
 import lombok.ast.Expression;
 import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.IntegralLiteral;
 import lombok.ast.MethodDeclaration;
 import lombok.ast.Modifiers;
 import lombok.ast.Node;
@@ -52,15 +66,23 @@
 import lombok.ast.StrictListAccessor;
 import lombok.ast.StringLiteral;
 import lombok.ast.TypeBody;
+import lombok.ast.TypeMember;
+import lombok.ast.VariableDeclaration;
 import lombok.ast.VariableDefinition;
 import lombok.ast.VariableDefinitionEntry;
+import lombok.ast.VariableReference;
 
 /**
  * Checks annotations to make sure they are valid
  */
 public class AnnotationDetector extends Detector implements Detector.JavaScanner {
+
+    public static final Implementation IMPLEMENTATION = new Implementation(
+              AnnotationDetector.class,
+              Scope.JAVA_FILE_SCOPE);
+
     /** Placing SuppressLint on a local variable doesn't work for class-file based checks */
-    public static final Issue ISSUE = Issue.create(
+    public static final Issue INSIDE_METHOD = Issue.create(
             "LocalSuppress", //$NON-NLS-1$
             "@SuppressLint on invalid element",
 
@@ -75,9 +97,42 @@
             Category.CORRECTNESS,
             3,
             Severity.ERROR,
-            new Implementation(
-                    AnnotationDetector.class,
-                    Scope.JAVA_FILE_SCOPE));
+            IMPLEMENTATION);
+
+    /** IntDef annotations should be unique */
+    public static final Issue UNIQUE = Issue.create(
+            "UniqueConstants", //$NON-NLS-1$
+            "Overlapping Enumeration Constants",
+
+            "The `@IntDef` annotation allows you to " +
+            "create a light-weight \"enum\" or type definition. However, it's possible to " +
+            "accidentally specify the same value for two or more of the values, which can " +
+            "lead to hard-to-detect bugs. This check looks for this scenario and flags any " +
+            "repeated constants.\n" +
+            "\n" +
+            "In some cases, the repeated constant is intentional (for example, renaming a " +
+            "constant to a more intuitive name, and leaving the old name in place for " +
+            "compatibility purposes.)  In that case, simply suppress this check by adding a " +
+            "`@SuppressLint(\"UniqueConstants\")` annotation.",
+
+            Category.CORRECTNESS,
+            3,
+            Severity.ERROR,
+            IMPLEMENTATION);
+
+    /** Flags should typically be specified as bit shifts */
+    public static final Issue FLAG_STYLE = Issue.create(
+            "ShiftFlags", //$NON-NLS-1$
+            "Dangerous Flag Constant Declaration",
+
+            "When defining multiple constants for use in flags, the recommended style is " +
+            "to use the form `1 << 2`, `1 << 3`, `1 << 4` and so on to ensure that the " +
+            "constants are unique and non-overlapping.",
+
+            Category.CORRECTNESS,
+            3,
+            Severity.WARNING,
+            IMPLEMENTATION);
 
     /** Constructs a new {@link AnnotationDetector} check */
     public AnnotationDetector() {
@@ -139,9 +194,7 @@
                                 if (expressions == null) {
                                     continue;
                                 }
-                                Iterator<Expression> arrayIterator = expressions.iterator();
-                                while (arrayIterator.hasNext()) {
-                                    Expression arrayElement = arrayIterator.next();
+                                for (Expression arrayElement : expressions) {
                                     if (arrayElement instanceof StringLiteral) {
                                         String id = ((StringLiteral) arrayElement).astValue();
                                         if (!checkId(node, id)) {
@@ -153,11 +206,165 @@
                         }
                     }
                 }
+            } else if (INT_DEF_ANNOTATION.equals(type) || "IntDef".equals(type)) {
+                // Make sure that all the constants are unique
+                ResolvedNode resolved = mContext.resolve(node);
+                if (resolved instanceof ResolvedAnnotation) {
+                    ensureUniqueValues(((ResolvedAnnotation)resolved), node);
+                }
             }
 
             return super.visitAnnotation(node);
         }
 
+        private void ensureUniqueValues(@NonNull ResolvedAnnotation annotation,
+                @NonNull Annotation node) {
+            Object allowed = annotation.getValue();
+            if (allowed instanceof Object[]) {
+                Object[] allowedValues = (Object[]) allowed;
+                Map<Number,Integer> valueToIndex =
+                        Maps.newHashMapWithExpectedSize(allowedValues.length);
+
+                List<Node> constants = null;
+                for (AnnotationElement element : node.astElements()) {
+                    if (element.astName() == null
+                            || ATTR_VALUE.equals(element.astName().astValue())) {
+                        AnnotationValue value = element.astValue();
+                        if (value instanceof ArrayInitializer) {
+                            ArrayInitializer initializer = (ArrayInitializer)value;
+                            constants = Lists.newArrayListWithExpectedSize(allowedValues.length);
+                            for (Expression expression : initializer.astExpressions()) {
+                                constants.add(expression);
+                            }
+                        }
+                        break;
+                    }
+                }
+                if (constants != null) {
+                    if (constants.size() != allowedValues.length) {
+                        constants = null;
+                    } else {
+                        boolean flag = annotation.getValue(TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
+                        if (flag) {
+                            ensureUsingFlagStyle(constants);
+                        }
+                    }
+                }
+
+                for (int index = 0; index < allowedValues.length; index++) {
+                    Object o = allowedValues[index];
+                    if (o instanceof Number) {
+                        Number number = (Number)o;
+                        if (valueToIndex.containsKey(number)) {
+                            @SuppressWarnings("UnnecessaryLocalVariable")
+                            Number repeatedValue = number;
+
+                            Location location;
+                            String message;
+                            if (constants != null) {
+                                Node constant = constants.get(index);
+                                int prevIndex = valueToIndex.get(number);
+                                Node prevConstant = constants.get(prevIndex);
+                                message = String.format(
+                                        "Constants `%1$s` and `%2$s` specify the same exact "
+                                                + "value (%3$s); this is usually a cut & paste or "
+                                                + "merge error",
+                                        constant.toString(), prevConstant.toString(),
+                                        repeatedValue.toString());
+                                location = mContext.getLocation(constant);
+                                Location secondary = mContext.getLocation(prevConstant);
+                                secondary.setMessage("Previous same value");
+                                location.setSecondary(secondary);
+                            } else {
+                                message = String.format(
+                                        "More than one constant specifies the same exact "
+                                                + "value (%1$s); this is usually a cut & paste or"
+                                                + "merge error",
+                                        repeatedValue.toString());
+                                location = mContext.getLocation(node);
+                            }
+                            Node scope = getAnnotationScope(node);
+                            mContext.report(UNIQUE, scope, location, message);
+                            break;
+                        }
+                        valueToIndex.put(number, index);
+                    }
+                }
+            }
+        }
+
+        @NonNull
+        private static List<VariableDefinitionEntry> findDeclarations(
+                @Nullable ClassDeclaration cls,
+                @NonNull List<VariableReference> references) {
+            if (cls == null) {
+                return Collections.emptyList();
+            }
+            Map<String, VariableReference> referenceMap = Maps.newHashMap();
+            for (VariableReference reference : references) {
+                String name = reference.astIdentifier().astValue();
+                referenceMap.put(name, reference);
+            }
+            List<VariableDefinitionEntry> declarations = Lists.newArrayList();
+            for (TypeMember member : cls.astBody().astMembers()) {
+                if (member instanceof VariableDeclaration) {
+                    VariableDeclaration declaration = (VariableDeclaration)member;
+                    VariableDefinitionEntry field = declaration.astDefinition().astVariables()
+                            .first();
+                    String name = field.astName().astValue();
+                    if (referenceMap.containsKey(name)) {
+                        // TODO: When the Lombok ECJ bridge properly handles resolving variable
+                        // definitions into ECJ bindings this code should check that
+                        // mContext.resolve(field) == mContext.resolve(referenceMap.get(name)) !
+                        declarations.add(field);
+                    }
+                }
+            }
+
+            return declarations;
+        }
+
+        private void ensureUsingFlagStyle(@NonNull List<Node> constants) {
+            if (constants.size() < 3) {
+                return;
+            }
+
+            List<VariableReference> references =
+                    Lists.newArrayListWithExpectedSize(constants.size());
+            for (Node constant : constants) {
+                if (constant instanceof VariableReference) {
+                    references.add((VariableReference) constant);
+                }
+            }
+            List<VariableDefinitionEntry> entries = findDeclarations(
+                    findSurroundingClass(constants.get(0)), references);
+            for (VariableDefinitionEntry entry : entries) {
+                Expression declaration = entry.astInitializer();
+                if (declaration == null) {
+                    continue;
+                }
+                if (declaration instanceof IntegralLiteral) {
+                    IntegralLiteral literal = (IntegralLiteral) declaration;
+                    // Allow -1, 0 and 1. You can write 1 as "1 << 0" but IntelliJ for
+                    // example warns that that's a redundant shift.
+                    long value = literal.astLongValue();
+                    if (Math.abs(value) <= 1) {
+                        continue;
+                    }
+                    // Only warn if we're setting a specific bit
+                    if (Long.bitCount(value) != 1) {
+                        continue;
+                    }
+                    int shift = Long.numberOfTrailingZeros(value);
+                    String message = String.format(
+                            "Consider declaring this constant using 1 << %1$d instead",
+                            shift);
+                    mContext.report(FLAG_STYLE, declaration, mContext.getLocation(declaration),
+                            message);
+                }
+            }
+        }
+
         private boolean checkId(Annotation node, String id) {
             IssueRegistry registry = mContext.getDriver().getRegistry();
             Issue issue = registry.getIssue(id);
@@ -193,7 +400,8 @@
 
                 // This issue doesn't have AST access: annotations are not
                 // available for local variables or parameters
-                mContext.report(ISSUE, node, mContext.getLocation(node), String.format(
+                Node scope = getAnnotationScope(node);
+                mContext.report(INSIDE_METHOD, scope, mContext.getLocation(node), String.format(
                     "The `@SuppressLint` annotation cannot be used on a local " +
                     "variable with the lint check '%1$s': move out to the " +
                     "surrounding method", id));
@@ -203,4 +411,21 @@
             return true;
         }
     }
+
+    /**
+     * Returns the node to use as the scope for the given annotation node.
+     * You can't annotate an annotation itself (with {@code @SuppressLint}), but
+     * you should be able to place an annotation next to it, as a sibling, to only
+     * suppress the error on this annotated element, not the whole surrounding class.
+     */
+    @NonNull
+    private static Node getAnnotationScope(@NonNull Annotation node) {
+        //
+        Node scope = getParentOfType(node,
+              AnnotationDeclaration.class, true);
+        if (scope == null) {
+            scope = node;
+        }
+        return scope;
+    }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
index 1af53ce..8219e9b 100755
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
@@ -1764,7 +1764,7 @@
                                 + "API level %1$d (current min is %2$d)", api, minSdk);
                         LintDriver driver = mContext.getDriver();
                         if (!driver.isSuppressed(mContext, UNSUPPORTED, node)) {
-                            mContext.report(UNSUPPORTED, location, message);
+                            mContext.report(UNSUPPORTED, node, location, message);
                         }
                     }
                 } else {
@@ -1792,7 +1792,7 @@
                                     api, minSdk, fqcn);
                                 LintDriver driver = mContext.getDriver();
                                 if (!driver.isSuppressed(mContext, UNSUPPORTED, typeReference)) {
-                                    mContext.report(UNSUPPORTED, location, message);
+                                    mContext.report(UNSUPPORTED, typeReference, location, message);
                                 }
                             }
                         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
new file mode 100644
index 0000000..1a8dd67
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_HOST;
+import static com.android.SdkConstants.ATTR_PATH;
+import static com.android.SdkConstants.ATTR_PATH_PATTERN;
+import static com.android.SdkConstants.ATTR_PATH_PREFIX;
+import static com.android.SdkConstants.ATTR_SCHEME;
+
+import static com.android.xml.AndroidManifest.ATTRIBUTE_MIME_TYPE;
+import static com.android.xml.AndroidManifest.ATTRIBUTE_NAME;
+import static com.android.xml.AndroidManifest.ATTRIBUTE_PORT;
+import static com.android.xml.AndroidManifest.NODE_ACTION;
+import static com.android.xml.AndroidManifest.NODE_CATEGORY;
+import static com.android.xml.AndroidManifest.NODE_DATA;
+import static com.android.xml.AndroidManifest.NODE_INTENT;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.res2.AbstractResourceRepository;
+import com.android.ide.common.res2.ResourceItem;
+import com.android.ide.common.resources.ResourceUrl;
+import com.android.resources.ResourceType;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.XmlContext;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Check if the usage of App Indexing is correct.
+ */
+public class AppIndexingApiDetector extends Detector implements Detector.XmlScanner {
+
+    private static final Implementation IMPLEMENTATION = new Implementation(
+            AppIndexingApiDetector.class, Scope.MANIFEST_SCOPE);
+
+    public static final Issue ISSUE_ERROR = Issue.create("AppIndexingError", //$NON-NLS-1$
+            "Wrong Usage of App Indexing",
+            "Ensures the app can correctly handle deep links and integrate with " +
+                    "App Indexing for Google search.",
+            Category.USABILITY, 5, Severity.ERROR, IMPLEMENTATION)
+            .addMoreInfo("https://g.co/AppIndexing");
+
+    public static final Issue ISSUE_WARNING = Issue.create("AppIndexingWarning", //$NON-NLS-1$
+            "Missing App Indexing Support",
+            "Ensures the app can correctly handle deep links and integrate with " +
+                    "App Indexing for Google search.",
+            Category.USABILITY, 5, Severity.WARNING, IMPLEMENTATION)
+            .addMoreInfo("https://g.co/AppIndexing");
+
+    private static final String[] PATH_ATTR_LIST = new String[]{ATTR_PATH_PREFIX, ATTR_PATH,
+            ATTR_PATH_PATTERN};
+
+    @Override
+    @Nullable
+    public Collection<String> getApplicableElements() {
+        return Collections.singletonList(NODE_INTENT);
+    }
+
+    @Override
+    public void visitElement(@NonNull XmlContext context, @NonNull Element intent) {
+        boolean actionView = hasActionView(intent);
+        boolean browsable = isBrowsable(intent);
+        boolean isHttp = false;
+        boolean hasScheme = false;
+        boolean hasHost = false;
+        boolean hasPort = false;
+        boolean hasPath = false;
+        boolean hasMimeType = false;
+        Element firstData = null;
+        NodeList children = intent.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(NODE_DATA)) {
+                Element data = (Element) child;
+                if (firstData == null) {
+                    firstData = data;
+                }
+                if (isHttpSchema(data)) {
+                    isHttp = true;
+                }
+                checkSingleData(context, data);
+
+                for (String name : PATH_ATTR_LIST) {
+                    if (data.hasAttributeNS(ANDROID_URI, name)) {
+                        hasPath = true;
+                    }
+                }
+
+                if (data.hasAttributeNS(ANDROID_URI, ATTR_SCHEME)) {
+                    hasScheme = true;
+                }
+
+                if (data.hasAttributeNS(ANDROID_URI, ATTR_HOST)) {
+                    hasHost = true;
+                }
+
+                if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_PORT)) {
+                    hasPort = true;
+                }
+
+                if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_MIME_TYPE)) {
+                    hasMimeType = true;
+                }
+            }
+        }
+
+        // In data field, a URL is consisted by
+        // <scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
+        // Each part of the URL should not have illegal character.
+        if ((hasPath || hasHost || hasPort) && !hasScheme) {
+            context.report(ISSUE_ERROR, firstData, context.getLocation(firstData),
+                    "android:scheme missing");
+        }
+
+        if ((hasPath || hasPort) && !hasHost) {
+            context.report(ISSUE_ERROR, firstData, context.getLocation(firstData),
+                    "android:host missing");
+        }
+
+        if (actionView && browsable) {
+            if (firstData == null) {
+                // If this activity is an ACTION_VIEW action with category BROWSABLE, but doesn't
+                // have data node, it may be a mistake and we will report error.
+                context.report(ISSUE_ERROR, intent, context.getLocation(intent),
+                        "Missing data node?");
+            } else if (!hasScheme && !hasMimeType) {
+                // If this activity is an action view, is browsable, but has neither a
+                // URL nor mimeType, it may be a mistake and we will report error.
+                context.report(ISSUE_ERROR, firstData, context.getLocation(firstData),
+                        "Missing URL for the intent filter?");
+            }
+        }
+
+        // If this activity is an ACTION_VIEW action, has a http URL but doesn't have
+        // BROWSABLE, it may be a mistake and and we will report warning.
+        if (actionView && isHttp && !browsable) {
+            context.report(ISSUE_WARNING, intent, context.getLocation(intent),
+                    "Activity supporting ACTION_VIEW is not set as BROWSABLE");
+        }
+    }
+
+    private static boolean hasActionView(Element intent) {
+        NodeList children = intent.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE &&
+                    child.getNodeName().equals(NODE_ACTION)) {
+                Element action = (Element) child;
+                if (action.hasAttributeNS(ANDROID_URI, ATTRIBUTE_NAME)) {
+                    Attr attr = action.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_NAME);
+                    if (attr.getValue().equals("android.intent.action.VIEW")) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private static boolean isBrowsable(Element intent) {
+        NodeList children = intent.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE &&
+                    child.getNodeName().equals(NODE_CATEGORY)) {
+                Element e = (Element) child;
+                if (e.hasAttributeNS(ANDROID_URI, ATTRIBUTE_NAME)) {
+                    Attr attr = e.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_NAME);
+                    if (attr.getNodeValue().equals("android.intent.category.BROWSABLE")) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private static boolean isHttpSchema(Element data) {
+        if (data.hasAttributeNS(ANDROID_URI, ATTR_SCHEME)) {
+            String value = data.getAttributeNodeNS(ANDROID_URI, ATTR_SCHEME).getValue();
+            if (value.equalsIgnoreCase("http") || value.equalsIgnoreCase("https")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static void checkSingleData(XmlContext context, Element data) {
+        // path, pathPrefix and pathPattern should starts with /.
+        for (String name : PATH_ATTR_LIST) {
+            if (data.hasAttributeNS(ANDROID_URI, name)) {
+                Attr attr = data.getAttributeNodeNS(ANDROID_URI, name);
+                String path = replaceUrlWithValue(context, attr.getValue());
+                if (!path.startsWith("/") && !path.startsWith(SdkConstants.PREFIX_RESOURCE_REF)) {
+                    context.report(ISSUE_ERROR, attr, context.getLocation(attr),
+                            "android:" + name + " attribute should start with '/', but it is : "
+                                    + path);
+                }
+            }
+        }
+
+        // port should be a legal number.
+        if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_PORT)) {
+            Attr attr = data.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_PORT);
+            try {
+                String port = replaceUrlWithValue(context, attr.getValue());
+                Integer.parseInt(port);
+            } catch (NumberFormatException e) {
+                context.report(ISSUE_ERROR, attr, context.getLocation(attr),
+                        "android:port is not a legal number");
+            }
+        }
+
+        // Each field should be non empty.
+        NamedNodeMap attrs = data.getAttributes();
+        for (int i = 0; i < attrs.getLength(); i++) {
+            Node item = attrs.item(i);
+            if (item.getNodeType() == Node.ATTRIBUTE_NODE) {
+                Attr attr = (Attr) attrs.item(i);
+                if (attr.getValue().isEmpty()) {
+                    context.report(ISSUE_ERROR, attr, context.getLocation(attr),
+                            attr.getName() + " cannot be empty");
+                }
+            }
+        }
+    }
+
+    private static String replaceUrlWithValue(@NonNull XmlContext context,
+            @NonNull String str) {
+        Project project = context.getProject();
+        LintClient client = context.getClient();
+        if (!client.supportsProjectResources()) {
+            return str;
+        }
+        ResourceUrl style = ResourceUrl.parse(str);
+        if (style == null || style.type != ResourceType.STRING || style.framework) {
+            return str;
+        }
+        AbstractResourceRepository resources = client.getProjectResources(project, true);
+        if (resources == null) {
+            return str;
+        }
+        List<ResourceItem> items = resources.getResourceItem(ResourceType.STRING, style.name);
+        if (items == null || items.isEmpty()) {
+            return str;
+        }
+        ResourceValue resourceValue = items.get(0).getResourceValue(false);
+        if (resourceValue == null) {
+            return str;
+        }
+        return resourceValue.getValue() == null ? str : resourceValue.getValue();
+    }
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index 0a352b3..4e50397 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -30,7 +30,7 @@
 /** Registry which provides a list of checks to be performed on an Android project */
 public class BuiltinIssueRegistry extends IssueRegistry {
     private static final List<Issue> sIssues;
-    static final int INITIAL_CAPACITY = 215;
+    static final int INITIAL_CAPACITY = 222;
 
     static {
         List<Issue> issues = new ArrayList<Issue>(INITIAL_CAPACITY);
@@ -39,13 +39,17 @@
         issues.add(AddJavascriptInterfaceDetector.ISSUE);
         issues.add(AlarmDetector.ISSUE);
         issues.add(AlwaysShowActionDetector.ISSUE);
-        issues.add(AnnotationDetector.ISSUE);
+        issues.add(AnnotationDetector.FLAG_STYLE);
+        issues.add(AnnotationDetector.INSIDE_METHOD);
+        issues.add(AnnotationDetector.UNIQUE);
         issues.add(ApiDetector.INLINED);
         issues.add(ApiDetector.OVERRIDE);
         issues.add(ApiDetector.UNSUPPORTED);
         issues.add(ApiDetector.UNUSED);
         issues.add(AppCompatCallDetector.ISSUE);
         issues.add(AppCompatResourceDetector.ISSUE);
+        issues.add(AppIndexingApiDetector.ISSUE_ERROR);
+        issues.add(AppIndexingApiDetector.ISSUE_WARNING);
         issues.add(ArraySizeDetector.INCONSISTENT);
         issues.add(AssertDetector.ISSUE);
         issues.add(ButtonDetector.BACK_BUTTON);
@@ -74,6 +78,7 @@
         issues.add(DuplicateResourceDetector.TYPE_MISMATCH);
         issues.add(ExtraTextDetector.ISSUE);
         issues.add(FieldGetterDetector.ISSUE);
+        issues.add(FullBackupContentDetector.ISSUE);
         issues.add(FragmentDetector.ISSUE);
         issues.add(GetSignaturesDetector.ISSUE);
         issues.add(GradleDetector.COMPATIBILITY);
@@ -211,8 +216,10 @@
         issues.add(SupportAnnotationDetector.CHECK_PERMISSION);
         issues.add(SupportAnnotationDetector.CHECK_RESULT);
         issues.add(SupportAnnotationDetector.COLOR_USAGE);
+        issues.add(SupportAnnotationDetector.MISSING_PERMISSION);
         issues.add(SupportAnnotationDetector.RANGE);
         issues.add(SupportAnnotationDetector.RESOURCE_TYPE);
+        issues.add(SupportAnnotationDetector.THREAD);
         issues.add(SupportAnnotationDetector.TYPE_DEF);
         issues.add(SystemPermissionsDetector.ISSUE);
         issues.add(TextFieldDetector.ISSUE);
@@ -273,13 +280,13 @@
         } else {
             int initialSize = 12;
             if (scope.contains(Scope.RESOURCE_FILE)) {
-                initialSize += 70;
+                initialSize += 75;
             } else if (scope.contains(Scope.ALL_RESOURCE_FILES)) {
                 initialSize += 10;
             }
 
             if (scope.contains(Scope.JAVA_FILE)) {
-                initialSize += 48;
+                initialSize += 55;
             } else if (scope.contains(Scope.CLASS_FILE)) {
                 initialSize += 15;
             } else if (scope.contains(Scope.MANIFEST)) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java
index 68484af..568293d 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java
@@ -281,7 +281,7 @@
                                     if (!label.equals(CANCEL_LABEL)
                                             && LintUtils.isEnglishResource(context, true)
                                             && context.isEnabled(CASE)) {
-                                        assert label.equalsIgnoreCase(CANCEL_LABEL);
+                                        assert label.trim().equalsIgnoreCase(CANCEL_LABEL) : label;
                                         context.report(CASE, child, context.getLocation(child),
                                             String.format(
                                             "The standard Android way to capitalize %1$s " +
@@ -298,7 +298,7 @@
                                     if (!label.equals(OK_LABEL)
                                             && LintUtils.isEnglishResource(context, true)
                                             && context.isEnabled(CASE)) {
-                                        assert text.trim().equalsIgnoreCase(OK_LABEL) : text;
+                                        assert label.trim().equalsIgnoreCase(OK_LABEL) : label;
                                         context.report(CASE, child, context.getLocation(child),
                                             String.format(
                                             "The standard Android way to capitalize %1$s " +
@@ -380,7 +380,7 @@
                             isYes ? YES_LABEL : NO_LABEL,
                             isYes ? ANDROID_OK_RESOURCE : ANDROID_CANCEL_RESOURCE,
                             isYes ? YES_LABEL : NO_LABEL);
-                    context.report(CASE, element, location, message, null);
+                    context.report(CASE, element, location, message);
                 }
             }
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
index 36ae3c6..6dc6e67 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
@@ -18,10 +18,11 @@
 
 import static com.android.SdkConstants.CLASS_VIEW;
 import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.filterRelevantAnnotations;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
 import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
 import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.detector.api.Category;
@@ -68,7 +69,7 @@
 
             Category.CORRECTNESS,
             9,
-            Severity.WARNING,
+            Severity.ERROR,
             IMPLEMENTATION);
 
     /** Constructs a new {@link CallSuperDetector} check */
@@ -157,27 +158,24 @@
         }
 
         // Look up annotations metadata
-        while (true) {
-            ResolvedMethod superMethod = method.getSuperMethod();
-            if (superMethod == null) {
-                return null;
-            }
-
-            Iterable<JavaParser.ResolvedAnnotation> annotations = superMethod.getAnnotations();
-            for (JavaParser.ResolvedAnnotation annotation : annotations) {
-                annotation = SupportAnnotationDetector.getRelevantAnnotation(annotation);
-                if (annotation != null) {
-                    String signature = annotation.getSignature();
-                    if (CALL_SUPER_ANNOTATION.equals(signature)) {
-                        return superMethod;
-                    } else if (signature.endsWith(".OverrideMustInvoke")) {
-                        // Handle findbugs annotation on the fly too
-                        return superMethod;
-                    }
+        ResolvedMethod directSuper = method.getSuperMethod();
+        ResolvedMethod superMethod = directSuper;
+        while (superMethod != null) {
+            Iterable<ResolvedAnnotation> annotations = superMethod.getAnnotations();
+            annotations = filterRelevantAnnotations(annotations);
+            for (ResolvedAnnotation annotation : annotations) {
+                String signature = annotation.getSignature();
+                if (CALL_SUPER_ANNOTATION.equals(signature)) {
+                    return directSuper;
+                } else if (signature.endsWith(".OverrideMustInvoke")) {
+                    // Handle findbugs annotation on the fly too
+                    return directSuper;
                 }
             }
-            method = superMethod;
+            superMethod = superMethod.getSuperMethod();
         }
+
+        return null;
     }
 
     /** Visits a method and determines whether the method calls its super method */
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
index 24b174e..f16296f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
@@ -17,6 +17,7 @@
 package com.android.tools.lint.checks;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
@@ -131,7 +132,7 @@
                         if (end == -1) {
                             end = n;
                         }
-                        checkComment(context, source, 0, start, end);
+                        checkComment(context, null, source, 0, start, end);
                     } else if (next == '*') {
                         // Block comment
                         int start = i + 2;
@@ -139,7 +140,7 @@
                         if (end == -1) {
                             end = n;
                         }
-                        checkComment(context, source, 0, start, end);
+                        checkComment(context, null, source, 0, start, end);
                     }
                 }
             }
@@ -157,13 +158,14 @@
         @Override
         public boolean visitComment(Comment node) {
             String contents = node.astContent();
-            checkComment(mContext, contents, node.getPosition().getStart(), 0, contents.length());
+            checkComment(mContext, node, contents, node.getPosition().getStart(), 0, contents.length());
             return super.visitComment(node);
         }
     }
 
     private static void checkComment(
-            @NonNull Context context,
+            @NonNull JavaContext context,
+            @Nullable Comment node,
             @NonNull String source,
             int offset,
             int start,
@@ -178,7 +180,7 @@
                             0, ESCAPE_STRING.length())) {
                         Location location = Location.create(context.file, source,
                                 offset + i - 1, offset + i - 1 + ESCAPE_STRING.length());
-                        context.report(EASTER_EGG, location,
+                        context.report(EASTER_EGG, node, location,
                                 "Code might be hidden here; found unicode escape sequence " +
                                 "which is interpreted as comment end, compiled code follows");
                     }
@@ -190,7 +192,7 @@
                 // TODO: Only flag this issue in release mode??
                 Location location = Location.create(context.file, source,
                         offset + i - 1, offset + i - 1 + STOPSHIP_COMMENT.length());
-                context.report(STOP_SHIP, location,
+                context.report(STOP_SHIP, node, location,
                         "`STOPSHIP` comment found; points to code which must be fixed prior " +
                         "to release");
             }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
index dd5bf77..1ad7853 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
@@ -18,9 +18,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
 import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
 import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Detector;
@@ -38,8 +36,6 @@
 
 import lombok.ast.AstVisitor;
 import lombok.ast.ConstructorInvocation;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
 
 /**
  * Checks for errors related to Date Formats
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
index ab06867..c7271fc 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
@@ -25,6 +25,8 @@
 import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
 import static com.android.SdkConstants.ATTR_PACKAGE;
 import static com.android.SdkConstants.ATTR_STYLE;
+import static com.android.SdkConstants.AUTO_URI;
+import static com.android.SdkConstants.TAG_LAYOUT;
 import static com.android.SdkConstants.TOOLS_URI;
 import static com.android.SdkConstants.VIEW_TAG;
 import static com.android.resources.ResourceFolderType.ANIM;
@@ -130,6 +132,13 @@
             Element element = attribute.getOwnerElement();
             if (isCustomView(element) && context.getResourceFolderType() != null) {
                 return;
+            } else if (context.getResourceFolderType() == ResourceFolderType.LAYOUT) {
+                // Data binding: These look like Android framework views but
+                // are data binding directives not in the Android namespace
+                Element root = element.getOwnerDocument().getDocumentElement();
+                if (TAG_LAYOUT.equals(root.getTagName())) {
+                    return;
+                }
             }
 
             if (name.indexOf(':') != -1) {
@@ -157,6 +166,15 @@
                 // ....&& !attribute.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)
                 && attribute.getOwnerElement().getParentNode().getNodeType() == Node.ELEMENT_NODE
                 && !isCustomView((Element) attribute.getOwnerElement().getParentNode())) {
+            if (context.getResourceFolderType() == ResourceFolderType.LAYOUT
+                    && AUTO_URI.equals(uri)) {
+                // Data binding: Can add attributes like onClickListener to buttons etc.
+                Element root = attribute.getOwnerDocument().getDocumentElement();
+                if (TAG_LAYOUT.equals(root.getTagName())) {
+                    return;
+                }
+            }
+
             context.report(MISSING_NAMESPACE, attribute,
                     context.getLocation(attribute),
                     String.format("Unexpected namespace prefix \"%1$s\" found for tag `%2$s`",
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
new file mode 100644
index 0000000..3b05589
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.ResourceXmlDetector;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.XmlContext;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Check which makes sure that a full-backup-content descriptor file is valid and logical
+ */
+public class FullBackupContentDetector extends ResourceXmlDetector implements JavaScanner {
+    /**
+     * Validation of {@code <full-backup-content>} XML elements
+     */
+    public static final Issue ISSUE = Issue.create(
+            "FullBackupContent", //$NON-NLS-1$
+            "Valid Full Backup Content File",
+
+            "Ensures that a `<full-backup-content>` file, which is pointed to by a " +
+            "`android:fullBackupContent attribute` in the manifest file, is valid",
+
+            Category.CORRECTNESS,
+            5,
+            Severity.FATAL,
+            new Implementation(
+                    FullBackupContentDetector.class,
+                    Scope.RESOURCE_FILE_SCOPE));
+
+    @SuppressWarnings("SpellCheckingInspection")
+    private static final String DOMAIN_SHARED_PREF = "sharedpref";
+    private static final String DOMAIN_ROOT = "root";
+    private static final String DOMAIN_FILE = "file";
+    private static final String DOMAIN_DATABASE = "database";
+    private static final String DOMAIN_EXTERNAL = "external";
+    private static final String TAG_EXCLUDE = "exclude";
+    private static final String TAG_INCLUDE = "include";
+    private static final String TAG_FULL_BACKUP_CONTENT = "full-backup-content";
+    private static final String ATTR_PATH = "path";
+    private static final String ATTR_DOMAIN = "domain";
+
+    /**
+     * Constructs a new {@link FullBackupContentDetector}
+     */
+    public FullBackupContentDetector() {
+    }
+
+    @Override
+    public boolean appliesTo(@NonNull ResourceFolderType folderType) {
+        return folderType == ResourceFolderType.XML;
+    }
+
+    @Override
+    public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
+        Element root = document.getDocumentElement();
+        if (root == null) {
+            return;
+        }
+        if (!TAG_FULL_BACKUP_CONTENT.equals(root.getTagName())) {
+            return;
+        }
+
+        List<Element> includes = Lists.newArrayList();
+        List<Element> excludes = Lists.newArrayList();
+        NodeList children = root.getChildNodes();
+        for (int i = 0, n = children.getLength(); i < n; i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element element = (Element) child;
+                String tag = element.getTagName();
+                if (TAG_INCLUDE.equals(tag)) {
+                    includes.add(element);
+                } else if (TAG_EXCLUDE.equals(tag)) {
+                    excludes.add(element);
+                } else {
+                    // See FullBackup#validateInnerTagContents
+                    context.report(ISSUE, element, context.getNameLocation(element),
+                            String.format("Unexpected element `<%1$s>`", tag));
+                }
+            }
+        }
+
+        Multimap<String, String> includePaths = ArrayListMultimap.create(includes.size(), 4);
+        for (Element include : includes) {
+            String domain = validateDomain(context, include);
+            String path = validatePath(context, include);
+            if (domain == null) {
+                continue;
+            }
+            includePaths.put(domain, path);
+        }
+
+        for (Element exclude : excludes) {
+            String excludePath = validatePath(context, exclude);
+            if (excludePath.isEmpty()) {
+                continue;
+            }
+            String domain = validateDomain(context, exclude);
+            if (domain == null) {
+                continue;
+            }
+            if (includePaths.isEmpty()) {
+                // There is no <include> anywhere: that means that everything
+                // is considered included and there's no potential prefix mismatch
+                continue;
+            }
+
+            boolean hasPrefix = false;
+            Collection<String> included = includePaths.get(domain);
+            if (included == null) {
+                continue;
+            }
+            for (String includePath : included) {
+                if (excludePath.startsWith(includePath)) {
+                    if (excludePath.equals(includePath)) {
+                        Attr pathNode = exclude.getAttributeNode(ATTR_PATH);
+                        assert pathNode != null;
+                        Location location = context.getValueLocation(pathNode);
+                        // Find corresponding include path so we can link to it in the
+                        // chained location list
+                        for (Element include : includes) {
+                            Attr includePathNode = include.getAttributeNode(ATTR_PATH);
+                            String includeDomain = include.getAttribute(ATTR_DOMAIN);
+                            if (includePathNode != null
+                                    && excludePath.equals(includePathNode.getValue())
+                                    && domain.equals(includeDomain)) {
+                                Location earlier = context.getLocation(includePathNode);
+                                earlier.setMessage("Unnecessary/conflicting <include>");
+                                location.setSecondary(earlier);
+                            }
+                        }
+                        context.report(ISSUE, exclude, location,
+                                String.format("Include `%1$s` is also excluded", excludePath));
+                    }
+                    hasPrefix = true;
+                    break;
+                }
+            }
+            if (!hasPrefix) {
+                Attr pathNode = exclude.getAttributeNode(ATTR_PATH);
+                assert pathNode != null;
+                context.report(ISSUE, exclude, context.getValueLocation(pathNode),
+                        String.format("`%1$s` is not in an included path", excludePath));
+            }
+        }
+    }
+
+    @NonNull
+    private static String validatePath(@NonNull XmlContext context, @NonNull Element element) {
+        Attr pathNode = element.getAttributeNode(ATTR_PATH);
+        if (pathNode == null) {
+            return "";
+        }
+        String value = pathNode.getValue();
+        if (value.contains("//")) {
+            context.report(ISSUE, element, context.getValueLocation(pathNode),
+                    "Paths are not allowed to contain `//`");
+        } else if (value.contains("..")) {
+            context.report(ISSUE, element, context.getValueLocation(pathNode),
+                    "Paths are not allowed to contain `..`");
+        } else if (value.contains("/")) {
+            String domain = element.getAttribute(ATTR_DOMAIN);
+            if (DOMAIN_SHARED_PREF.equals(domain) || DOMAIN_DATABASE.equals(domain)) {
+                context.report(ISSUE, element, context.getValueLocation(pathNode),
+                        String.format("Subdirectories are not allowed for domain `%1$s`",
+                                domain));
+            }
+        }
+        return value;
+    }
+
+    @Nullable
+    private static String validateDomain(@NonNull XmlContext context, @NonNull Element element) {
+        Attr domainNode = element.getAttributeNode(ATTR_DOMAIN);
+        if (domainNode == null) {
+            context.report(ISSUE, element, context.getLocation(element),
+                String.format("Missing domain attribute, expected one of %1$s",
+                        Joiner.on(", ").join(VALID_DOMAINS)));
+            return null;
+        }
+        String domain = domainNode.getValue();
+        for (String availableDomain : VALID_DOMAINS) {
+            if (availableDomain.equals(domain)) {
+                return domain;
+            }
+        }
+        context.report(ISSUE, element, context.getValueLocation(domainNode),
+                String.format("Unexpected domain `%1$s`, expected one of %2$s", domain,
+                        Joiner.on(", ").join(VALID_DOMAINS)));
+
+        return domain;
+    }
+
+    /** Valid domains; see FullBackup#getTokenForXmlDomain for authoritative list */
+    private static final String[] VALID_DOMAINS = new String[] {
+            DOMAIN_ROOT, DOMAIN_FILE, DOMAIN_DATABASE, DOMAIN_SHARED_PREF, DOMAIN_EXTERNAL
+    };
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
index 42562e4..33e49e6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
@@ -851,7 +851,8 @@
 
         // Network check for really up to date libraries? Only done in batch mode
         if (context.getScope().size() > 1 && context.isEnabled(REMOTE_VERSION)) {
-            PreciseRevision latest = getLatestVersion(context, dependency, dependency.isPreview());
+            PreciseRevision latest = getLatestVersionFromRemoteRepo(context.getClient(), dependency,
+                    dependency.isPreview());
             if (latest != null && isOlderThan(dependency, latest.getMajor(), latest.getMinor(),
                     latest.getMicro())) {
                 version = latest;
@@ -879,12 +880,14 @@
     }
 
     /** TODO: Cache these results somewhere! */
-    private static PreciseRevision getLatestVersion(@NonNull Context context,
+    @Nullable
+    public static PreciseRevision getLatestVersionFromRemoteRepo(@NonNull LintClient client,
             @NonNull GradleCoordinate dependency, boolean allowPreview) {
-        return getLatestVersion(context, dependency, true, allowPreview);
+        return getLatestVersionFromRemoteRepo(client, dependency, true, allowPreview);
     }
 
-    private static PreciseRevision getLatestVersion(@NonNull Context context,
+    @Nullable
+    private static PreciseRevision getLatestVersionFromRemoteRepo(@NonNull LintClient client,
             @NonNull GradleCoordinate dependency, boolean firstRowOnly, boolean allowPreview) {
         StringBuilder query = new StringBuilder();
         String encoding = UTF_8.name();
@@ -902,7 +905,7 @@
         }
         query.append("&wt=json");
 
-        String response = readUrlData(context, dependency, query.toString());
+        String response = readUrlData(client, dependency, query.toString());
         if (response == null) {
             return null;
         }
@@ -963,7 +966,7 @@
         if (!allowPreview && foundPreview && firstRowOnly) {
             // Recurse: search more than the first row this time to see if we can find a
             // non-preview version
-            return getLatestVersion(context, dependency, false, false);
+            return getLatestVersionFromRemoteRepo(client, dependency, false, false);
         }
 
         return null;
@@ -972,21 +975,20 @@
     /** Normally null; used for testing */
     @Nullable
     @VisibleForTesting
-    static Map<String,String> ourMockData;
+    static Map<String,String> sMockData;
 
     @Nullable
     private static String readUrlData(
-            @NonNull Context context,
+            @NonNull LintClient client,
             @NonNull GradleCoordinate dependency,
             @NonNull String query) {
         // For unit testing: avoid network as well as unexpected new versions
-        if (ourMockData != null) {
-            String value = ourMockData.get(query);
+        if (sMockData != null) {
+            String value = sMockData.get(query);
             assert value != null : query;
             return value;
         }
 
-        LintClient client = context.getClient();
         try {
             URL url = new URL(query);
 
@@ -1046,6 +1048,8 @@
         // See if the support library version is lower than the targetSdkVersion
         if (mTargetSdkVersion > 0 && dependency.getMajorVersion() < mTargetSdkVersion &&
                 dependency.getMajorVersion() != GradleCoordinate.PLUS_REV_VALUE &&
+                // The multidex library doesn't follow normal supportlib numbering scheme
+                !dependency.getArtifactId().startsWith("multidex") &&
                 context.isEnabled(COMPATIBILITY)) {
             String message = "This support library should not use a lower version ("
                 + dependency.getMajorVersion() + ") than the `targetSdkVersion` ("
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
index 5f7967d..f436283 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
@@ -21,15 +21,23 @@
 import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
 import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
 import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
-import com.android.tools.lint.detector.api.*;
-
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Node;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.Speed;
 
 import java.lang.reflect.Modifier;
 import java.util.Collections;
 import java.util.List;
 
+import lombok.ast.ClassDeclaration;
+import lombok.ast.Node;
+
 /**
  * Checks that Handler implementations are top level classes or static.
  * See the corresponding check in the android.os.Handler source code.
@@ -97,7 +105,7 @@
         Node locationNode = node instanceof ClassDeclaration
                 ? ((ClassDeclaration) node).astName() : node;
         Location location = context.getLocation(locationNode);
-        context.report(ISSUE, location, String.format(
+        context.report(ISSUE, locationNode, location, String.format(
                 "This Handler class should be static or leaks might occur (%1$s)",
                 cls.getName()));
     }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
index 9581682..c1f9962 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
@@ -386,6 +386,7 @@
                 }
 
                 if (candidates.size() > 1) {
+                    Collections.sort(candidates); // ensure stable test output
                     Location location = null;
                     List<String> folderNames = Lists.newArrayList();
                     for (int i = candidates.size() - 1; i >= 0; i--) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java
index 61680dd..66537f5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java
@@ -52,6 +52,8 @@
 import com.android.builder.model.ProductFlavorContainer;
 import com.android.builder.model.SourceProviderContainer;
 import com.android.builder.model.Variant;
+import com.android.ide.common.res2.AbstractResourceRepository;
+import com.android.ide.common.resources.ResourceUrl;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
@@ -814,6 +816,33 @@
                     (mApplicationTagHandle == null || isMainManifest(context, context.file))) {
                 mApplicationTagHandle = context.createLocationHandle(element);
             }
+            Attr fullBackupNode = element.getAttributeNodeNS(ANDROID_URI, "fullBackupContent");
+            if (fullBackupNode != null &&
+                    fullBackupNode.getValue().startsWith(PREFIX_RESOURCE_REF) &&
+                    context.getClient().supportsProjectResources()) {
+                AbstractResourceRepository resources = context.getClient()
+                        .getProjectResources(context.getProject(), true);
+                ResourceUrl url = ResourceUrl.parse(fullBackupNode.getValue());
+                if (url != null && !url.framework
+                        && resources != null
+                        && !resources.hasResourceItem(url.type, url.name)) {
+                    Location location = context.getValueLocation(fullBackupNode);
+                    context.report(ALLOW_BACKUP, fullBackupNode, location,
+                            "Missing `<full-backup-content>` resource");
+                }
+            } else if (fullBackupNode == null && context.getMainProject().getTargetSdk() >= 23) {
+                Location location = context.getLocation(element);
+                context.report(ALLOW_BACKUP, element, location,
+                        "Should explicitly set `android:fullBackupContent` to `true` or `false` "
+                                + "to opt-in to or out of full app data back-up and restore, or "
+                                + "alternatively to an `@xml` resource which specifies which "
+                                + "files to backup");
+            } else if (fullBackupNode == null && hasGcmReceiver(element)) {
+                Location location = context.getLocation(element);
+                context.report(ALLOW_BACKUP, element, location,
+                        "Should explicitly set `android:fullBackupContent` to avoid backing up "
+                                + "the GCM device specific regId.");
+            }
         } else if (mSeenApplication) {
             if (context.isEnabled(ORDER)) {
                 context.report(ORDER, element, context.getLocation(element),
@@ -842,6 +871,41 @@
         }
     }
 
+    /**
+     * Returns true if the given application element has a receiver with an intent filter
+     * action for GCM receive
+     */
+    private static boolean hasGcmReceiver(@NonNull Element application) {
+        NodeList applicationChildren = application.getChildNodes();
+        for (int i1 = 0, n1 = applicationChildren.getLength(); i1 < n1; i1++) {
+            Node applicationChild = applicationChildren.item(i1);
+            if (applicationChild.getNodeType() == Node.ELEMENT_NODE
+                    && TAG_RECEIVER.equals(applicationChild.getNodeName())) {
+                NodeList receiverChildren = applicationChild.getChildNodes();
+                for (int i2 = 0, n2 = receiverChildren.getLength(); i2 < n2; i2++) {
+                    Node receiverChild = receiverChildren.item(i2);
+                    if (receiverChild.getNodeType() == Node.ELEMENT_NODE
+                            && TAG_INTENT_FILTER.equals(receiverChild.getNodeName())) {
+                        NodeList filterChildren = receiverChild.getChildNodes();
+                        for (int i3 = 0, n3 = filterChildren.getLength(); i3 < n3; i3++) {
+                            Node filterChild = filterChildren.item(i3);
+                            if (filterChild.getNodeType() == Node.ELEMENT_NODE
+                                    && NODE_ACTION.equals(filterChild.getNodeName())) {
+                                Element action = (Element) filterChild;
+                                String name = action.getAttributeNS(ANDROID_URI, ATTR_NAME);
+                                if ("com.google.android.c2dm.intent.RECEIVE".equals(name)) {
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
     private static void checkMipmapIcon(@NonNull XmlContext context, @NonNull Element element) {
         Attr attribute = element.getAttributeNodeNS(ANDROID_URI, ATTR_ICON);
         if (attribute == null) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
index 78690fa..50bfc21 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
@@ -17,10 +17,14 @@
 package com.android.tools.lint.checks;
 
 import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.APP_PREFIX;
 import static com.android.SdkConstants.AUTO_URI;
+import static com.android.SdkConstants.TOOLS_PREFIX;
+import static com.android.SdkConstants.TOOLS_URI;
 import static com.android.SdkConstants.URI_PREFIX;
 import static com.android.SdkConstants.XMLNS_PREFIX;
 
+import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Implementation;
@@ -136,7 +140,8 @@
         NamedNodeMap attributes = root.getAttributes();
         for (int i = 0, n = attributes.getLength(); i < n; i++) {
             Node item = attributes.item(i);
-            if (item.getNodeName().startsWith(XMLNS_PREFIX)) {
+            String prefix = item.getNodeName();
+            if (prefix.startsWith(XMLNS_PREFIX)) {
                 String value = item.getNodeValue();
 
                 if (!value.equals(ANDROID_URI)) {
@@ -147,7 +152,7 @@
                         if (mUnusedNamespaces == null) {
                             mUnusedNamespaces = new HashMap<String, Attr>();
                         }
-                        mUnusedNamespaces.put(item.getNodeName().substring(XMLNS_PREFIX.length()),
+                        mUnusedNamespaces.put(prefix.substring(XMLNS_PREFIX.length()),
                                 attribute);
                     } else if (value.startsWith("urn:")) { //$NON-NLS-1$
                         continue;
@@ -162,6 +167,11 @@
                             value.startsWith("http://schemas.android.com/")) { //$NON-NLS-1$
                         context.report(RES_AUTO, attribute, context.getValueLocation(attribute),
                                 "Suspicious namespace: Did you mean `" + AUTO_URI + "`?");
+                    } else if (value.equals(TOOLS_URI) && (prefix.equals(XMLNS_ANDROID) ||
+                            prefix.endsWith(APP_PREFIX) && prefix.equals(
+                                    XMLNS_PREFIX + APP_PREFIX))) {
+                        context.report(TYPO, attribute, context.getValueLocation(attribute),
+                                "Suspicious namespace and prefix combination");
                     }
 
                     if (!context.isEnabled(TYPO)) {
@@ -209,6 +219,12 @@
                                     "Unexpected namespace URI bound to the `\"android\"` " +
                                     "prefix, was `%1$s`, expected `%2$s`", value, ANDROID_URI));
                     }
+                } else if (!prefix.equals(XMLNS_ANDROID) &&
+                        ((prefix.endsWith(TOOLS_PREFIX) && prefix.equals(XMLNS_PREFIX + TOOLS_PREFIX)) ||
+                        (prefix.endsWith(APP_PREFIX) && prefix.equals(XMLNS_PREFIX + APP_PREFIX)))) {
+                    Attr attribute = (Attr) item;
+                    context.report(TYPO, attribute, context.getValueLocation(attribute),
+                            "Suspicious namespace and prefix combination");
                 }
             }
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
index 60437aa..a664483 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
@@ -40,7 +40,7 @@
 import java.util.Collection;
 
 /**
- * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag.
+ * Checks whether a scroll view contains a nested scrolling widget
  */
 public class NestedScrollingWidgetDetector extends LayoutDetector {
     private int mVisitingHorizontalScroll;
@@ -77,6 +77,7 @@
     }
 
     @Override
+    @NonNull
     public Collection<String> getApplicableElements() {
         return Arrays.asList(
                 SCROLL_VIEW,
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
new file mode 100644
index 0000000..6494e78
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import static com.android.SdkConstants.CLASS_INTENT;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_READ;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_WRITE;
+import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.detector.api.JavaContext;
+
+import java.util.ListIterator;
+
+import lombok.ast.BinaryExpression;
+import lombok.ast.BinaryOperator;
+import lombok.ast.Cast;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.Expression;
+import lombok.ast.ExpressionStatement;
+import lombok.ast.InlineIfExpression;
+import lombok.ast.Node;
+import lombok.ast.NullLiteral;
+import lombok.ast.Select;
+import lombok.ast.Statement;
+import lombok.ast.VariableDeclaration;
+import lombok.ast.VariableDefinition;
+import lombok.ast.VariableDefinitionEntry;
+import lombok.ast.VariableReference;
+
+/**
+ * Utility for locating permissions required by an intent or content resolver
+ */
+public class PermissionFinder {
+    /**
+     * Operation that has a permission requirement -- such as a method call,
+     * a content resolver read or write operation, an intent, etc.
+     */
+    public enum Operation {
+        CALL, ACTION, READ, WRITE;
+
+        /** Prefix to use when describing a name with a permission requirement */
+        public String prefix() {
+            switch (this) {
+                case ACTION:
+                    return "by intent";
+                case READ:
+                    return "to read";
+                case WRITE:
+                    return "to write";
+                case CALL:
+                default:
+                    return "by";
+            }
+        }
+    }
+
+    /** A permission requirement given a name and operation */
+    public static class Result {
+        @NonNull public final PermissionRequirement requirement;
+        @NonNull public final String name;
+        @NonNull public final Operation operation;
+
+        public Result(
+                @NonNull Operation operation,
+                @NonNull PermissionRequirement requirement,
+                @NonNull String name) {
+            this.operation = operation;
+            this.requirement = requirement;
+            this.name = name;
+        }
+    }
+
+    /**
+     * Searches for a permission requirement for the given parameter in the given call
+     *
+     * @param operation the operation to look up
+     * @param context   the context to use for lookup
+     * @param parameter the parameter which contains the value which implies the permission
+     * @return the result with the permission requirement, or null if nothing is found
+     */
+    @Nullable
+    public static Result findRequiredPermissions(
+            @NonNull Operation operation,
+            @NonNull JavaContext context,
+            @NonNull Node parameter) {
+
+        // To find the permission required by an intent, we proceed in 3 steps:
+        // (1) Locate the parameter in the start call that corresponds to
+        //     the Intent
+        //
+        // (2) Find the place where the intent is initialized, and figure
+        //     out the action name being passed to it.
+        //
+        // (3) Find the place where the action is defined, and look for permission
+        //     annotations on that action declaration!
+
+        return new PermissionFinder(context, operation).search(parameter);
+    }
+
+    private PermissionFinder(@NonNull JavaContext context, @NonNull Operation operation) {
+        mContext = context;
+        mOperation = operation;
+    }
+
+    @NonNull private final JavaContext mContext;
+    @NonNull private final Operation mOperation;
+
+    @Nullable
+    public Result search(@NonNull Node node) {
+        if (node instanceof NullLiteral) {
+            return null;
+        } else if (node instanceof InlineIfExpression) {
+            InlineIfExpression expression = (InlineIfExpression) node;
+            if (expression.astIfTrue() != null) {
+                Result result = search(expression.astIfTrue());
+                if (result != null) {
+                    return result;
+                }
+            }
+            if (expression.astIfFalse() != null) {
+                Result result = search(expression.astIfFalse());
+                if (result != null) {
+                    return result;
+                }
+            }
+        } else if (node instanceof Cast) {
+            Cast cast = (Cast) node;
+            return search(cast.astOperand());
+        } else if (node instanceof ConstructorInvocation && mOperation == Operation.ACTION) {
+            // Identifies "new Intent(argument)" calls and, if found, continues
+            // resolving the argument instead looking for the action definition
+            ConstructorInvocation call = (ConstructorInvocation) node;
+            String type = call.astTypeReference().getTypeName();
+            if (type.equals("Intent") || type.equals(CLASS_INTENT)) {
+                Expression action = call.astArguments().first();
+                if (action != null) {
+                    return search(action);
+                }
+            }
+            return null;
+        } else if ((node instanceof VariableReference || node instanceof Select)) {
+            ResolvedNode resolved = mContext.resolve(node);
+            if (resolved instanceof ResolvedField) {
+                ResolvedField field = (ResolvedField) resolved;
+                if (mOperation == Operation.ACTION) {
+                    ResolvedAnnotation annotation = field.getAnnotation(PERMISSION_ANNOTATION);
+                    if (annotation != null) {
+                        return getPermissionRequirement(field, annotation);
+                    }
+                } else if (mOperation == Operation.READ || mOperation == Operation.WRITE) {
+                    String fqn = mOperation == Operation.READ
+                            ? PERMISSION_ANNOTATION_READ : PERMISSION_ANNOTATION_WRITE;
+                    ResolvedAnnotation annotation = field.getAnnotation(fqn);
+                    if (annotation != null) {
+                        Object o = annotation.getValue();
+                        if (o instanceof ResolvedAnnotation) {
+                            annotation = (ResolvedAnnotation) o;
+                            if (annotation.matches(PERMISSION_ANNOTATION)) {
+                                return getPermissionRequirement(field, annotation);
+                            }
+                        } else {
+                            // The complex annotations used for read/write cannot be
+                            // expressed in the external annotations format, so they're inlined.
+                            // (See Extractor.AnnotationData#write).
+                            //
+                            // Instead we've inlined the fields of the annotation on the
+                            // outer one:
+                            return getPermissionRequirement(field, annotation);
+                        }
+                    }
+                } else {
+                    assert false : mOperation;
+                }
+            } else if (node instanceof VariableReference) {
+                Statement statement = getParentOfType(node, Statement.class, false);
+                if (statement != null) {
+                    ListIterator<Node> iterator =
+                            statement.getParent().getChildren().listIterator();
+                    while (iterator.hasNext()) {
+                        if (iterator.next() == statement) {
+                            if (iterator.hasPrevious()) { // should always be true
+                                iterator.previous();
+                            }
+                            break;
+                        }
+                    }
+
+                    String targetName = ((VariableReference)node).astIdentifier().astValue();
+                    while (iterator.hasPrevious()) {
+                        Node previous = iterator.previous();
+                        if (previous instanceof VariableDeclaration) {
+                            VariableDeclaration declaration = (VariableDeclaration) previous;
+                            VariableDefinition definition = declaration.astDefinition();
+                            for (VariableDefinitionEntry entry : definition
+                                    .astVariables()) {
+                                if (entry.astInitializer() != null
+                                        && entry.astName().astValue().equals(targetName)) {
+                                    return search(entry.astInitializer());
+                                }
+                            }
+                        } else if (previous instanceof ExpressionStatement) {
+                            ExpressionStatement expressionStatement =
+                                    (ExpressionStatement) previous;
+                            Expression expression = expressionStatement.astExpression();
+                            if (expression instanceof BinaryExpression &&
+                                    ((BinaryExpression) expression).astOperator()
+                                            == BinaryOperator.ASSIGN) {
+                                BinaryExpression binaryExpression = (BinaryExpression) expression;
+                                if (targetName.equals(binaryExpression.astLeft().toString())) {
+                                    return search(binaryExpression.astRight());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    @NonNull
+    private Result getPermissionRequirement(
+            @NonNull ResolvedField field,
+            @NonNull ResolvedAnnotation annotation) {
+        PermissionRequirement requirement = PermissionRequirement.create(mContext, annotation);
+        String name = field.getContainingClass().getSimpleName() + "." + field.getName();
+        return new Result(mOperation, requirement, name);
+    }
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
new file mode 100644
index 0000000..632748c
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.sdklib.AndroidVersion;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A {@linkplain PermissionHolder} knows which permissions are held/granted and can look up
+ * individual permissions and respond to queries about whether they are held or not.
+ */
+public interface PermissionHolder {
+
+    /** Returns true if the permission holder has been granted the given permission */
+    boolean hasPermission(@NonNull String permission);
+
+    /** Returns true if the given permission is known to be revocable for targetSdkVersion >= M */
+    boolean isRevocable(@NonNull String permission);
+
+    @NonNull
+    AndroidVersion getMinSdkVersion();
+
+    @NonNull
+    AndroidVersion getTargetSdkVersion();
+
+    /**
+     * A convenience implementation of {@link PermissionHolder} backed by a set
+     */
+    class SetPermissionLookup implements PermissionHolder {
+        private Set<String> mGrantedPermissions;
+        private Set<String> mRevocablePermissions;
+        private AndroidVersion mMinSdkVersion;
+        private AndroidVersion mTargetSdkVersion;
+
+        public SetPermissionLookup(
+                @NonNull Set<String> grantedPermissions,
+                @NonNull Set<String> revocablePermissions,
+                @NonNull AndroidVersion minSdkVersion,
+                @NonNull AndroidVersion targetSdkVersion) {
+            mGrantedPermissions = grantedPermissions;
+            mRevocablePermissions = revocablePermissions;
+            mMinSdkVersion = minSdkVersion;
+            mTargetSdkVersion = targetSdkVersion;
+        }
+
+        @VisibleForTesting
+        public SetPermissionLookup(@NonNull Set<String> grantedPermissions,
+                @NonNull Set<String> revocablePermissions) {
+            this(grantedPermissions, revocablePermissions, AndroidVersion.DEFAULT,
+                    AndroidVersion.DEFAULT);
+        }
+
+        @VisibleForTesting
+        public SetPermissionLookup(@NonNull Set<String> grantedPermissions) {
+            this(grantedPermissions, Collections.<String>emptySet());
+        }
+
+        @Override
+        public boolean hasPermission(@NonNull String permission) {
+            return mGrantedPermissions.contains(permission);
+        }
+
+        @Override
+        public boolean isRevocable(@NonNull String permission) {
+            return mRevocablePermissions.contains(permission);
+        }
+
+        @NonNull
+        @Override
+        public AndroidVersion getMinSdkVersion() {
+            return mMinSdkVersion;
+        }
+
+        @NonNull
+        @Override
+        public AndroidVersion getTargetSdkVersion() {
+            return mTargetSdkVersion;
+        }
+
+        /**
+         * Creates a {@linkplain PermissionHolder} which combines the permissions
+         * held by the given holder, with the permissions implied by the given
+         * {@link PermissionRequirement}
+         */
+        @NonNull
+        public static PermissionHolder join(@NonNull PermissionHolder lookup,
+                                            @NonNull PermissionRequirement requirement) {
+            SetPermissionLookup empty = new SetPermissionLookup(Collections.<String>emptySet(),
+                    Collections.<String>emptySet(), lookup.getMinSdkVersion(),
+                    lookup.getTargetSdkVersion());
+            return join(lookup, requirement.getMissingPermissions(empty));
+        }
+
+        /**
+         * Creates a {@linkplain PermissionHolder} which combines the permissions
+         * held by the given holder, along with a set of additional permission names
+         */
+        @NonNull
+        public static PermissionHolder join(@NonNull final PermissionHolder lookup,
+                @Nullable final Set<String> permissions) {
+            if (permissions != null && !permissions.isEmpty()) {
+                return new PermissionHolder() {
+                    @Override
+                    public boolean hasPermission(@NonNull String permission) {
+                        return lookup.hasPermission(permission)
+                                || permissions.contains(permission);
+                    }
+
+                    @Override
+                    public boolean isRevocable(@NonNull String permission) {
+                        return lookup.isRevocable(permission);
+                    }
+
+                    @NonNull
+                    @Override
+                    public AndroidVersion getMinSdkVersion() {
+                        return lookup.getMinSdkVersion();
+                    }
+
+                    @NonNull
+                    @Override
+                    public AndroidVersion getTargetSdkVersion() {
+                        return lookup.getTargetSdkVersion();
+                    }
+                };
+            }
+            return lookup;
+        }
+    }
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
new file mode 100644
index 0000000..8923fcb
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+
+import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ALL_OF;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ANY_OF;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_CONDITIONAL;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import lombok.ast.BinaryExpression;
+import lombok.ast.BinaryOperator;
+import lombok.ast.Expression;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.Node;
+import lombok.ast.Select;
+import lombok.ast.VariableDefinitionEntry;
+
+/**
+ * A permission requirement is a boolean expression of permission names that a
+ * caller must satisfy for a given Android API.
+ */
+public abstract class PermissionRequirement {
+    public static final String ATTR_PROTECTION_LEVEL = "protectionLevel"; //$NON-NLS-1$
+    public static final String VALUE_DANGEROUS = "dangerous"; //$NON-NLS-1$
+
+    protected final ResolvedAnnotation annotation;
+    private int firstApi;
+    private int lastApi;
+
+    @SuppressWarnings("ConstantConditions")
+    public static final PermissionRequirement NONE = new PermissionRequirement(null) {
+        @Override
+        public boolean isSatisfied(@NonNull PermissionHolder available) {
+            return true;
+        }
+
+        @Override
+        public boolean appliesTo(@NonNull PermissionHolder available) {
+            return false;
+        }
+
+        @Override
+        public boolean isConditional() {
+            return false;
+        }
+
+        @Override
+        public boolean isRevocable(@NonNull PermissionHolder revocable) {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "None";
+        }
+
+        @Override
+        protected void addMissingPermissions(@NonNull PermissionHolder available,
+          @NonNull Set<String> result) {
+        }
+
+        @Override
+        protected void addRevocablePermissions(@NonNull Set<String> result,
+                @NonNull PermissionHolder revocable) {
+        }
+
+        @Nullable
+        @Override
+        public BinaryOperator getOperator() {
+            return null;
+        }
+
+        @NonNull
+        @Override
+        public Iterable<PermissionRequirement> getChildren() {
+            return Collections.emptyList();
+        }
+    };
+
+    private PermissionRequirement(@NonNull ResolvedAnnotation annotation) {
+        this.annotation = annotation;
+    }
+
+    @NonNull
+    public static PermissionRequirement create(
+            @Nullable Context context,
+            @NonNull ResolvedAnnotation annotation) {
+        String value = (String)annotation.getValue(ATTR_VALUE);
+        if (value != null && !value.isEmpty()) {
+            for (int i = 0, n = value.length(); i < n; i++) {
+                char c = value.charAt(i);
+                // See if it's a complex expression and if so build it up
+                if (c == '&' || c == '|' || c == '^') {
+                    return Complex.parse(annotation, context, value);
+                }
+            }
+
+            return new Single(annotation, value);
+        }
+
+        Object v = annotation.getValue(ATTR_ANY_OF);
+        if (v != null) {
+            if (v instanceof String[]) {
+                String[] anyOf = (String[])v;
+                if (anyOf.length > 0) {
+                    return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf);
+                }
+            } else if (v instanceof String) {
+                String[] anyOf = new String[] { (String)v };
+                return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf);
+            }
+        }
+
+        v = annotation.getValue(ATTR_ALL_OF);
+        if (v != null) {
+            if (v instanceof String[]) {
+                String[] allOf = (String[])v;
+                if (allOf.length > 0) {
+                    return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf);
+                }
+            } else if (v instanceof String) {
+                String[] allOf = new String[] { (String)v };
+                return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf);
+            }
+        }
+
+        return NONE;
+    }
+
+    /**
+     * Returns false if this permission does not apply given the specified minimum and
+     * target sdk versions
+     *
+     * @param minSdkVersion the minimum SDK version
+     * @param targetSdkVersion the target SDK version
+     * @return true if this permission requirement applies for the given versions
+     */
+    /**
+     * Returns false if this permission does not apply given the specified minimum and target
+     * sdk versions
+     *
+     * @param available   the permission holder which also knows the min and target versions
+     * @return true if this permission requirement applies for the given versions
+     */
+    protected boolean appliesTo(@NonNull PermissionHolder available) {
+        if (firstApi == 0) { // initialized?
+            firstApi = -1; // initialized, not specified
+
+            // Not initialized
+            Object o = annotation.getValue("apis");
+            if (o instanceof String) {
+                String range = (String)o;
+                // Currently only support the syntax "a..b" where a and b are inclusive end points
+                // and where "a" and "b" are optional
+                int index = range.indexOf("..");
+                if (index != -1) {
+                    try {
+                        if (index > 0) {
+                            firstApi = Integer.parseInt(range.substring(0, index));
+                        } else {
+                            firstApi = 1;
+                        }
+                        if (index + 2 < range.length()) {
+                            lastApi = Integer.parseInt(range.substring(index + 2));
+                        } else {
+                            lastApi = Integer.MAX_VALUE;
+                        }
+                    } catch (NumberFormatException ignore) {
+                    }
+                }
+            }
+        }
+
+        if (firstApi != -1) {
+            AndroidVersion minSdkVersion = available.getMinSdkVersion();
+            if (minSdkVersion.getFeatureLevel() > lastApi) {
+                return false;
+            }
+
+            AndroidVersion targetSdkVersion = available.getTargetSdkVersion();
+            if (targetSdkVersion.getFeatureLevel() < firstApi) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns whether this requirement is conditional, meaning that there are
+     * some circumstances in which the requirement is not necessary. For
+     * example, consider
+     * {@code android.app.backup.BackupManager.dataChanged(java.lang.String)} .
+     * Here the {@code android.permission.BACKUP} is required but only if the
+     * argument is not your own package.
+     * <p>
+     * This is used to handle permissions differently between the "missing" and
+     * "unused" checks. When checking for missing permissions, we err on the
+     * side of caution: if you are missing a permission, but the permission is
+     * conditional, you may not need it so we may not want to complain. However,
+     * when looking for unused permissions, we don't want to flag the
+     * conditional permissions as unused since they may be required.
+     *
+     * @return true if this requirement is conditional
+     */
+    public boolean isConditional() {
+        Object o = annotation.getValue(ATTR_CONDITIONAL);
+        if (o instanceof Boolean) {
+            return (Boolean)o;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether this requirement is for a single permission (rather than
+     * a boolean expression such as one permission or another.)
+     *
+     * @return true if this requirement is just a simple permission name
+     */
+    public boolean isSingle() {
+        return true;
+    }
+
+    /**
+     * Whether the permission requirement is satisfied given the set of granted permissions
+     *
+     * @param available the available permissions
+     * @return true if all permissions specified by this requirement are available
+     */
+    public abstract boolean isSatisfied(@NonNull PermissionHolder available);
+
+    /** Describes the missing permissions (e.g. "P1, P2 and P3") */
+    public String describeMissingPermissions(@NonNull PermissionHolder available) {
+        return "";
+    }
+
+    /** Returns the missing permissions (e.g. {"P1", "P2", "P3"} */
+    public Set<String> getMissingPermissions(@NonNull PermissionHolder available) {
+        Set<String> result = Sets.newHashSet();
+        addMissingPermissions(available, result);
+        return result;
+    }
+
+    protected abstract void addMissingPermissions(@NonNull PermissionHolder available,
+        @NonNull Set<String> result);
+
+    /** Returns the permissions in the requirement that are revocable */
+    public Set<String> getRevocablePermissions(@NonNull PermissionHolder revocable) {
+        Set<String> result = Sets.newHashSet();
+        addRevocablePermissions(result, revocable);
+        return result;
+    }
+
+    protected abstract void addRevocablePermissions(@NonNull Set<String> result,
+            @NonNull PermissionHolder revocable);
+
+    /**
+     * Returns whether this permission is revocable
+     *
+     * @param revocable the set of revocable permissions
+     * @return true if a user can revoke the permission
+     */
+    public abstract boolean isRevocable(@NonNull PermissionHolder revocable);
+
+    /**
+     * For permission requirements that combine children, the operator to combine them with; null
+     * for leaf nodes
+     */
+    @Nullable
+    public abstract BinaryOperator getOperator();
+
+    /**
+     * Returns nested requirements, combined via {@link #getOperator()}
+     */
+    @NonNull
+    public abstract Iterable<PermissionRequirement> getChildren();
+
+    /** Require a single permission */
+    private static class Single extends PermissionRequirement {
+        public final String name;
+
+        public Single(@NonNull ResolvedAnnotation annotation, @NonNull String name) {
+            super(annotation);
+            this.name = name;
+        }
+
+        @Override
+        public boolean isRevocable(@NonNull PermissionHolder revocable) {
+            return revocable.isRevocable(name) || isRevocableSystemPermission(name);
+        }
+
+        @Nullable
+        @Override
+        public BinaryOperator getOperator() {
+            return null;
+        }
+
+        @NonNull
+        @Override
+        public Iterable<PermissionRequirement> getChildren() {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public boolean isSingle() {
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        @Override
+        public boolean isSatisfied(@NonNull PermissionHolder available) {
+            return available.hasPermission(name) || !appliesTo(available);
+        }
+
+        @Override
+        public String describeMissingPermissions(@NonNull PermissionHolder available) {
+            return isSatisfied(available) ? "" : name;
+        }
+
+        @Override
+        protected void addMissingPermissions(@NonNull PermissionHolder available,
+            @NonNull Set<String> missing) {
+            if (!isSatisfied(available)) {
+                missing.add(name);
+            }
+        }
+
+        @Override
+        protected void addRevocablePermissions(@NonNull Set<String> result,
+                @NonNull PermissionHolder revocable) {
+            if (isRevocable(revocable)) {
+                result.add(name);
+            }
+        }
+    }
+
+    protected static void appendOperator(StringBuilder sb, BinaryOperator operator) {
+        sb.append(' ');
+        if (operator == BinaryOperator.LOGICAL_AND) {
+            sb.append("and");
+        } else if (operator == BinaryOperator.LOGICAL_OR) {
+            sb.append("or");
+        } else {
+            assert operator == BinaryOperator.BITWISE_XOR : operator;
+            sb.append("xor");
+        }
+        sb.append(' ');
+    }
+
+    /**
+     * Require a series of permissions, all with the same operator.
+     */
+    private static class Many extends PermissionRequirement {
+        public final BinaryOperator operator;
+        public final List<PermissionRequirement> permissions;
+
+        public Many(
+                @NonNull ResolvedAnnotation annotation,
+                BinaryOperator operator,
+                String[] names) {
+            super(annotation);
+            assert operator == BinaryOperator.LOGICAL_OR
+                    || operator == BinaryOperator.LOGICAL_AND : operator;
+            assert names.length >= 2;
+            this.operator = operator;
+            this.permissions = Lists.newArrayListWithExpectedSize(names.length);
+            for (String name : names) {
+                permissions.add(new Single(annotation, name));
+            }
+        }
+
+        @Override
+        public boolean isSingle() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            sb.append(permissions.get(0));
+
+            for (int i = 1; i < permissions.size(); i++) {
+                appendOperator(sb, operator);
+                sb.append(permissions.get(i));
+            }
+
+            return sb.toString();
+        }
+
+        @Override
+        public boolean isSatisfied(@NonNull PermissionHolder available) {
+            if (operator == BinaryOperator.LOGICAL_AND) {
+                for (PermissionRequirement requirement : permissions) {
+                    if (!requirement.isSatisfied(available) && requirement.appliesTo(available)) {
+                        return false;
+                    }
+                }
+                return true;
+            } else {
+                assert operator == BinaryOperator.LOGICAL_OR : operator;
+                for (PermissionRequirement requirement : permissions) {
+                    if (requirement.isSatisfied(available) || !requirement.appliesTo(available)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        }
+
+        @Override
+        public String describeMissingPermissions(@NonNull PermissionHolder available) {
+            StringBuilder sb = new StringBuilder();
+            boolean first = true;
+            for (PermissionRequirement requirement : permissions) {
+                if (!requirement.isSatisfied(available)) {
+                    if (first) {
+                        first = false;
+                    } else {
+                        appendOperator(sb, operator);
+                    }
+                    sb.append(requirement.describeMissingPermissions(available));
+                }
+            }
+            return sb.toString();
+        }
+
+        @Override
+        protected void addMissingPermissions(@NonNull PermissionHolder available,
+          @NonNull Set<String> missing) {
+            for (PermissionRequirement requirement : permissions) {
+                if (!requirement.isSatisfied(available)) {
+                    requirement.addMissingPermissions(available, missing);
+                }
+            }
+        }
+
+        @Override
+        protected void addRevocablePermissions(@NonNull Set<String> result,
+                @NonNull PermissionHolder revocable) {
+            for (PermissionRequirement requirement : permissions) {
+                requirement.addRevocablePermissions(result, revocable);
+            }
+        }
+
+        @Override
+        public boolean isRevocable(@NonNull PermissionHolder revocable) {
+            // TODO: Pass in the available set of permissions here, and if
+            // the operator is BinaryOperator.LOGICAL_OR, only return revocable=true
+            // if an unsatisfied permission is also revocable. In other words,
+            // if multiple permissions are allowed, and some of them are satisfied and
+            // not revocable the overall permission requirement is not revocable.
+            for (PermissionRequirement requirement : permissions) {
+                if (requirement.isRevocable(revocable)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Nullable
+        @Override
+        public BinaryOperator getOperator() {
+            return operator;
+        }
+
+        @NonNull
+        @Override
+        public Iterable<PermissionRequirement> getChildren() {
+            return permissions;
+        }
+    }
+
+    /**
+     * Require multiple permissions. This is a group of permissions with some
+     * associated boolean logic, such as "B or (C and (D or E))".
+     */
+    private static class Complex extends PermissionRequirement {
+        public final BinaryOperator operator;
+        public final PermissionRequirement left;
+        public final PermissionRequirement right;
+
+        public Complex(
+                @NonNull ResolvedAnnotation annotation,
+                BinaryOperator operator,
+                PermissionRequirement left,
+                PermissionRequirement right) {
+            super(annotation);
+            this.operator = operator;
+            this.left = left;
+            this.right = right;
+        }
+
+        @Override
+        public boolean isSingle() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            boolean needsParentheses = left instanceof Complex &&
+                    ((Complex) left).operator != BinaryOperator.LOGICAL_AND;
+            if (needsParentheses) {
+                sb.append('(');
+            }
+            sb.append(left.toString());
+            if (needsParentheses) {
+                sb.append(')');
+            }
+
+            appendOperator(sb, operator);
+
+            needsParentheses = right instanceof Complex &&
+                    ((Complex) right).operator != BinaryOperator.LOGICAL_AND;
+            if (needsParentheses) {
+                sb.append('(');
+            }
+            sb.append(right.toString());
+            if (needsParentheses) {
+                sb.append(')');
+            }
+
+            return sb.toString();
+        }
+
+        @Override
+        public boolean isSatisfied(@NonNull PermissionHolder available) {
+            boolean satisfiedLeft = left.isSatisfied(available) || !left.appliesTo(available);
+            boolean satisfiedRight = right.isSatisfied(available) || !right.appliesTo(available);
+            if (operator == BinaryOperator.LOGICAL_AND) {
+                return satisfiedLeft && satisfiedRight;
+            } else if (operator == BinaryOperator.LOGICAL_OR) {
+                return satisfiedLeft || satisfiedRight;
+            } else {
+                assert operator == BinaryOperator.BITWISE_XOR : operator;
+                return satisfiedLeft ^ satisfiedRight;
+            }
+        }
+
+        @Override
+        public String describeMissingPermissions(@NonNull PermissionHolder available) {
+            boolean satisfiedLeft = left.isSatisfied(available);
+            boolean satisfiedRight = right.isSatisfied(available);
+            if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) {
+                if (satisfiedLeft) {
+                    if (satisfiedRight) {
+                        return "";
+                    }
+                    return right.describeMissingPermissions(available);
+                } else if (satisfiedRight) {
+                    return left.describeMissingPermissions(available);
+                } else {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append(left.describeMissingPermissions(available));
+                    appendOperator(sb, operator);
+                    sb.append(right.describeMissingPermissions(available));
+                    return sb.toString();
+                }
+            } else {
+                assert operator == BinaryOperator.BITWISE_XOR : operator;
+                return toString();
+            }
+        }
+
+        @Override
+        protected void addMissingPermissions(@NonNull PermissionHolder available,
+          @NonNull Set<String> missing) {
+            boolean satisfiedLeft = left.isSatisfied(available);
+            boolean satisfiedRight = right.isSatisfied(available);
+            if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) {
+                if (satisfiedLeft) {
+                    if (satisfiedRight) {
+                        return;
+                    }
+                    right.addMissingPermissions(available, missing);
+                } else if (satisfiedRight) {
+                    left.addMissingPermissions(available, missing);
+                } else {
+                    left.addMissingPermissions(available, missing);
+                    right.addMissingPermissions(available, missing);
+                }
+            } else {
+                assert operator == BinaryOperator.BITWISE_XOR : operator;
+                left.addMissingPermissions(available, missing);
+                right.addMissingPermissions(available, missing);
+            }
+        }
+
+        @Override
+        protected void addRevocablePermissions(@NonNull Set<String> result,
+                @NonNull PermissionHolder revocable) {
+            left.addRevocablePermissions(result, revocable);
+            right.addRevocablePermissions(result, revocable);
+        }
+
+        @Override
+        public boolean isRevocable(@NonNull PermissionHolder revocable) {
+            // TODO: If operator == BinaryOperator.LOGICAL_OR only return
+            // revocable the there isn't a non-revocable term which is also satisfied.
+            return left.isRevocable(revocable) || right.isRevocable(revocable);
+        }
+
+        @NonNull
+        public static PermissionRequirement parse(@NonNull ResolvedAnnotation annotation,
+                @Nullable Context context, @NonNull final String value) {
+            // Parse an expression of the form (A op1 B op2 C) op3 (D op4 E) etc.
+            // We'll just use the Java parser to handle this to ensure that operator
+            // precedence etc is correct.
+            if (context == null) {
+                return NONE;
+            }
+            JavaParser javaParser = context.getClient().getJavaParser(null);
+            if (javaParser == null) {
+                return NONE;
+            }
+            try {
+                JavaContext javaContext = new JavaContext(context.getDriver(),
+                        context.getProject(), context.getMainProject(), context.file,
+                        javaParser) {
+                    @Nullable
+                    @Override
+                    public String getContents() {
+                        return ""
+                                + "class Test { void test() {\n"
+                                + "boolean result=" + value
+                                + ";\n}\n}";
+                    }
+                };
+                Node node = javaParser.parseJava(javaContext);
+                if (node != null) {
+                    final AtomicReference<Expression> reference = new AtomicReference<Expression>();
+                    node.accept(new ForwardingAstVisitor() {
+                        @Override
+                        public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
+                            reference.set(node.astInitializer());
+                            return true;
+                        }
+                    });
+                    Expression expression = reference.get();
+                    if (expression != null) {
+                        return parse(annotation, expression);
+                    }
+                }
+
+                return NONE;
+            } finally {
+                javaParser.dispose();
+            }
+        }
+
+        private static PermissionRequirement parse(
+                @NonNull ResolvedAnnotation annotation,
+                @NonNull Expression expression) {
+            if (expression instanceof Select) {
+                return new Single(annotation, expression.toString());
+            } else if (expression instanceof BinaryExpression) {
+                BinaryExpression binaryExpression = (BinaryExpression) expression;
+                BinaryOperator operator = binaryExpression.astOperator();
+                if (operator == BinaryOperator.LOGICAL_AND
+                        || operator == BinaryOperator.LOGICAL_OR
+                        || operator == BinaryOperator.BITWISE_XOR) {
+                    PermissionRequirement left = parse(annotation, binaryExpression.astLeft());
+                    PermissionRequirement right = parse(annotation, binaryExpression.astRight());
+                    return new Complex(annotation, operator, left, right);
+                }
+            }
+            return NONE;
+        }
+
+        @Nullable
+        @Override
+        public BinaryOperator getOperator() {
+            return operator;
+        }
+
+        @NonNull
+        @Override
+        public Iterable<PermissionRequirement> getChildren() {
+            return Arrays.asList(left, right);
+        }
+    }
+
+    /**
+     * Returns true if the given permission name is a revocable permission for
+     * targetSdkVersion >= 23
+     *
+     * @param name permission name
+     * @return true if this is a revocable permission
+     */
+    public static boolean isRevocableSystemPermission(@NonNull String name) {
+        return Arrays.binarySearch(REVOCABLE_PERMISSION_NAMES, name) >= 0;
+    }
+
+    @VisibleForTesting
+    static final String[] REVOCABLE_PERMISSION_NAMES = new String[] {
+            "android.permission.ACCESS_COARSE_LOCATION",
+            "android.permission.ACCESS_FINE_LOCATION",
+            "android.permission.BODY_SENSORS",
+            "android.permission.CALL_PHONE",
+            "android.permission.CAMERA",
+            "android.permission.PROCESS_OUTGOING_CALLS",
+            "android.permission.READ_CALENDAR",
+            "android.permission.READ_CALL_LOG",
+            "android.permission.READ_CELL_BROADCASTS",
+            "android.permission.READ_CONTACTS",
+            "android.permission.READ_EXTERNAL_STORAGE",
+            "android.permission.READ_PHONE_STATE",
+            "android.permission.READ_PROFILE",
+            "android.permission.READ_SMS",
+            "android.permission.READ_SOCIAL_STREAM",
+            "android.permission.RECEIVE_MMS",
+            "android.permission.RECEIVE_SMS",
+            "android.permission.RECEIVE_WAP_PUSH",
+            "android.permission.RECORD_AUDIO",
+            "android.permission.SEND_SMS",
+            "android.permission.USE_FINGERPRINT",
+            "android.permission.USE_SIP",
+            "android.permission.WRITE_CALENDAR",
+            "android.permission.WRITE_CALL_LOG",
+            "android.permission.WRITE_CONTACTS",
+            "android.permission.WRITE_EXTERNAL_STORAGE",
+            "android.permission.WRITE_SETTINGS",
+            "android.permission.WRITE_PROFILE",
+            "android.permission.WRITE_SOCIAL_STREAM",
+            "com.android.voicemail.permission.ADD_VOICEMAIL",
+    };
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
index b3bb0e1..749902e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
@@ -93,7 +93,7 @@
                 if (fqcn.equals(PREFERENCE_ACTIVITY) &&
                         !context.getDriver().isSuppressed(context, ISSUE, element)) {
                     String message = "`PreferenceActivity` should not be exported";
-                    context.report(ISSUE, context.getLocation(element), message);
+                    context.report(ISSUE, element, context.getLocation(element), message);
                 }
                 mExportedActivities.put(fqcn, context.createLocationHandle(element));
             }
@@ -151,7 +151,8 @@
             String message = String.format(
                     "`PreferenceActivity` subclass `%1$s` should not be exported",
                     className);
-            context.report(ISSUE, mExportedActivities.get(className).resolve(), message);
+            Location location = mExportedActivities.get(className).resolve();
+            context.report(ISSUE, declarationOrAnonymous, location, message);
         }
     }
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
index 68efcdf..a3e0400 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
@@ -239,7 +239,7 @@
                         context.report(ISSUE, item, context.getLocation(child), message);
                     }
                 } else {
-                    for (int j = 0, m = text.charAt(j); j < m; j++) {
+                    for (int j = 0, m = text.length(); j < m; j++) {
                         char c = text.charAt(j);
                         if (c == '@') {
                             ResourceUrl url = ResourceUrl.parse(text.trim());
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java
index 19610b3..744e5a3 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java
@@ -30,15 +30,10 @@
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.TextFormat;
-import com.android.utils.SdkUtils;
 import com.google.common.base.Splitter;
-import com.google.common.io.Files;
 
 import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
 import java.util.Iterator;
-import java.util.Properties;
 
 /**
  * Check for errors in .property files
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
index 786a4f6..de262a0 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
@@ -203,7 +203,7 @@
                     String.format(
                             "`%1$s` is a `<%2$s>` but is registered in the manifest as a `<%3$s>`",
                             className, tag, classToTag(wrongClass)));
-        } else if (!tag.equals(TAG_RECEIVER)) { // don't need to be registered
+        } else if (!TAG_RECEIVER.equals(tag)) { // don't need to be registered
             if (context.getMainProject().isGradleProject()) {
                 // Disabled for now; we need to formalize the difference between
                 // the *manifest* package and the variant package, since in some contexts
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
index b348380..1fae9e6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
@@ -389,7 +389,7 @@
                     continue;
                 }
                 Set<LayoutNode> canGrowRight = left.canGrowRight();
-                if (canGrowLeft.size() > 0 || canGrowRight.size() > 0) {
+                if (!canGrowLeft.isEmpty() || !canGrowRight.isEmpty()) {
                     canGrowRight.addAll(canGrowLeft);
                     LayoutNode nodeToBlame = right;
                     LayoutNode otherNode = left;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
index 47af129..a78ca03 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
@@ -44,6 +44,7 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
 import com.android.resources.ResourceFolderType;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.Context;
@@ -297,12 +298,13 @@
         return false;
     }
 
-    private static boolean hasLayoutVariations(File file) {
+    @VisibleForTesting
+    static boolean hasLayoutVariations(File file) {
         File parent = file.getParentFile();
         if (parent == null) {
             return false;
         }
-        File res = file.getParentFile();
+        File res = parent.getParentFile();
         if (res == null) {
             return false;
         }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
index c05973f..9071724 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
@@ -50,7 +50,6 @@
 import com.android.tools.lint.detector.api.Severity;
 import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
-import com.android.xml.AndroidManifest;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Element;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java
index 7647c2b..62ffd15 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java
@@ -17,22 +17,19 @@
 package com.android.tools.lint.checks;
 
 
-import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
+import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL;
 
 import com.android.annotations.NonNull;
 import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
 import com.android.tools.lint.detector.api.XmlContext;
 
 import org.w3c.dom.Attr;
 
-import java.io.File;
 import java.util.Collection;
 import java.util.Collections;
 
@@ -57,24 +54,12 @@
                     Scope.MANIFEST_SCOPE
             ));
     private static final String SIGNATURE_OR_SYSTEM = "signatureOrSystem"; //$NON-NLS-1$
-    private static final String PROTECTION_LEVEL = "protectionLevel"; //$NON-NLS-1$
 
-    @NonNull
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    @Override
-    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
-        return file.getName().equals(ANDROID_MANIFEST_XML);
-    }
-
-    // ---- Implements Detector.XmlScanner ---- TAG_PERMISSION
+    // ---- Implements Detector.XmlScanner ----
 
     @Override
     public Collection<String> getApplicableAttributes() {
-        return Collections.singletonList(PROTECTION_LEVEL);
+        return Collections.singletonList(ATTR_PROTECTION_LEVEL);
     }
 
     @Override
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
index 9f7ca78..6b902a5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
@@ -43,7 +43,6 @@
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.res2.AbstractResourceRepository;
 import com.android.ide.common.res2.ResourceItem;
-import com.android.ide.common.resources.configuration.LocaleQualifier;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 import com.android.tools.lint.client.api.JavaParser;
@@ -402,7 +401,7 @@
      * Checks whether the text begins with a non-unit word, pointing to a string
      * that should probably be a plural instead. This
      */
-    private boolean checkPotentialPlural(XmlContext context, Element element, String text,
+    private static boolean checkPotentialPlural(XmlContext context, Element element, String text,
             int wordBegin) {
         // This method should only be called if the text is known to start with a word
         assert Character.isLetter(text.charAt(wordBegin));
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
index 567f7a5..d34759a 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
@@ -16,57 +16,92 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_NAME;
 import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.SdkConstants.CLASS_INTENT;
 import static com.android.SdkConstants.INT_DEF_ANNOTATION;
 import static com.android.SdkConstants.R_CLASS;
 import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
 import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
+import static com.android.SdkConstants.TAG_PERMISSION;
+import static com.android.SdkConstants.TAG_USES_PERMISSION;
 import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
 import static com.android.resources.ResourceType.COLOR;
 import static com.android.resources.ResourceType.DRAWABLE;
 import static com.android.resources.ResourceType.MIPMAP;
+import static com.android.tools.lint.checks.PermissionFinder.Operation.ACTION;
+import static com.android.tools.lint.checks.PermissionFinder.Operation.READ;
+import static com.android.tools.lint.checks.PermissionFinder.Operation.WRITE;
+import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL;
+import static com.android.tools.lint.checks.PermissionRequirement.VALUE_DANGEROUS;
+import static com.android.tools.lint.detector.api.JavaContext.findSurroundingMethod;
 import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.resources.ResourceType;
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.checks.PermissionFinder.Operation;
+import com.android.tools.lint.checks.PermissionFinder.Result;
+import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
 import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
 import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
 import com.android.tools.lint.client.api.JavaParser.ResolvedField;
 import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
 import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.LintClient;
 import com.android.tools.lint.detector.api.Category;
 import com.android.tools.lint.detector.api.ConstantEvaluator;
 import com.android.tools.lint.detector.api.Detector;
 import com.android.tools.lint.detector.api.Implementation;
 import com.android.tools.lint.detector.api.Issue;
 import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Project;
 import com.android.tools.lint.detector.api.Scope;
 import com.android.tools.lint.detector.api.Severity;
+import com.android.utils.XmlUtils;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Locale;
+import java.util.Set;
 
 import lombok.ast.ArrayCreation;
 import lombok.ast.ArrayInitializer;
 import lombok.ast.AstVisitor;
 import lombok.ast.BinaryExpression;
 import lombok.ast.BinaryOperator;
+import lombok.ast.Catch;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.EnumConstant;
 import lombok.ast.Expression;
 import lombok.ast.ExpressionStatement;
 import lombok.ast.FloatingPointLiteral;
 import lombok.ast.ForwardingAstVisitor;
 import lombok.ast.InlineIfExpression;
 import lombok.ast.IntegralLiteral;
+import lombok.ast.MethodDeclaration;
 import lombok.ast.MethodInvocation;
 import lombok.ast.Node;
 import lombok.ast.NullLiteral;
 import lombok.ast.Select;
 import lombok.ast.Statement;
 import lombok.ast.StringLiteral;
+import lombok.ast.Try;
+import lombok.ast.TypeReference;
 import lombok.ast.UnaryExpression;
 import lombok.ast.UnaryOperator;
 import lombok.ast.VariableDeclaration;
@@ -173,14 +208,56 @@
         Severity.WARNING,
         IMPLEMENTATION);
 
+    /** Method result should be used */
+    public static final Issue MISSING_PERMISSION = Issue.create(
+            "MissingPermission", //$NON-NLS-1$
+            "Missing Permissions",
+
+            "This check scans through your code and libraries and looks at the APIs being used, " +
+            "and checks this against the set of permissions required to access those APIs. If " +
+            "the code using those APIs is called at runtime, then the program will crash.\n" +
+            "\n" +
+            "Furthermore, for permissions that are revocable (with targetSdkVersion 23), client " +
+            "code must also be prepared to handle the calls throwing an exception if the user " +
+            "rejects the request for permission at runtime.",
+
+            Category.CORRECTNESS,
+            9,
+            Severity.ERROR,
+            IMPLEMENTATION);
+
+    /** Passing the wrong constant to an int or String method */
+    public static final Issue THREAD = Issue.create(
+            "WrongThread", //$NON-NLS-1$
+            "Wrong Thread",
+
+            "Ensures that a method which expects to be called on a specific thread, is actually " +
+            "called from that thread. For example, calls on methods in widgets should always " +
+            "be made on the UI thread.",
+
+            Category.CORRECTNESS,
+            6,
+            Severity.ERROR,
+            IMPLEMENTATION)
+            .addMoreInfo(
+                    "http://developer.android.com/guide/components/processes-and-threads.html#Threads");
+
     public static final String CHECK_RESULT_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "CheckResult"; //$NON-NLS-1$
     public static final String COLOR_INT_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "ColorInt"; //$NON-NLS-1$
     public static final String INT_RANGE_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "IntRange"; //$NON-NLS-1$
     public static final String FLOAT_RANGE_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "FloatRange"; //$NON-NLS-1$
     public static final String SIZE_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "Size"; //$NON-NLS-1$
+    public static final String PERMISSION_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresPermission"; //$NON-NLS-1$
+    public static final String UI_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "UiThread"; //$NON-NLS-1$
+    public static final String MAIN_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "MainThread"; //$NON-NLS-1$
+    public static final String WORKER_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "WorkerThread"; //$NON-NLS-1$
+    public static final String BINDER_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "BinderThread"; //$NON-NLS-1$
+    public static final String PERMISSION_ANNOTATION_READ = PERMISSION_ANNOTATION + ".Read"; //$NON-NLS-1$
+    public static final String PERMISSION_ANNOTATION_WRITE = PERMISSION_ANNOTATION + ".Write"; //$NON-NLS-1$
 
-    public static final String RES_SUFFIX = "Res";       //$NON-NLS-1$
-    public static final String ATTR_SUGGEST = "suggest"; //$NON-NLS-1$
+    public static final String RES_SUFFIX = "Res";
+    public static final String THREAD_SUFFIX = "Thread";
+    public static final String ATTR_SUGGEST = "suggest";
     public static final String ATTR_TO = "to";
     public static final String ATTR_FROM = "from";
     public static final String ATTR_FROM_INCLUSIVE = "fromInclusive";
@@ -188,6 +265,17 @@
     public static final String ATTR_MULTIPLE = "multiple";
     public static final String ATTR_MIN = "min";
     public static final String ATTR_MAX = "max";
+    public static final String ATTR_ALL_OF = "allOf";
+    public static final String ATTR_ANY_OF = "anyOf";
+    public static final String ATTR_CONDITIONAL = "conditional";
+
+    /**
+     * Marker ResourceType used to signify that an expression is of type {@code @ColorInt},
+     * which isn't actually a ResourceType but one we want to specifically compare with.
+     * We're using {@link ResourceType#PUBLIC} because that one won't appear in the R
+     * class (and ResourceType is an enum we can't just create new constants for.)
+     */
+    public static final ResourceType COLOR_INT_MARKER_TYPE = ResourceType.PUBLIC;
 
     /**
      * Constructs a new {@link SupportAnnotationDetector} check
@@ -195,31 +283,47 @@
     public SupportAnnotationDetector() {
     }
 
-    private static void checkMethodAnnotation(
+    private void checkMethodAnnotation(
             @NonNull JavaContext context,
-            @NonNull MethodInvocation node,
+            @NonNull ResolvedMethod method,
+            @NonNull Node node,
             @NonNull ResolvedAnnotation annotation) {
         String signature = annotation.getSignature();
         if (CHECK_RESULT_ANNOTATION.equals(signature)
                 || signature.endsWith(".CheckReturnValue")) { // support findbugs annotation too
             checkResult(context, node, annotation);
+        } else if (signature.equals(PERMISSION_ANNOTATION)) {
+            PermissionRequirement requirement = PermissionRequirement.create(context, annotation);
+            checkPermission(context, node, method, null, requirement);
+        } else if (signature.endsWith(THREAD_SUFFIX)
+                && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+            checkThreading(context, node, method, signature);
         }
     }
 
-    private static void checkParameterAnnotation(
+    private void checkParameterAnnotation(
             @NonNull JavaContext context,
             @NonNull Node argument,
-            @NonNull ResolvedAnnotation annotation) {
+            @NonNull Node call,
+            @NonNull ResolvedMethod method,
+            @NonNull ResolvedAnnotation annotation,
+            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
         String signature = annotation.getSignature();
 
         if (COLOR_INT_ANNOTATION.equals(signature)) {
             checkColor(context, argument);
         } else if (signature.equals(INT_RANGE_ANNOTATION)) {
-            checkIntRange(context, annotation, argument);
+            checkIntRange(context, annotation, argument, allAnnotations);
         } else if (signature.equals(FLOAT_RANGE_ANNOTATION)) {
             checkFloatRange(context, annotation, argument);
         } else if (signature.equals(SIZE_ANNOTATION)) {
             checkSize(context, annotation, argument);
+        } else if (signature.startsWith(PERMISSION_ANNOTATION)) {
+            // PERMISSION_ANNOTATION, PERMISSION_ANNOTATION_READ, PERMISSION_ANNOTATION_WRITE
+            // When specified on a parameter, that indicates that we're dealing with
+            // a permission requirement on this *method* which depends on the value
+            // supplied by this parameter
+            checkParameterPermission(context, signature, call, method, argument);
         } else {
             // We only run @IntDef, @StringDef and @<Type>Res checks if we're not
             // running inside Android Studio / IntelliJ where there are already inspections
@@ -228,9 +332,9 @@
             // have to
             if (signature.equals(INT_DEF_ANNOTATION)) {
                 boolean flag = annotation.getValue(TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
-                checkTypeDefConstant(context, annotation, argument, null, flag);
+                checkTypeDefConstant(context, annotation, argument, null, flag, allAnnotations);
             } else if (signature.equals(STRING_DEF_ANNOTATION)) {
-                checkTypeDefConstant(context, annotation, argument, null, false);
+                checkTypeDefConstant(context, annotation, argument, null, false, allAnnotations);
             } else if (signature.endsWith(RES_SUFFIX)) {
                 String typeString = signature.substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
                         signature.length() - RES_SUFFIX.length()).toLowerCase(Locale.US);
@@ -244,6 +348,35 @@
         }
     }
 
+    private void checkParameterPermission(
+            @NonNull JavaContext context,
+            @NonNull String signature,
+            @NonNull Node call,
+            @NonNull ResolvedMethod method,
+            @NonNull Node argument) {
+        Operation operation = null;
+        if (signature.equals(PERMISSION_ANNOTATION_READ)) {
+            operation = READ;
+        } else if (signature.equals(PERMISSION_ANNOTATION_WRITE)) {
+            operation = WRITE;
+        } else {
+            TypeDescriptor type = context.getType(argument);
+            if (type == null) {
+                return;
+            }
+            if (type.matchesSignature(CLASS_INTENT)) {
+                operation = ACTION;
+            }
+        }
+        if (operation == null) {
+            return;
+        }
+        Result result = PermissionFinder.findRequiredPermissions(operation, context, argument);
+        if (result != null) {
+            checkPermission(context, call, method, result, result.requirement);
+        }
+    }
+
     private static void checkColor(@NonNull JavaContext context, @NonNull Node argument) {
         if (argument instanceof InlineIfExpression) {
             InlineIfExpression expression = (InlineIfExpression) argument;
@@ -252,9 +385,9 @@
             return;
         }
 
-        ResourceType type = getResourceType(argument);
+        List<ResourceType> types = getResourceTypes(context, argument);
 
-        if (type == ResourceType.COLOR) {
+        if (types != null && types.contains(ResourceType.COLOR)) {
             String message = String.format(
                     "Should pass resolved color instead of resource id here: " +
                             "`getResources().getColor(%1$s)`", argument.toString());
@@ -262,10 +395,270 @@
         }
     }
 
-    private static void checkResult(@NonNull JavaContext context, @NonNull MethodInvocation node,
+    private void checkPermission(
+            @NonNull JavaContext context,
+            @NonNull Node node,
+            @Nullable ResolvedMethod method,
+            @Nullable Result result,
+            @NonNull PermissionRequirement requirement) {
+        if (requirement.isConditional()) {
+            return;
+        }
+        PermissionHolder permissions = getPermissions(context);
+        if (!requirement.isSatisfied(permissions)) {
+            // See if it looks like we're holding the permission implicitly by @RequirePermission
+            // annotations in the surrounding context
+            permissions  = addLocalPermissions(context, permissions, node);
+            if (!requirement.isSatisfied(permissions)) {
+                Operation operation;
+                String name;
+                if (result != null) {
+                    name = result.name;
+                    operation = result.operation;
+                } else {
+                    assert method != null;
+                    name = method.getContainingClass().getSimpleName() + "." + method.getName();
+                    operation = Operation.CALL;
+                }
+                String message = getMissingPermissionMessage(requirement, name, permissions,
+                        operation);
+                context.report(MISSING_PERMISSION, node, context.getLocation(node), message);
+            }
+        } else if (requirement.isRevocable(permissions) &&
+                context.getMainProject().getTargetSdkVersion().getFeatureLevel() >= 23) {
+            // Ensure that the caller is handling a security exception
+            // First check to see if we're inside a try/catch which catches a SecurityException
+            // (or some wider exception than that). Check for nested try/catches too.
+            boolean handlesMissingPermission = false;
+            Node parent = node;
+            while (true) {
+                Try tryCatch = getParentOfType(parent, Try.class);
+                if (tryCatch == null) {
+                    break;
+                } else {
+                    for (Catch aCatch : tryCatch.astCatches()) {
+                        TypeReference catchType = aCatch.astExceptionDeclaration().
+                                astTypeReference();
+                        if (isSecurityException(context,
+                                catchType)) {
+                            handlesMissingPermission = true;
+                            break;
+                        }
+                    }
+                    parent = tryCatch;
+                }
+            }
+
+            // If not, check to see if the method itself declares that it throws a
+            // SecurityException or something wider.
+            if (!handlesMissingPermission) {
+                MethodDeclaration declaration = getParentOfType(parent, MethodDeclaration.class);
+                if (declaration != null) {
+                    for (TypeReference typeReference : declaration.astThrownTypeReferences()) {
+                        if (isSecurityException(context, typeReference)) {
+                            handlesMissingPermission = true;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // If not, check to see if the code is deliberately checking to see if the
+            // given permission is available.
+            if (!handlesMissingPermission) {
+                Node methodNode = JavaContext.findSurroundingMethod(node);
+                if (methodNode != null) {
+                    CheckPermissionVisitor visitor = new CheckPermissionVisitor(node);
+                    methodNode.accept(visitor);
+                    handlesMissingPermission = visitor.checksPermission();
+                }
+            }
+
+            if (!handlesMissingPermission) {
+                String message = getUnhandledPermissionMessage();
+                context.report(MISSING_PERMISSION, node, context.getLocation(node), message);
+            }
+        }
+    }
+
+    @NonNull
+    private static PermissionHolder addLocalPermissions(
+            @NonNull JavaContext context,
+            @NonNull PermissionHolder permissions,
+            @NonNull Node node) {
+        // Accumulate @RequirePermissions available in the local context
+        Node methodNode = JavaContext.findSurroundingMethod(node);
+        if (methodNode == null) {
+            return permissions;
+        }
+        ResolvedNode resolved = context.resolve(methodNode);
+        if (!(resolved instanceof ResolvedMethod)) {
+            return permissions;
+        }
+        ResolvedMethod method = (ResolvedMethod) resolved;
+        ResolvedAnnotation annotation = method.getAnnotation(PERMISSION_ANNOTATION);
+        permissions = mergeAnnotationPermissions(context, permissions, annotation);
+        annotation = method.getContainingClass().getAnnotation(PERMISSION_ANNOTATION);
+        permissions = mergeAnnotationPermissions(context, permissions, annotation);
+        return permissions;
+    }
+
+    @NonNull
+    private static PermissionHolder mergeAnnotationPermissions(
+            @NonNull JavaContext context,
+            @NonNull PermissionHolder permissions,
+            @Nullable ResolvedAnnotation annotation) {
+        if (annotation != null) {
+            PermissionRequirement requirement = PermissionRequirement.create(context, annotation);
+            permissions = SetPermissionLookup.join(permissions, requirement);
+        }
+
+        return permissions;
+    }
+
+    /** Returns the error message shown when a given call is missing one or more permissions */
+    public static String getMissingPermissionMessage(@NonNull PermissionRequirement requirement,
+            @NonNull String callName, @NonNull PermissionHolder permissions,
+            @NonNull Operation operation) {
+        return String.format("Missing permissions required %1$s %2$s: %3$s", operation.prefix(),
+                callName, requirement.describeMissingPermissions(permissions));
+    }
+
+    /** Returns the error message shown when a revocable permission call is not properly handled */
+    public static String getUnhandledPermissionMessage() {
+        return "Call requires permission which may be rejected by user: code should explicitly "
+                + "check to see if permission is available (with `checkPermission`) or handle "
+                + "a potential `SecurityException`";
+    }
+
+    /**
+     * Visitor which looks through a method, up to a given call (the one requiring a
+     * permission) and checks whether it's preceeded by a call to checkPermission or
+     * checkCallingPermission or enforcePermission etc.
+     * <p>
+     * Currently it only looks for the presence of this check; it does not perform
+     * flow analysis to determine whether the check actually affects program flow
+     * up to the permission call, or whether the check permission is checking for
+     * permissions sufficient to satisfy the permission requirement of the target call,
+     * or whether the check return value (== PERMISSION_GRANTED vs != PERMISSION_GRANTED)
+     * is handled correctly, etc.
+     */
+    private static class CheckPermissionVisitor extends ForwardingAstVisitor {
+        private boolean mChecksPermission;
+        private boolean mDone;
+        private final Node mTarget;
+
+        public CheckPermissionVisitor(@NonNull Node target) {
+            mTarget = target;
+        }
+
+        @Override
+        public boolean visitNode(Node node) {
+            return mDone;
+        }
+
+        @Override
+        public boolean visitMethodInvocation(MethodInvocation node) {
+            if (node == mTarget) {
+                mDone = true;
+            }
+
+            String name = node.astName().astValue();
+            if ((name.startsWith("check") || name.startsWith("enforce"))
+                    && name.endsWith("Permission")) {
+                mChecksPermission = true;
+                mDone = true;
+            }
+            return super.visitMethodInvocation(node);
+        }
+
+        public boolean checksPermission() {
+            return mChecksPermission;
+        }
+    }
+
+    private static boolean isSecurityException(
+            @NonNull JavaContext context,
+            @NonNull TypeReference typeReference) {
+        TypeDescriptor type = context.getType(typeReference);
+        return type != null && (type.matchesSignature("java.lang.SecurityException") ||
+                type.matchesSignature("java.lang.RuntimeException") ||
+                type.matchesSignature("java.lang.Exception") ||
+                type.matchesSignature("java.lang.Throwable"));
+    }
+
+    private PermissionHolder mPermissions;
+
+    private PermissionHolder getPermissions(
+            @NonNull JavaContext context) {
+        if (mPermissions == null) {
+            Set<String> permissions = Sets.newHashSetWithExpectedSize(30);
+            Set<String> revocable = Sets.newHashSetWithExpectedSize(4);
+            LintClient client = context.getClient();
+            // Gather permissions from all projects that contribute to the
+            // main project.
+            Project mainProject = context.getMainProject();
+            for (File manifest : mainProject.getManifestFiles()) {
+                addPermissions(client, permissions, revocable, manifest);
+            }
+            for (Project library : mainProject.getAllLibraries()) {
+                for (File manifest : library.getManifestFiles()) {
+                    addPermissions(client, permissions, revocable, manifest);
+                }
+            }
+
+            AndroidVersion minSdkVersion = mainProject.getMinSdkVersion();
+            AndroidVersion targetSdkVersion = mainProject.getTargetSdkVersion();
+            mPermissions = new SetPermissionLookup(permissions, revocable, minSdkVersion,
+                targetSdkVersion);
+        }
+
+        return mPermissions;
+    }
+
+    private static void addPermissions(@NonNull LintClient client,
+            @NonNull Set<String> permissions,
+            @NonNull Set<String> revocable,
+            @NonNull File manifest) {
+        Document document = XmlUtils.parseDocumentSilently(client.readFile(manifest), true);
+        if (document == null) {
+            return;
+        }
+        Element root = document.getDocumentElement();
+        if (root == null) {
+            return;
+        }
+        NodeList children = root.getChildNodes();
+        for (int i = 0, n = children.getLength(); i < n; i++) {
+            org.w3c.dom.Node item = children.item(i);
+            if (item.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) {
+                continue;
+            }
+            String nodeName = item.getNodeName();
+            if (nodeName.equals(TAG_USES_PERMISSION)) {
+                Element element = (Element)item;
+                String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+                if (!name.isEmpty()) {
+                    permissions.add(name);
+                }
+            } else if (nodeName.equals(TAG_PERMISSION)) {
+                Element element = (Element)item;
+                String protectionLevel = element.getAttributeNS(ANDROID_URI,
+                        ATTR_PROTECTION_LEVEL);
+                if (VALUE_DANGEROUS.equals(protectionLevel)) {
+                    String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+                    if (!name.isEmpty()) {
+                        revocable.add(name);
+                    }
+                }
+            }
+        }
+    }
+
+    private static void checkResult(@NonNull JavaContext context, @NonNull Node node,
             @NonNull ResolvedAnnotation annotation) {
         if (node.getParent() instanceof ExpressionStatement) {
-            String methodName = node.astName().astValue();
+            String methodName = JavaContext.getMethodName(node);
             Object suggested = annotation.getValue(ATTR_SUGGEST);
 
             // Failing to check permissions is a potential security issue (and had an existing
@@ -273,7 +666,8 @@
             // custom severity in their LintOptions etc) so continue to use that issue
             // (which also has category Security rather than Correctness) for these:
             Issue issue = CHECK_RESULT;
-            if (methodName.startsWith("check") && methodName.contains("Permission")) {
+            if (methodName != null && methodName.startsWith("check")
+                    && methodName.contains("Permission")) {
                 issue = CHECK_PERMISSION;
             }
 
@@ -290,6 +684,101 @@
         }
     }
 
+    private static void checkThreading(
+            @NonNull JavaContext context,
+            @NonNull Node node,
+            @NonNull ResolvedMethod method,
+            @NonNull String annotation) {
+        String threadContext = getThreadContext(context, node);
+        if (threadContext != null && !isCompatibleThread(threadContext, annotation)) {
+            String message = String.format("Method %1$s must be called from the `%2$s` thread, currently inferred thread is `%3$s` thread",
+                    method.getName(), describeThread(annotation), describeThread(threadContext));
+            context.report(THREAD, node, context.getLocation(node), message);
+        }
+    }
+
+    @NonNull
+    public static String describeThread(@NonNull String annotation) {
+        if (UI_THREAD_ANNOTATION.equals(annotation)) {
+            return "UI";
+        }
+        else if (MAIN_THREAD_ANNOTATION.equals(annotation)) {
+            return "main";
+        }
+        else if (BINDER_THREAD_ANNOTATION.equals(annotation)) {
+            return "binder";
+        }
+        else if (WORKER_THREAD_ANNOTATION.equals(annotation)) {
+            return "worker";
+        } else {
+            return "other";
+        }
+    }
+
+    /** returns true if the two threads are compatible */
+    public static boolean isCompatibleThread(@NonNull String thread1, @NonNull String thread2) {
+        if (thread1.equals(thread2)) {
+            return true;
+        }
+
+        // Allow @UiThread and @MainThread to be combined
+        if (thread1.equals(UI_THREAD_ANNOTATION)) {
+            if (thread2.equals(MAIN_THREAD_ANNOTATION)) {
+                return true;
+            }
+        } else if (thread1.equals(MAIN_THREAD_ANNOTATION)) {
+            if (thread2.equals(UI_THREAD_ANNOTATION)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /** Attempts to infer the current thread context at the site of the given method call */
+    @Nullable
+    private static String getThreadContext(@NonNull JavaContext context,
+            @NonNull Node methodCall) {
+        Node node = findSurroundingMethod(methodCall);
+        if (node != null) {
+            ResolvedNode resolved = context.resolve(node);
+            if (resolved instanceof ResolvedMethod) {
+                ResolvedMethod method = (ResolvedMethod) resolved;
+                ResolvedClass cls = method.getContainingClass();
+
+                while (method != null) {
+                    for (ResolvedAnnotation annotation : method.getAnnotations()) {
+                        String name = annotation.getSignature();
+                        if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+                                && name.endsWith(THREAD_SUFFIX)) {
+                            return name;
+                        }
+                    }
+                    method = method.getSuperMethod();
+                }
+
+                // See if we're extending a class with a known threading context
+                while (cls != null) {
+                    for (ResolvedAnnotation annotation : cls.getAnnotations()) {
+                        String name = annotation.getSignature();
+                        if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+                                && name.endsWith(THREAD_SUFFIX)) {
+                            return name;
+                        }
+                    }
+                    cls = cls.getSuperClass();
+                }
+            }
+        }
+
+        // In the future, we could also try to infer the threading context using
+        // other heuristics. For example, if we're in a method with unknown threading
+        // context, but we see that the method is called by another method with a known
+        // threading context, we can infer that that threading context is the context for
+        // this thread too (assuming the call is direct).
+
+        return null;
+    }
 
     private static boolean isNumber(@NonNull Node argument) {
         return argument instanceof IntegralLiteral || argument instanceof UnaryExpression
@@ -314,17 +803,22 @@
             @NonNull JavaContext context,
             @NonNull Node argument,
             @Nullable ResourceType expectedType) {
-        ResourceType actual = getResourceType(argument);
+        List<ResourceType> actual = getResourceTypes(context, argument);
         if (actual == null && (!isNumber(argument) || isZero(argument) || isMinusOne(argument)) ) {
             return;
         } else if (actual != null && (expectedType == null
-                || expectedType == actual
-                || expectedType == DRAWABLE && (actual == COLOR || actual == MIPMAP))) {
+                || actual.contains(expectedType)
+                || expectedType == DRAWABLE && (actual.contains(COLOR) || actual.contains(MIPMAP)))) {
             return;
         }
 
         String message;
-        if (expectedType != null) {
+        if (actual != null && actual.size() == 1 && actual.get(0) == COLOR_INT_MARKER_TYPE) {
+            message = "Expected a color resource id (`R.color.`) but received an RGB integer";
+        } else if (expectedType == COLOR_INT_MARKER_TYPE) {
+            message = String.format("Should pass resolved color instead of resource id here: " +
+                    "`getResources().getColor(%1$s)`", argument.toString());
+        } else if (expectedType != null) {
             message = String.format(
                     "Expected resource of type %1$s", expectedType.getName());
         } else {
@@ -334,7 +828,8 @@
     }
 
     @Nullable
-    private static ResourceType getResourceType(@NonNull Node argument) {
+    private static List<ResourceType> getResourceTypes(@NonNull JavaContext context,
+            @NonNull Node argument) {
         if (argument instanceof Select) {
             Select node = (Select) argument;
             if (node.astOperand() instanceof Select) {
@@ -342,15 +837,17 @@
                 if (select.astOperand() instanceof Select) { // android.R....
                     Select innerSelect = (Select) select.astOperand();
                     if (innerSelect.astIdentifier().astValue().equals(R_CLASS)) {
-                        String type = select.astIdentifier().astValue();
-                        return ResourceType.getEnum(type);
+                        String typeName = select.astIdentifier().astValue();
+                        ResourceType type = ResourceType.getEnum(typeName);
+                        return type != null ? Collections.singletonList(type) : null;
                     }
                 }
                 if (select.astOperand() instanceof VariableReference) {
                     VariableReference reference = (VariableReference) select.astOperand();
                     if (reference.astIdentifier().astValue().equals(R_CLASS)) {
-                        String type = select.astIdentifier().astValue();
-                        return ResourceType.getEnum(type);
+                        String typeName = select.astIdentifier().astValue();
+                        ResourceType type = ResourceType.getEnum(typeName);
+                        return type != null ? Collections.singletonList(type) : null;
                     }
                 }
             }
@@ -365,8 +862,9 @@
                         Expression typeOperand = select.astOperand();
                         if (typeOperand instanceof Select) {
                             Select typeSelect = (Select) typeOperand;
-                            String type = typeSelect.astIdentifier().astValue();
-                            return ResourceType.getEnum(type);
+                            String typeName = typeSelect.astIdentifier().astValue();
+                            ResourceType type = ResourceType.getEnum(typeName);
+                            return type != null ? Collections.singletonList(type) : null;
                         }
                     }
                 }
@@ -394,7 +892,7 @@
                                 .astVariables()) {
                             if (entry.astInitializer() != null
                                     && entry.astName().astValue().equals(targetName)) {
-                                return getResourceType(entry.astInitializer());
+                                return getResourceTypes(context, entry.astInitializer());
                             }
                         }
                     } else if (previous instanceof ExpressionStatement) {
@@ -405,12 +903,42 @@
                                         == BinaryOperator.ASSIGN) {
                             BinaryExpression binaryExpression = (BinaryExpression) expression;
                             if (targetName.equals(binaryExpression.astLeft().toString())) {
-                                return getResourceType(binaryExpression.astRight());
+                                return getResourceTypes(context, binaryExpression.astRight());
                             }
                         }
                     }
                 }
             }
+        } else if (argument instanceof MethodInvocation) {
+            ResolvedNode resolved = context.resolve(argument);
+            if (resolved != null) {
+                for (ResolvedAnnotation annotation : resolved.getAnnotations()) {
+                    String signature = annotation.getSignature();
+                    if (signature.equals(COLOR_INT_ANNOTATION)) {
+                        return Collections.singletonList(COLOR_INT_MARKER_TYPE);
+                    }
+                    if (signature.endsWith(RES_SUFFIX)
+                            && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+                        String typeString = signature.substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
+                                signature.length() - RES_SUFFIX.length()).toLowerCase(Locale.US);
+                        ResourceType type = ResourceType.getEnum(typeString);
+                        if (type != null) {
+                            return Collections.singletonList(type);
+                        } else if (typeString.equals("any")) { // @AnyRes
+                            ResourceType[] types = ResourceType.values();
+                            List<ResourceType> result = Lists.newArrayListWithExpectedSize(
+                                    types.length);
+                            for (ResourceType t : types) {
+                                if (t != COLOR_INT_MARKER_TYPE) {
+                                    result.add(t);
+                                }
+                            }
+
+                            return result;
+                        }
+                    }
+                }
+            }
         }
 
         return null;
@@ -419,19 +947,35 @@
     private static void checkIntRange(
             @NonNull JavaContext context,
             @NonNull ResolvedAnnotation annotation,
+            @NonNull Node argument,
+            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
+        String message = getIntRangeError(context, annotation, argument);
+        if (message != null) {
+            if (findIntDef(allAnnotations) != null) {
+                // Don't flag int range errors if there is an int def annotation there too;
+                // there could be a valid @IntDef constant. (The @IntDef check will
+                // perform range validation by calling getIntRange.)
+                return;
+            }
+
+            context.report(RANGE, argument, context.getLocation(argument), message);
+        }
+    }
+
+    @Nullable
+    private static String getIntRangeError(
+            @NonNull JavaContext context,
+            @NonNull ResolvedAnnotation annotation,
             @NonNull Node argument) {
         Object object = ConstantEvaluator.evaluate(context, argument);
         if (!(object instanceof Number)) {
-            return;
+            return null;
         }
         long value = ((Number)object).longValue();
         long from = getLongAttribute(annotation, ATTR_FROM, Long.MIN_VALUE);
         long to = getLongAttribute(annotation, ATTR_TO, Long.MAX_VALUE);
 
-        String message = getIntRangeError(value, from, to);
-        if (message != null) {
-            context.report(RANGE, argument, context.getLocation(argument), message);
-        }
+        return getIntRangeError(value, from, to);
     }
 
     /**
@@ -622,12 +1166,37 @@
         return message;
     }
 
+    @Nullable
+    private static ResolvedAnnotation findIntRange(
+            @NonNull Iterable<ResolvedAnnotation> annotations) {
+        for (ResolvedAnnotation annotation : annotations) {
+            if (INT_RANGE_ANNOTATION.equals(annotation.getName())) {
+                return annotation;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static ResolvedAnnotation findIntDef(
+            @NonNull Iterable<ResolvedAnnotation> annotations) {
+        for (ResolvedAnnotation annotation : annotations) {
+            if (INT_DEF_ANNOTATION.equals(annotation.getName())) {
+                return annotation;
+            }
+        }
+
+        return null;
+    }
+
     private static void checkTypeDefConstant(
             @NonNull JavaContext context,
             @NonNull ResolvedAnnotation annotation,
             @NonNull Node argument,
             @Nullable Node errorNode,
-            boolean flag) {
+            boolean flag,
+            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
         if (argument instanceof NullLiteral) {
             // Accepted for @StringDef
             return;
@@ -635,7 +1204,8 @@
 
         if (argument instanceof StringLiteral) {
             StringLiteral string = (StringLiteral) argument;
-            checkTypeDefConstant(context, annotation, argument, errorNode, false, string.astValue());
+            checkTypeDefConstant(context, annotation, argument, errorNode, false, string.astValue(),
+                    allAnnotations);
         } else if (argument instanceof IntegralLiteral) {
             IntegralLiteral literal = (IntegralLiteral) argument;
             int value = literal.astIntValue();
@@ -643,35 +1213,68 @@
                 // Accepted for a flag @IntDef
                 return;
             }
-            checkTypeDefConstant(context, annotation, argument, errorNode, flag, value);
+
+            ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+            if (rangeAnnotation != null) {
+                // Allow @IntRange on this number
+                if (getIntRangeError(context, rangeAnnotation, literal) == null) {
+                    return;
+                }
+            }
+
+            checkTypeDefConstant(context, annotation, argument, errorNode, flag, value,
+                    allAnnotations);
         } else if (isMinusOne(argument)) {
             // -1 is accepted unconditionally for flags
             if (!flag) {
-                reportTypeDef(context, annotation, argument, errorNode);
+                ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+                if (rangeAnnotation != null) {
+                    // Allow @IntRange on this number
+                    if (getIntRangeError(context, rangeAnnotation, argument) == null) {
+                        return;
+                    }
+                }
+
+                reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
             }
         } else if (argument instanceof InlineIfExpression) {
             InlineIfExpression expression = (InlineIfExpression) argument;
             if (expression.astIfTrue() != null) {
-                checkTypeDefConstant(context, annotation, expression.astIfTrue(), errorNode, flag);
+                checkTypeDefConstant(context, annotation, expression.astIfTrue(), errorNode, flag,
+                        allAnnotations);
             }
             if (expression.astIfFalse() != null) {
-                checkTypeDefConstant(context, annotation, expression.astIfFalse(), errorNode, flag);
+                checkTypeDefConstant(context, annotation, expression.astIfFalse(), errorNode, flag,
+                        allAnnotations);
             }
         } else if (argument instanceof UnaryExpression) {
             UnaryExpression expression = (UnaryExpression) argument;
             UnaryOperator operator = expression.astOperator();
             if (flag) {
-                checkTypeDefConstant(context, annotation, expression.astOperand(), errorNode, true);
+                checkTypeDefConstant(context, annotation, expression.astOperand(), errorNode, true,
+                        allAnnotations);
             } else if (operator == UnaryOperator.BINARY_NOT) {
                 context.report(TYPE_DEF, expression, context.getLocation(expression),
                         "Flag not allowed here");
+            } else if (operator == UnaryOperator.UNARY_MINUS) {
+                ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+                if (rangeAnnotation != null) {
+                    // Allow @IntRange on this number
+                    if (getIntRangeError(context, rangeAnnotation, argument) == null) {
+                        return;
+                    }
+                }
+
+                reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
             }
         } else if (argument instanceof BinaryExpression) {
             // If it's ?: then check both the if and else clauses
             BinaryExpression expression = (BinaryExpression) argument;
             if (flag) {
-                checkTypeDefConstant(context, annotation, expression.astLeft(), errorNode, true);
-                checkTypeDefConstant(context, annotation, expression.astRight(), errorNode, true);
+                checkTypeDefConstant(context, annotation, expression.astLeft(), errorNode, true,
+                        allAnnotations);
+                checkTypeDefConstant(context, annotation, expression.astRight(), errorNode, true,
+                        allAnnotations);
             } else {
                 BinaryOperator operator = expression.astOperator();
                 if (operator == BinaryOperator.BITWISE_AND
@@ -684,7 +1287,8 @@
         } else {
             ResolvedNode resolved = context.resolve(argument);
             if (resolved instanceof ResolvedField) {
-                checkTypeDefConstant(context, annotation, argument, errorNode, flag, resolved);
+                checkTypeDefConstant(context, annotation, argument, errorNode, flag, resolved,
+                        allAnnotations);
             } else if (argument instanceof VariableReference) {
                 Statement statement = getParentOfType(argument, Statement.class, false);
                 if (statement != null) {
@@ -710,7 +1314,8 @@
                                         && entry.astName().astValue().equals(targetName)) {
                                     checkTypeDefConstant(context, annotation,
                                             entry.astInitializer(),
-                                            errorNode != null ? errorNode : argument, flag);
+                                            errorNode != null ? errorNode : argument, flag,
+                                            allAnnotations);
                                     return;
                                 }
                             }
@@ -724,7 +1329,8 @@
                                 if (targetName.equals(binaryExpression.astLeft().toString())) {
                                     checkTypeDefConstant(context, annotation,
                                             binaryExpression.astRight(),
-                                            errorNode != null ? errorNode : argument, flag);
+                                            errorNode != null ? errorNode : argument, flag,
+                                            allAnnotations);
                                     return;
                                 }
                             }
@@ -737,7 +1343,8 @@
 
     private static void checkTypeDefConstant(@NonNull JavaContext context,
             @NonNull ResolvedAnnotation annotation, @NonNull Node argument,
-            @Nullable Node errorNode, boolean flag, Object value) {
+            @Nullable Node errorNode, boolean flag, Object value,
+            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
         Object allowed = annotation.getValue();
         if (allowed instanceof Object[]) {
             Object[] allowedValues = (Object[]) allowed;
@@ -746,22 +1353,23 @@
                     return;
                 }
             }
-            reportTypeDef(context, argument, errorNode, flag, allowedValues);
+            reportTypeDef(context, argument, errorNode, flag, allowedValues, allAnnotations);
         }
     }
 
     private static void reportTypeDef(@NonNull JavaContext context,
             @NonNull ResolvedAnnotation annotation, @NonNull Node argument,
-            @Nullable Node errorNode) {
+            @Nullable Node errorNode, @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
         Object allowed = annotation.getValue();
         if (allowed instanceof Object[]) {
             Object[] allowedValues = (Object[]) allowed;
-            reportTypeDef(context, argument, errorNode, false, allowedValues);
+            reportTypeDef(context, argument, errorNode, false, allowedValues, allAnnotations);
         }
     }
 
     private static void reportTypeDef(@NonNull JavaContext context, @NonNull Node node,
-            @Nullable Node errorNode, boolean flag, @NonNull Object[] allowedValues) {
+            @Nullable Node errorNode, boolean flag, @NonNull Object[] allowedValues,
+            @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
         String values = listAllowedValues(allowedValues);
         String message;
         if (flag) {
@@ -769,6 +1377,17 @@
         } else {
             message = "Must be one of: " + values;
         }
+
+        ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+        if (rangeAnnotation != null) {
+            // Allow @IntRange on this number
+            String rangeError = getIntRangeError(context, rangeAnnotation, node);
+            if (rangeError != null && !rangeError.isEmpty()) {
+                message += " or " + Character.toLowerCase(rangeError.charAt(0))
+                        + rangeError.substring(1);
+            }
+        }
+
         if (errorNode == null) {
             errorNode = node;
         }
@@ -832,41 +1451,66 @@
         return defaultValue;
     }
 
-    @Nullable
-    static ResolvedAnnotation getRelevantAnnotation(@NonNull ResolvedAnnotation annotation) {
-        String signature = annotation.getSignature();
-        if (signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
-            // Bail on the nullness annotations early since they're the most commonly
-            // defined ones. They're not analyzed in lint yet.
-            if (signature.endsWith(".Nullable") || signature.endsWith(".NonNull")) {
-                return null;
+    @NonNull
+    static Iterable<ResolvedAnnotation> filterRelevantAnnotations(
+            @NonNull Iterable<ResolvedAnnotation> annotations) {
+        List<ResolvedAnnotation> result = null;
+        Iterator<ResolvedAnnotation> iterator = annotations.iterator();
+        int index = 0;
+        while (iterator.hasNext()) {
+            ResolvedAnnotation annotation = iterator.next();
+            index++;
+
+            String signature = annotation.getSignature();
+            if (signature.startsWith("java.")) {
+                // @Override, @SuppressWarnings etc. Ignore
+                continue;
             }
 
+            if (signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+                // Bail on the nullness annotations early since they're the most commonly
+                // defined ones. They're not analyzed in lint yet.
+                if (signature.endsWith(".Nullable") || signature.endsWith(".NonNull")) {
+                    continue;
+                }
 
-            return annotation;
-        }
+                // Common case: there's just one annotation; no need to create a list copy
+                if (!iterator.hasNext() && index == 1) {
+                    return annotations;
+                }
+                if (result == null) {
+                    result = new ArrayList<ResolvedAnnotation>(2);
+                }
+                result.add(annotation);
+            }
 
-        if (signature.startsWith("java.")) {
-            // @Override, @SuppressWarnings etc. Ignore
-            return null;
-        }
-
-        // Special case @IntDef and @StringDef: These are used on annotations
-        // themselves. For example, you create a new annotation named @foo.bar.Baz,
-        // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures.
-        // Here we want to map from @foo.bar.Baz to the corresponding int def.
-        // Don't need to compute this if performing @IntDef or @StringDef lookup
-        ResolvedClass type = annotation.getClassType();
-        if (type != null) {
-            for (ResolvedAnnotation inner : type.getAnnotations()) {
-                if (inner.matches(INT_DEF_ANNOTATION)
-                        || inner.matches(STRING_DEF_ANNOTATION)) {
-                    return inner;
+            // Special case @IntDef and @StringDef: These are used on annotations
+            // themselves. For example, you create a new annotation named @foo.bar.Baz,
+            // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures.
+            // Here we want to map from @foo.bar.Baz to the corresponding int def.
+            // Don't need to compute this if performing @IntDef or @StringDef lookup
+            ResolvedClass type = annotation.getClassType();
+            if (type != null) {
+                Iterator<ResolvedAnnotation> iterator2 = type.getAnnotations().iterator();
+                while (iterator2.hasNext()) {
+                    ResolvedAnnotation inner = iterator2.next();
+                    if (inner.matches(INT_DEF_ANNOTATION)
+                            || inner.matches(PERMISSION_ANNOTATION)
+                            || inner.matches(INT_RANGE_ANNOTATION)
+                            || inner.matches(STRING_DEF_ANNOTATION)) {
+                        if (!iterator.hasNext() && !iterator2.hasNext() && index == 1) {
+                            return annotations;
+                        }
+                        if (result == null) {
+                            result = new ArrayList<ResolvedAnnotation>(2);
+                        }
+                        result.add(inner);
+                    }
                 }
             }
         }
 
-        return null;
+        return result != null ? result : Collections.<ResolvedAnnotation>emptyList();
     }
 
     // ---- Implements JavaScanner ----
@@ -874,7 +1518,11 @@
     @Override
     public
     List<Class<? extends Node>> getApplicableNodeTypes() {
-        return Collections.<Class<? extends Node>>singletonList(MethodInvocation.class);
+        //noinspection unchecked
+        return Arrays.<Class<? extends Node>>asList(
+          MethodInvocation.class,
+          ConstructorInvocation.class,
+          EnumConstant.class);
     }
 
     @Nullable
@@ -883,7 +1531,7 @@
         return new CallVisitor(context);
     }
 
-    private static class CallVisitor extends ForwardingAstVisitor {
+    private class CallVisitor extends ForwardingAstVisitor {
         private final JavaContext mContext;
 
         public CallVisitor(JavaContext context) {
@@ -895,31 +1543,63 @@
             ResolvedNode resolved = mContext.resolve(call);
             if (resolved instanceof ResolvedMethod) {
                 ResolvedMethod method = (ResolvedMethod) resolved;
-                Iterable<ResolvedAnnotation> annotations = method.getAnnotations();
-                for (ResolvedAnnotation annotation : annotations) {
-                    annotation = getRelevantAnnotation(annotation);
-                    if (annotation != null) {
-                        checkMethodAnnotation(mContext, call, annotation);
-                    }
-                }
-
-                Iterator<Expression> arguments = call.astArguments().iterator();
-                for (int i = 0, n = method.getArgumentCount();
-                        i < n && arguments.hasNext();
-                        i++) {
-                    Expression argument = arguments.next();
-
-                    annotations = method.getParameterAnnotations(i);
-                    for (ResolvedAnnotation annotation : annotations) {
-                        annotation = getRelevantAnnotation(annotation);
-                        if (annotation != null) {
-                            checkParameterAnnotation(mContext, argument, annotation);
-                        }
-                    }
-                }
+                checkCall(call, method);
             }
 
             return false;
         }
+
+        @Override
+        public boolean visitConstructorInvocation(@NonNull ConstructorInvocation call) {
+            ResolvedNode resolved = mContext.resolve(call);
+            if (resolved instanceof ResolvedMethod) {
+                ResolvedMethod method = (ResolvedMethod) resolved;
+                checkCall(call, method);
+            }
+
+            return false;
+        }
+
+        @Override
+        public boolean visitEnumConstant(EnumConstant node) {
+            ResolvedNode resolved = mContext.resolve(node);
+            if (resolved instanceof ResolvedMethod) {
+                ResolvedMethod method = (ResolvedMethod) resolved;
+                checkCall(node, method);
+            }
+
+            return false;
+        }
+
+        private void checkCall(@NonNull Node call, ResolvedMethod method) {
+            Iterable<ResolvedAnnotation> annotations = method.getAnnotations();
+            annotations = filterRelevantAnnotations(annotations);
+            for (ResolvedAnnotation annotation : annotations) {
+                checkMethodAnnotation(mContext, method, call, annotation);
+            }
+
+            // Look for annotations on the class as well: these trickle
+            // down to all the methods in the class
+            ResolvedClass containingClass = method.getContainingClass();
+            annotations = containingClass.getAnnotations();
+            annotations = filterRelevantAnnotations(annotations);
+            for (ResolvedAnnotation annotation : annotations) {
+                checkMethodAnnotation(mContext, method, call, annotation);
+            }
+
+            Iterator<Expression> arguments = JavaContext.getParameters(call);
+            for (int i = 0, n = method.getArgumentCount();
+                    i < n && arguments.hasNext();
+                    i++) {
+                Expression argument = arguments.next();
+
+                annotations = method.getParameterAnnotations(i);
+                annotations = filterRelevantAnnotations(annotations);
+                for (ResolvedAnnotation annotation : annotations) {
+                    checkParameterAnnotation(mContext, argument, call, method, annotation,
+                            annotations);
+                }
+            }
+        }
     }
 }
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java
index c3d4324..c059fd6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java
@@ -32,7 +32,7 @@
 import java.util.Collection;
 
 /**
- * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag.
+ * Checks whether a view hierarchy has too many views or has a suspiciously deep hierarchy
  */
 public class TooManyViewsDetector extends LayoutDetector {
 
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java
index 594822b..7ec48e1 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java
@@ -185,7 +185,7 @@
         }
     }
 
-    private void checkFlow(@NonNull ClassContext context, @NonNull ClassNode classNode,
+    private static void checkFlow(@NonNull ClassContext context, @NonNull ClassNode classNode,
             @NonNull MethodNode method, @NonNull MethodInsnNode acquire) {
         // Track allocations such that we know whether the type of the call
         // is on a SecureRandom rather than a Random
diff --git a/lint/libs/lint-tests/build.gradle b/lint/libs/lint-tests/build.gradle
index cbebcca..7781b8a 100644
--- a/lint/libs/lint-tests/build.gradle
+++ b/lint/libs/lint-tests/build.gradle
@@ -1,14 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
-apply plugin: 'sdk-java-lib'
+apply plugin: 'jacoco'
 
 group = 'com.android.tools.lint'
 archivesBaseName = 'lint-tests'
@@ -19,6 +10,10 @@
 
     compile 'junit:junit:4.12'
     compile project(':base:testutils')
+
+
+    testCompile 'org.mockito:mockito-all:1.9.5'
+    testCompile 'org.codehaus.groovy:groovy-all:2.2.1'
 }
 
 sourceSets {
diff --git a/lint/libs/lint-tests/lint-tests.iml b/lint/libs/lint-tests/lint-tests.iml
index ca46d9d..66d4ddf 100644
--- a/lint/libs/lint-tests/lint-tests.iml
+++ b/lint/libs/lint-tests/lint-tests.iml
@@ -4,8 +4,10 @@
     <exclude-output />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
       <excludeFolder url="file://$MODULE_DIR$/.settings" />
       <excludeFolder url="file://$MODULE_DIR$/build" />
+      <excludeFolder url="file://$MODULE_DIR$/src/test/.settings" />
     </content>
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="inheritedJdk" />
@@ -20,5 +22,7 @@
     <orderEntry type="module" module-name="layoutlib-api-base" exported="" />
     <orderEntry type="module" module-name="sdklib-base" exported="" />
     <orderEntry type="library" exported="" name="intellij-annotations" level="project" />
+    <orderEntry type="library" scope="TEST" name="groovy" level="project" />
+    <orderEntry type="library" scope="TEST" name="mockito" level="project" />
   </component>
 </module>
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
index 4344cd7..e103a08 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
@@ -115,7 +115,7 @@
 
     private Detector mDetector;
 
-    private Detector getDetectorInstance() {
+    protected final Detector getDetectorInstance() {
         if (mDetector == null) {
             mDetector = getDetector();
         }
@@ -123,20 +123,7 @@
         return mDetector;
     }
 
-    protected List<Issue> getIssues() {
-        List<Issue> issues = new ArrayList<Issue>();
-        Class<? extends Detector> detectorClass = getDetectorInstance().getClass();
-        // Get the list of issues from the registry and filter out others, to make sure
-        // issues are properly registered
-        List<Issue> candidates = new BuiltinIssueRegistry().getIssues();
-        for (Issue issue : candidates) {
-            if (issue.getImplementation().getDetectorClass() == detectorClass) {
-                issues.add(issue);
-            }
-        }
-
-        return issues;
-    }
+    protected abstract List<Issue> getIssues();
 
     public class CustomIssueRegistry extends IssueRegistry {
         @NonNull
@@ -242,6 +229,16 @@
         return checkLint(client, Collections.singletonList(projectDir));
     }
 
+    protected String lintProjectIncrementally(String currentFile, TestFile... files)
+            throws Exception {
+        File projectDir = getProjectDir(null, files);
+        File current = new File(projectDir, currentFile.replace('/', File.separatorChar));
+        assertTrue(current.exists());
+        TestLintClient client = createClient();
+        client.setIncremental(current);
+        return checkLint(client, Collections.singletonList(projectDir));
+    }
+
     /**
      * Run lint on the given files when constructed as a separate project
      * @return The output of the lint check. On Windows, this transforms all directory
@@ -543,8 +540,10 @@
             }
         }
 
+        @NonNull
         @Override
-        public Configuration getConfiguration(@NonNull Project project) {
+        public Configuration getConfiguration(@NonNull Project project,
+                @Nullable LintDriver driver) {
             return LintDetectorTest.this.getConfiguration(this, project);
         }
 
@@ -647,8 +646,11 @@
                 }
             };
             // Only support 1 resource folder in test setup right now
-            assertEquals(1, project.getResourceFolders().size());
-            resourceSet.addSource(project.getResourceFolders().get(0));
+            int size = project.getResourceFolders().size();
+            assertTrue("Found " + size + " test resources folders", size <= 1);
+            if (size == 1) {
+                resourceSet.addSource(project.getResourceFolders().get(0));
+            }
             try {
                 resourceSet.loadFromFiles(logger);
                 merger.addDataSet(resourceSet);
@@ -737,9 +739,6 @@
                     }
                 }
             }
-            catch (DuplicateDataException e) {
-                fail(e.getMessage());
-            }
             catch (MergingException e) {
                 fail(e.getMessage());
             }
diff --git a/lint/libs/lint-tests/src/test/.settings/org.eclipse.core.resources.prefs b/lint/libs/lint-tests/src/test/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..609db57
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//java/com/android/tools/lint/checks/TypoDetectorTest.java=UTF-8
+encoding//java/com/android/tools/lint/checks/TypoLookupTest.java=UTF-8
+encoding//java/com/android/tools/lint/checks/TypographyDetectorTest.java=UTF-8
+encoding//java/com/android/tools/lint/checks/data/res/values-nb/typos.xml=UTF-8
diff --git a/lint/libs/lint-tests/src/test/.settings/org.moreunit.prefs b/lint/libs/lint-tests/src/test/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..1d51ca3
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/.settings/org.moreunit.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=lint-check-tests\:java\:lint-api\:src/main/java\#lint-check-tests\:java\:lint-checks\:src/main/java\#lint-check-tests\:java\:lint-cli\:src/main/java
+org.moreunit.useprojectsettings=true
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/EcjParserTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/EcjParserTest.java
new file mode 100644
index 0000000..457a447
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/EcjParserTest.java
@@ -0,0 +1,916 @@
+/*
+ * 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.tools.lint;
+
+import static com.android.tools.lint.EcjParser.equalsCompound;
+import static com.android.tools.lint.EcjParser.startsWithCompound;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.checks.AbstractCheckTest;
+import com.android.tools.lint.checks.SdCardDetector;
+import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtilsTest;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.collect.Lists;
+
+import org.intellij.lang.annotations.Language;
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.ast.AnnotationElement;
+import lombok.ast.BinaryExpression;
+import lombok.ast.Block;
+import lombok.ast.ClassDeclaration;
+import lombok.ast.DescribedNode;
+import lombok.ast.ExpressionStatement;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.Identifier;
+import lombok.ast.KeywordModifier;
+import lombok.ast.MethodInvocation;
+import lombok.ast.Modifiers;
+import lombok.ast.Node;
+import lombok.ast.NormalTypeBody;
+import lombok.ast.Select;
+import lombok.ast.TypeReferencePart;
+import lombok.ast.VariableDeclaration;
+import lombok.ast.VariableDefinition;
+import lombok.ast.VariableReference;
+import lombok.ast.printer.SourceFormatter;
+import lombok.ast.printer.SourcePrinter;
+import lombok.ast.printer.TextFormatter;
+
+public class EcjParserTest extends AbstractCheckTest {
+    public void testTryCatchHang() throws Exception {
+        // Ensure that we're really using this parser
+        JavaParser javaParser = createClient().getJavaParser(null);
+        assertNotNull(javaParser);
+        assertTrue(javaParser.getClass().getName(), javaParser instanceof EcjParser);
+
+        // See https://code.google.com/p/projectlombok/issues/detail?id=573#c6
+        // With lombok.ast 0.2.1 and the parboiled-based Java parser this test will hang forever.
+        assertEquals(
+                "No warnings.",
+
+                lintProject("src/test/pkg/TryCatchHang.java.txt=>src/test/pkg/TryCatchHang.java"));
+    }
+
+    public void testKitKatLanguageFeatures() throws Exception {
+        String testClass = "" +
+                "package test.pkg;\n" +
+                "\n" +
+                "import java.io.BufferedReader;\n" +
+                "import java.io.FileReader;\n" +
+                "import java.io.IOException;\n" +
+                "import java.lang.reflect.InvocationTargetException;\n" +
+                "import java.util.List;\n" +
+                "import java.util.Map;\n" +
+                "import java.util.TreeMap;\n" +
+                "\n" +
+                "public class Java7LanguageFeatureTest {\n" +
+                "    public void testDiamondOperator() {\n" +
+                "        Map<String, List<Integer>> map = new TreeMap<>();\n" +
+                "    }\n" +
+                "\n" +
+                "    public int testStringSwitches(String value) {\n" +
+                "        final String first = \"first\";\n" +
+                "        final String second = \"second\";\n" +
+                "\n" +
+                "        switch (value) {\n" +
+                "            case first:\n" +
+                "                return 41;\n" +
+                "            case second:\n" +
+                "                return 42;\n" +
+                "            default:\n" +
+                "                return 0;\n" +
+                "        }\n" +
+                "    }\n" +
+                "\n" +
+                "    public String testTryWithResources(String path) throws IOException {\n" +
+                "        try (BufferedReader br = new BufferedReader(new FileReader(path))) {\n" +
+                "            return br.readLine();\n" +
+                "        }\n" +
+                "    }\n" +
+                "\n" +
+                "    public void testNumericLiterals() {\n" +
+                "        int thousand = 1_000;\n" +
+                "        int million = 1_000_000;\n" +
+                "        int binary = 0B01010101;\n" +
+                "    }\n" +
+                "\n" +
+                "    public void testMultiCatch() {\n" +
+                "\n" +
+                "        try {\n" +
+                "            Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n" +
+                "        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {\n" +
+                "            e.printStackTrace();\n" +
+                "        } catch (ClassNotFoundException e) {\n" +
+                "            // TODO: Logging here\n" +
+                "        }\n" +
+                "    }\n" +
+                "}\n";
+
+        Node unit = LintUtilsTest.getCompilationUnit(testClass);
+        assertNotNull(unit);
+
+        // Now print the AST back and make sure that it contains at least the essence of the AST
+        TextFormatter formatter = new TextFormatter();
+        unit.accept(new SourcePrinter(formatter));
+        String actual = formatter.finish();
+        assertEquals(""
+                + "package test.pkg;\n"
+                + "\n"
+                + "import java.io.BufferedReader;\n"
+                + "import java.io.FileReader;\n"
+                + "import java.io.IOException;\n"
+                + "import java.lang.reflect.InvocationTargetException;\n"
+                + "import java.util.List;\n"
+                + "import java.util.Map;\n"
+                + "import java.util.TreeMap;\n"
+                + "\n"
+                + "public class Java7LanguageFeatureTest {\n"
+                + "    public void testDiamondOperator() {\n"
+                + "        Map<String, List<Integer>> map = new TreeMap();\n" // missing types on rhs
+                + "    }\n"
+                + "    \n"
+                + "    public int testStringSwitches(String value) {\n"
+                + "        final String first = \"first\";\n"
+                + "        final String second = \"second\";\n"
+                + "        switch (value) {\n"
+                + "        case first:\n"
+                + "            return 41;\n"
+                + "        case second:\n"
+                + "            return 42;\n"
+                + "        default:\n"
+                + "            return 0;\n"
+                + "        }\n"
+                + "    }\n"
+                + "    \n"
+                + "    public String testTryWithResources(String path) throws IOException {\n"
+                + "        try {\n" // Note how the initialization clause is gone here
+                + "            return br.readLine();\n"
+                + "        }\n"
+                + "    }\n"
+                + "    \n"
+                + "    public void testNumericLiterals() {\n"
+                + "        int thousand = 1_000;\n"
+                + "        int million = 1_000_000;\n"
+                + "        int binary = 0B01010101;\n"
+                + "    }\n"
+                + "    \n"
+                + "    public void testMultiCatch() {\n"
+                + "        try {\n"
+                + "            Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n"
+                + "        } catch (IllegalAccessException e) {\n" // Note: missing other union types
+                + "            e.printStackTrace();\n"
+                + "        } catch (ClassNotFoundException e) {\n"
+                + "        }\n"
+                + "    }\n"
+                + "}",
+                actual);
+    }
+
+    @SuppressWarnings("ClassNameDiffersFromFileName")
+    public void testGetFields() throws Exception {
+        @Language("JAVA")
+        String source = ""
+                + "public class FieldTest {\n"
+                + "    public int field1 = 1;\n"
+                + "    public int field2 = 3;\n"
+                + "    public int field3 = 5;\n"
+                + "    \n"
+                + "    public static class Inner extends FieldTest {\n"
+                + "        public int field2 = 5;\n"
+                + "    }\n"
+                + "}\n";
+
+        final JavaContext context = LintUtilsTest.parse(source,
+                new File("src/test/pkg/FieldTest.java"));
+        assertNotNull(context);
+
+        Node compilationUnit = context.getCompilationUnit();
+        assertNotNull(compilationUnit);
+        final AtomicBoolean found = new AtomicBoolean();
+        compilationUnit.accept(new ForwardingAstVisitor() {
+            @Override
+            public boolean visitClassDeclaration(ClassDeclaration node) {
+                if (node.astName().astValue().equals("Inner")) {
+                    found.set(true);
+                    ResolvedNode resolved = context.resolve(node);
+                    assertNotNull(resolved);
+                    ResolvedClass cls = (ResolvedClass) resolved;
+                    List<ResolvedField> declaredFields = Lists.newArrayList(cls.getFields(false));
+                    assertEquals(1, declaredFields.size());
+                    assertEquals("field2", declaredFields.get(0).getName());
+
+                    declaredFields = Lists.newArrayList(cls.getFields(true));
+                    assertEquals(3, declaredFields.size());
+                    assertEquals("field2", declaredFields.get(0).getName());
+                    assertEquals("FieldTest.Inner", declaredFields.get(0).getContainingClassName());
+                    assertEquals("field1", declaredFields.get(1).getName());
+                    assertEquals("FieldTest", declaredFields.get(1).getContainingClassName());
+                    assertEquals("field3", declaredFields.get(2).getName());
+                    assertEquals("FieldTest", declaredFields.get(2).getContainingClassName());
+                }
+
+                return super.visitClassDeclaration(node);
+            }
+        });
+        assertTrue(found.get());
+    }
+
+    public void testResolution() throws Exception {
+        String source =
+                "package test.pkg;\n" +
+                "\n" +
+                "import java.io.File;\n" +
+                "\n" +
+                "public class TypeResolutionTest {\n" +
+                "    public static class Inner extends File {\n" +
+                "        public float myField = 5f;\n" +
+                "        public int[] myInts;\n" +
+                "\n" +
+                "        public Inner(File dir, String name) {\n" +
+                "            super(dir, name);\n" +
+                "        }\n" +
+                "\n" +
+                "        public void call(int arg1, double arg2) {\n" +
+                "            boolean x = super.canRead();\n" +
+                "            System.out.println(x);\n" +
+                "        }\n" +
+                "    }\n" +
+                "\n" +
+                "    @SuppressWarnings(\"all\")\n" +
+                "    public static class Other {\n" +
+                "         private void client(int z) {\n" +
+                "             int x = z;\n" +
+                "             int y = x + 5;\n" +
+                "             Inner inner = new Inner(null, null);\n" +
+                "             inner.myField = 6;\n" +
+                "             System.out.println(inner.myInts);\n" +
+                "         }\n" +
+                "    }\n" +
+                "}\n";
+
+        Node unit = LintUtilsTest.getCompilationUnit(source,
+                new File("src/test/pkg/TypeResolutionTest.java"));
+
+        // Visit all nodes and assert nativeNode != null unless I expect it!
+        unit.accept(new ForwardingAstVisitor() {
+            @SuppressWarnings("Contract")
+            @Override
+            public boolean visitNode(Node node) {
+                if (node.getNativeNode() == null && requiresNativeNode(node)) {
+                    fail("Expected native node on node of type " +
+                                    node.getClass().getSimpleName());
+                }
+                return super.visitNode(node);
+            }
+
+            private boolean requiresNativeNode(Node node) {
+                if (node instanceof TypeReferencePart &&
+                        node.getParent().getNativeNode() != null) {
+                    return false;
+                }
+
+                if (node instanceof Identifier
+                        || node instanceof NormalTypeBody
+                        || node instanceof Block
+                        || node instanceof VariableDeclaration
+                        || node instanceof VariableDefinition
+                        || node instanceof AnnotationElement
+                        || node instanceof BinaryExpression
+                        || node instanceof Modifiers
+                        || node instanceof KeywordModifier) {
+                    return false;
+                }
+
+                if (node instanceof VariableReference) {
+                    VariableReference reference = (VariableReference)node;
+                    if (reference.getParent() instanceof Select) {
+                        return false;
+                    }
+                } else if (node instanceof MethodInvocation) {
+                    Node parent = node.getParent();
+                    if (parent instanceof ExpressionStatement &&
+                            parent.getNativeNode() != null) {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        });
+
+        JavaParser parser = new EcjParser(new LintCliClient(), null);
+        AstPrettyPrinter astPrettyPrinter = new AstPrettyPrinter(parser);
+        unit.accept(new SourcePrinter(astPrettyPrinter));
+        String actual = astPrettyPrinter.finish();
+        assertEquals(
+                "[CompilationUnit]\n" +
+                "  [PackageDeclaration]\n" +
+                "    [Identifier test]\n" +
+                "      PROPERTY: name = test\n" +
+                "    [Identifier pkg]\n" +
+                "      PROPERTY: name = pkg\n" +
+                "  [ImportDeclaration]\n" +
+                "    PROPERTY: static = false\n" +
+                "    PROPERTY: star = false\n" +
+                "    [Identifier java]\n" +
+                "      PROPERTY: name = java\n" +
+                "    [Identifier io]\n" +
+                "      PROPERTY: name = io\n" +
+                "    [Identifier File]\n" +
+                "      PROPERTY: name = File\n" +
+                "  [ClassDeclaration TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+                "    [Modifiers], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+                "      [KeywordModifier public]\n" +
+                "        PROPERTY: modifier = public\n" +
+                "    typeName: [Identifier TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+                "      PROPERTY: name = TypeResolutionTest\n" +
+                "    [NormalTypeBody], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+                "        [ClassDeclaration Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "          [Modifiers], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "            [KeywordModifier public]\n" +
+                "              PROPERTY: modifier = public\n" +
+                "            [KeywordModifier static]\n" +
+                "              PROPERTY: modifier = static\n" +
+                "          typeName: [Identifier Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "            PROPERTY: name = Inner\n" +
+                "          extends: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
+                "            PROPERTY: WildcardKind = NONE\n" +
+                "            PROPERTY: arrayDimensions = 0\n" +
+                "            [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
+                "              [Identifier File]\n" +
+                "                PROPERTY: name = File\n" +
+                "          [NormalTypeBody], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "              [VariableDeclaration], type: float, resolved class: float \n" +
+                "                [VariableDefinition]\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                    [KeywordModifier public]\n" +
+                "                      PROPERTY: modifier = public\n" +
+                "                  type: [TypeReference float], type: float, resolved class: float \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    [TypeReferencePart], type: float, resolved class: float \n" +
+                "                      [Identifier float]\n" +
+                "                        PROPERTY: name = float\n" +
+                "                  [VariableDefinitionEntry], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier myField], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
+                "                      PROPERTY: name = myField\n" +
+                "                    [FloatingPointLiteral 5.0], type: float\n" +
+                "                      PROPERTY: value = 5f\n" +
+                "              [VariableDeclaration], type: int[], resolved class: int[] \n" +
+                "                [VariableDefinition]\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                    [KeywordModifier public]\n" +
+                "                      PROPERTY: modifier = public\n" +
+                "                  type: [TypeReference int[]], type: int[], resolved class: int[] \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 1\n" +
+                "                    [TypeReferencePart], type: int[], resolved class: int[] \n" +
+                "                      [Identifier int]\n" +
+                "                        PROPERTY: name = int\n" +
+                "                  [VariableDefinitionEntry], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier myInts], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
+                "                      PROPERTY: name = myInts\n" +
+                "              [ConstructorDeclaration], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                [Modifiers], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                  [KeywordModifier public]\n" +
+                "                    PROPERTY: modifier = public\n" +
+                "                typeName: [Identifier Inner], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                  PROPERTY: name = Inner\n" +
+                "                parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                  type: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
+                "                      [Identifier File]\n" +
+                "                        PROPERTY: name = File\n" +
+                "                  [VariableDefinitionEntry], resolved variable: dir java.io.File\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier dir], resolved variable: dir java.io.File\n" +
+                "                      PROPERTY: name = dir\n" +
+                "                parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                  type: [TypeReference String], type: java.lang.String, resolved class: java.lang.String \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    [TypeReferencePart], type: java.lang.String, resolved class: java.lang.String \n" +
+                "                      [Identifier String]\n" +
+                "                        PROPERTY: name = String\n" +
+                "                  [VariableDefinitionEntry], resolved variable: name java.lang.String\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier name], resolved variable: name java.lang.String\n" +
+                "                      PROPERTY: name = name\n" +
+                "                [Block], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                    [SuperConstructorInvocation], resolved method: java.io.File java.io.File\n" +
+                "                      [VariableReference], type: java.io.File, resolved variable: dir java.io.File\n" +
+                "                        [Identifier dir], type: java.io.File, resolved variable: dir java.io.File\n" +
+                "                          PROPERTY: name = dir\n" +
+                "                      [VariableReference], type: java.lang.String, resolved variable: name java.lang.String\n" +
+                "                        [Identifier name], type: java.lang.String, resolved variable: name java.lang.String\n" +
+                "                          PROPERTY: name = name\n" +
+                "              [MethodDeclaration call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+                "                [Modifiers], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+                "                  [KeywordModifier public]\n" +
+                "                    PROPERTY: modifier = public\n" +
+                "                returnType: [TypeReference void], type: void, resolved class: void \n" +
+                "                  PROPERTY: WildcardKind = NONE\n" +
+                "                  PROPERTY: arrayDimensions = 0\n" +
+                "                  [TypeReferencePart], type: void, resolved class: void \n" +
+                "                    [Identifier void]\n" +
+                "                      PROPERTY: name = void\n" +
+                "                methodName: [Identifier call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+                "                  PROPERTY: name = call\n" +
+                "                parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                  type: [TypeReference int], type: int, resolved class: int \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    [TypeReferencePart], type: int, resolved class: int \n" +
+                "                      [Identifier int]\n" +
+                "                        PROPERTY: name = int\n" +
+                "                  [VariableDefinitionEntry], resolved variable: arg1 int\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier arg1], resolved variable: arg1 int\n" +
+                "                      PROPERTY: name = arg1\n" +
+                "                parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                  type: [TypeReference double], type: double, resolved class: double \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    [TypeReferencePart], type: double, resolved class: double \n" +
+                "                      [Identifier double]\n" +
+                "                        PROPERTY: name = double\n" +
+                "                  [VariableDefinitionEntry], resolved variable: arg2 double\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier arg2], resolved variable: arg2 double\n" +
+                "                      PROPERTY: name = arg2\n" +
+                "                [Block], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+                "                    [VariableDeclaration], type: boolean, resolved class: boolean \n" +
+                "                      [VariableDefinition]\n" +
+                "                        PROPERTY: varargs = false\n" +
+                "                        [Modifiers]\n" +
+                "                        type: [TypeReference boolean], type: boolean, resolved class: boolean \n" +
+                "                          PROPERTY: WildcardKind = NONE\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          [TypeReferencePart], type: boolean, resolved class: boolean \n" +
+                "                            [Identifier boolean]\n" +
+                "                              PROPERTY: name = boolean\n" +
+                "                        [VariableDefinitionEntry], resolved variable: x boolean\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          varName: [Identifier x], resolved variable: x boolean\n" +
+                "                            PROPERTY: name = x\n" +
+                "                          [MethodInvocation canRead], type: boolean, resolved method: canRead java.io.File\n" +
+                "                            operand: [Super], type: java.io.File\n" +
+                "                            methodName: [Identifier canRead], type: boolean, resolved method: canRead java.io.File\n" +
+                "                              PROPERTY: name = canRead\n" +
+                "                    [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
+                "                      [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
+                "                        operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+                "                          operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+                "                            [Identifier System]\n" +
+                "                              PROPERTY: name = System\n" +
+                "                          selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+                "                            PROPERTY: name = out\n" +
+                "                        methodName: [Identifier println]\n" +
+                "                          PROPERTY: name = println\n" +
+                "                        [VariableReference], type: boolean, resolved variable: x boolean\n" +
+                "                          [Identifier x], type: boolean, resolved variable: x boolean\n" +
+                "                            PROPERTY: name = x\n" +
+                "        [ClassDeclaration Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+                "          [Modifiers], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+                "            [Annotation SuppressWarnings], type: java.lang.SuppressWarnings, resolved annotation: java.lang.SuppressWarnings \n" +
+                "              [TypeReference SuppressWarnings], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
+                "                PROPERTY: WildcardKind = NONE\n" +
+                "                PROPERTY: arrayDimensions = 0\n" +
+                "                [TypeReferencePart], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
+                "                  [Identifier SuppressWarnings]\n" +
+                "                    PROPERTY: name = SuppressWarnings\n" +
+                "              [AnnotationElement null], type: java.lang.SuppressWarnings, resolved annotation: java.lang.SuppressWarnings \n" +
+                "                [StringLiteral all], type: java.lang.String\n" +
+                "                  PROPERTY: value = \"all\"\n" +
+                "            [KeywordModifier public]\n" +
+                "              PROPERTY: modifier = public\n" +
+                "            [KeywordModifier static]\n" +
+                "              PROPERTY: modifier = static\n" +
+                "          typeName: [Identifier Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+                "            PROPERTY: name = Other\n" +
+                "          [NormalTypeBody], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+                "              [MethodDeclaration client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+                "                [Modifiers], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+                "                  [KeywordModifier private]\n" +
+                "                    PROPERTY: modifier = private\n" +
+                "                returnType: [TypeReference void], type: void, resolved class: void \n" +
+                "                  PROPERTY: WildcardKind = NONE\n" +
+                "                  PROPERTY: arrayDimensions = 0\n" +
+                "                  [TypeReferencePart], type: void, resolved class: void \n" +
+                "                    [Identifier void]\n" +
+                "                      PROPERTY: name = void\n" +
+                "                methodName: [Identifier client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+                "                  PROPERTY: name = client\n" +
+                "                parameter: [VariableDefinition], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+                "                  PROPERTY: varargs = false\n" +
+                "                  [Modifiers]\n" +
+                "                  type: [TypeReference int], type: int, resolved class: int \n" +
+                "                    PROPERTY: WildcardKind = NONE\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    [TypeReferencePart], type: int, resolved class: int \n" +
+                "                      [Identifier int]\n" +
+                "                        PROPERTY: name = int\n" +
+                "                  [VariableDefinitionEntry], resolved variable: z int\n" +
+                "                    PROPERTY: arrayDimensions = 0\n" +
+                "                    varName: [Identifier z], resolved variable: z int\n" +
+                "                      PROPERTY: name = z\n" +
+                "                [Block], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+                "                    [VariableDeclaration], type: int, resolved class: int \n" +
+                "                      [VariableDefinition]\n" +
+                "                        PROPERTY: varargs = false\n" +
+                "                        [Modifiers]\n" +
+                "                        type: [TypeReference int], type: int, resolved class: int \n" +
+                "                          PROPERTY: WildcardKind = NONE\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          [TypeReferencePart], type: int, resolved class: int \n" +
+                "                            [Identifier int]\n" +
+                "                              PROPERTY: name = int\n" +
+                "                        [VariableDefinitionEntry], resolved variable: x int\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          varName: [Identifier x], resolved variable: x int\n" +
+                "                            PROPERTY: name = x\n" +
+                "                          [VariableReference], type: int, resolved variable: z int\n" +
+                "                            [Identifier z], type: int, resolved variable: z int\n" +
+                "                              PROPERTY: name = z\n" +
+                "                    [VariableDeclaration], type: int, resolved class: int \n" +
+                "                      [VariableDefinition]\n" +
+                "                        PROPERTY: varargs = false\n" +
+                "                        [Modifiers]\n" +
+                "                        type: [TypeReference int], type: int, resolved class: int \n" +
+                "                          PROPERTY: WildcardKind = NONE\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          [TypeReferencePart], type: int, resolved class: int \n" +
+                "                            [Identifier int]\n" +
+                "                              PROPERTY: name = int\n" +
+                "                        [VariableDefinitionEntry], resolved variable: y int\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          varName: [Identifier y], resolved variable: y int\n" +
+                "                            PROPERTY: name = y\n" +
+                "                          [BinaryExpression +], type: int\n" +
+                "                            PROPERTY: operator = +\n" +
+                "                            left: [VariableReference], type: int, resolved variable: x int\n" +
+                "                              [Identifier x], type: int, resolved variable: x int\n" +
+                "                                PROPERTY: name = x\n" +
+                "                            right: [IntegralLiteral 5], type: int\n" +
+                "                              PROPERTY: value = 5\n" +
+                "                    [VariableDeclaration], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "                      [VariableDefinition]\n" +
+                "                        PROPERTY: varargs = false\n" +
+                "                        [Modifiers]\n" +
+                "                        type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "                          PROPERTY: WildcardKind = NONE\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "                            [Identifier Inner]\n" +
+                "                              PROPERTY: name = Inner\n" +
+                "                        [VariableDefinitionEntry], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                          PROPERTY: arrayDimensions = 0\n" +
+                "                          varName: [Identifier inner], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                            PROPERTY: name = inner\n" +
+                "                          [ConstructorInvocation Inner], type: test.pkg.TypeResolutionTest.Inner, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                            type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "                              PROPERTY: WildcardKind = NONE\n" +
+                "                              PROPERTY: arrayDimensions = 0\n" +
+                "                              [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+                "                                [Identifier Inner]\n" +
+                "                                  PROPERTY: name = Inner\n" +
+                "                            [NullLiteral], type: null\n" +
+                "                            [NullLiteral], type: null\n" +
+                "                    [ExpressionStatement], type: float\n" +
+                "                      [BinaryExpression =], type: float\n" +
+                "                        PROPERTY: operator = =\n" +
+                "                        left: [Select], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                          operand: [VariableReference], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                            [Identifier inner]\n" +
+                "                              PROPERTY: name = inner\n" +
+                "                          selected: [Identifier myField], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                            PROPERTY: name = myField\n" +
+                "                        right: [IntegralLiteral 6], type: int\n" +
+                "                          PROPERTY: value = 6\n" +
+                "                    [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
+                "                      [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
+                "                        operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+                "                          operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+                "                            [Identifier System]\n" +
+                "                              PROPERTY: name = System\n" +
+                "                          selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+                "                            PROPERTY: name = out\n" +
+                "                        methodName: [Identifier println]\n" +
+                "                          PROPERTY: name = println\n" +
+                "                        [Select], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                          operand: [VariableReference], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                            [Identifier inner]\n" +
+                "                              PROPERTY: name = inner\n" +
+                "                          selected: [Identifier myInts], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+                "                            PROPERTY: name = myInts\n",
+                actual);
+    }
+
+    public void testStartsWithCompound() throws Exception {
+        assertTrue(startsWithCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray()
+                }));
+
+        assertTrue(startsWithCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray(),
+                        "other".toCharArray(),
+                }));
+
+        assertFalse(startsWithCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "other".toCharArray()
+                }));
+
+        // Corner cases
+        assertFalse(startsWithCompound("test.pk",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray(),
+                }));
+        assertFalse(startsWithCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray()
+                }));
+        assertTrue(startsWithCompound("test.",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray()
+                }));
+        assertFalse(startsWithCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pk".toCharArray()
+                }));
+    }
+
+    public void testEqualsWithCompound() throws Exception {
+        assertTrue(equalsCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray()
+                }));
+
+        assertFalse(equalsCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray(),
+                        "other".toCharArray(),
+                }));
+
+        assertFalse(equalsCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "other".toCharArray()
+                }));
+
+        // Corner cases
+        assertFalse(equalsCompound("test.pk",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray(),
+                }));
+        assertFalse(equalsCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray()
+                }));
+        assertFalse(equalsCompound("test.",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pkg".toCharArray()
+                }));
+        assertFalse(equalsCompound("test.pkg",
+                new char[][]{
+                        "test".toCharArray(),
+                        "pk".toCharArray()
+                }));
+    }
+
+    @Override
+    protected Detector getDetector() {
+        return new SdCardDetector();
+    }
+
+    @Override
+    protected TestLintClient createClient() {
+        return new TestLintClient() {
+            @NonNull
+            @Override
+            protected ClassPathInfo getClassPath(@NonNull Project project) {
+                ClassPathInfo classPath = super.getClassPath(project);
+                // Insert fake classpath entries (non existent directories) to
+                // make sure the parser handles that gracefully. See issue 87740.
+                classPath.getLibraries().add(new File("nonexistent path"));
+                return classPath;
+            }
+        };
+    }
+
+    public static class AstPrettyPrinter implements SourceFormatter {
+
+        private final StringBuilder mOutput = new StringBuilder(1000);
+
+        private final JavaParser mResolver;
+
+        private int mIndent;
+
+        private String mName;
+
+        public AstPrettyPrinter(JavaParser resolver) {
+            mResolver = resolver;
+        }
+
+        private void add(String in, Object... args) {
+            for (int i = 0; i < mIndent; i++) {
+                mOutput.append("  ");
+            }
+            if (mName != null) {
+                mOutput.append(mName).append(": ");
+                mName = null;
+            }
+            if (args.length == 0) {
+                mOutput.append(in);
+            } else {
+                mOutput.append(String.format(in, args));
+            }
+        }
+
+        @Override
+        public void buildInline(Node node) {
+            buildNode(node);
+        }
+
+        @Override
+        public void buildBlock(Node node) {
+            buildNode(node);
+        }
+
+        private void buildNode(Node node) {
+            if (node == null) {
+                mIndent++;
+                return;
+            }
+            String name = node.getClass().getSimpleName();
+            String description = "";
+            if (node instanceof DescribedNode) {
+                description = " " + ((DescribedNode) node).getDescription();
+            }
+
+            String typeDescription = "";
+            String resolutionDescription = "";
+            JavaParser.TypeDescriptor t = mResolver.getType(null, node);
+            if (t != null) {
+                typeDescription = ", type: " + t.getName();
+            }
+            ResolvedNode resolved = mResolver.resolve(null, node);
+            if (resolved != null) {
+                String c = "unknown";
+                String extra = "";
+                if (resolved instanceof ResolvedClass) {
+                    c = "class";
+                } else if (resolved instanceof ResolvedMethod) {
+                    c = "method";
+                    ResolvedMethod method = (ResolvedMethod) resolved;
+                    extra = method.getContainingClass().getName();
+                } else if (resolved instanceof ResolvedField) {
+                    c = "field";
+                    ResolvedField field = (ResolvedField) resolved;
+                    extra = field.getContainingClass().getName();
+                } else if (resolved instanceof ResolvedVariable) {
+                    c = "variable";
+                    ResolvedVariable variable = (ResolvedVariable) resolved;
+                    extra = variable.getType().getName();
+                } else if (resolved instanceof ResolvedAnnotation) {
+                    c = "annotation";
+                }
+                resolutionDescription = String.format(", resolved %1$s: %2$s %3$s",
+                        c, resolved.getName(), extra);
+            }
+
+            add("[%1$s%2$s]%3$s%4$s\n", name, description, typeDescription, resolutionDescription);
+
+            mIndent++;
+        }
+
+        @Override
+        public void fail(String fail) {
+            Assert.fail(fail);
+        }
+
+        @Override
+        public void property(String name, Object value) {
+            add("PROPERTY: %s = %s\n", name, value);
+        }
+
+        @Override
+        public void keyword(String text) {
+        }
+
+        @Override
+        public void operator(String text) {
+        }
+
+        @Override
+        public void verticalSpace() {
+        }
+
+        @Override
+        public void space() {
+        }
+
+        @Override
+        public void append(String text) {
+        }
+
+        @Override
+        public void startSuppressBlock() {
+        }
+
+        @Override
+        public void endSuppressBlock() {
+        }
+
+        @Override
+        public void startSuppressIndent() {
+        }
+
+        @Override
+        public void endSuppressIndent() {
+        }
+
+        @Override
+        public void closeInline() {
+            mIndent--;
+        }
+
+        @Override
+        public void closeBlock() {
+            mIndent--;
+        }
+
+        @Override
+        public void addError(int start, int end, String message) {
+            fail(message);
+        }
+
+        @Override
+        public String finish() {
+            return mOutput.toString();
+        }
+
+        @Override
+        public void setTimeTaken(long taken) {
+        }
+
+        @Override
+        public void nameNextElement(String name) {
+            mName = name;
+        }
+    }
+}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
new file mode 100644
index 0000000..774a4c8
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2015 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.tools.lint;
+
+import static com.android.tools.lint.ExternalAnnotationRepository.FN_ANNOTATIONS_XML;
+import static com.google.common.base.Charsets.UTF_8;
+import static java.io.File.separatorChar;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.testutils.SdkTestCase;
+import com.android.tools.lint.client.api.JavaParser.DefaultTypeDescriptor;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
+import com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaParser.ResolvedPackage;
+import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtilsTest;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.ast.ClassDeclaration;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.MethodDeclaration;
+import lombok.ast.MethodInvocation;
+import lombok.ast.Node;
+
+public class ExternalAnnotationRepositoryTest extends SdkTestCase {
+
+    @Nullable
+    private ExternalAnnotationRepository getSdkAnnotations() {
+        File annotations = findSrcRelativeDir("tools/adt/idea/android/annotations");
+        if (annotations != null) {
+            List<File> files = Collections.singletonList(annotations);
+            ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
+            assertNotNull(manager);
+            return manager;
+        } else {
+            // Can't find it when running from Gradle; ignore for now
+            //fail("Could not find annotations database");
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private ExternalAnnotationRepository getExternalAnnotations(@NonNull String pkg,
+            @NonNull String contents) throws IOException {
+        File dir = Files.createTempDir();
+        try {
+            File pkgDir = new File(dir, pkg.replace('.', separatorChar));
+            boolean mkdirs = pkgDir.mkdirs();
+            assertTrue(mkdirs);
+            Files.write(contents, new File(pkgDir, FN_ANNOTATIONS_XML), UTF_8);
+
+            List<File> files = Collections.singletonList(dir);
+            ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
+            assertNotNull(manager);
+            return manager;
+
+        } finally {
+            deleteFile(dir);
+        }
+    }
+
+    public void testFields() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"android.graphics.Color\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation1\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Color BLUE\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Color TRANSPARENT\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "</root>\n");
+        assertNotNull(manager);
+        ResolvedClass cls = createClass("android.graphics.Color");
+        assertNotNull(manager.getAnnotation(cls, "android.support.annotation.Annotation1"));
+        ResolvedField blueField = createField("android.graphics.Color", "BLUE");
+        ResolvedField transparentField = createField("android.graphics.Color", "TRANSPARENT");
+        assertNotNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation3"));
+        assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation4"));
+        assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation5"));
+
+        assertNotNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation5"));
+        assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation3"));
+        assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation4"));
+    }
+
+    public void testMethods1() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
+                + "      <val name=\"value\" val=\"3\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Color int alpha(int)\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Color int argb(int, int, int, int)\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.ArrayAdapter ArrayAdapter(android.content.Context, int, int, java.util.List&lt;T&gt;) 3\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
+                + "  </item>"
+                + "</root>\n");
+        assertNotNull(manager);
+        ResolvedMethod method1 = createMethod("android.graphics.Color", "int", "HSVToColor",
+                "float[]");
+        assertNotNull(manager.getAnnotation(method1, 0, "android.support.annotation.Annotation5"));
+
+        // Generic types
+        ResolvedMethod method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
+                "android.content.Context, int, int, java.util.List<T>");
+        assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
+
+        // Raw types
+        method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
+                "android.content.Context, int, int, java.util.List");
+        assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
+    }
+
+    public void testMethods2() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod()\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "    <annotation name=\"android.support.annotation.Annotation6\">\n"
+                + "      <val name=\"suggest\" val=\"&quot;#other(String,String)&quot;\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(int) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
+                + "      <val name=\"value\" val=\"3\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(int[]) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(int,java.lang.Object) 1\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List&lt;T&gt;) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List&lt;T&gt;) 1\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"test.pkg.Test java.lang.Object myMethod(java.util.Map&lt;java.lang.String,java.util.Map&lt;java.lang.String,java.lang.String&gt;&gt;,int) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
+                + "  </item>\n"
+                + "</root>\n");
+        assertNotNull(manager);
+        ResolvedMethod method;
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "");
+        assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+        assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation6"));
+
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int");
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
+
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int[]");
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation3"));
+        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
+
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+                "int,java.lang.Object");
+        assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
+
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+                "android.content.Context, int, int, java.util.List<T>");
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
+        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
+        assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
+        assertNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
+
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+                "android.content.Context, int, int, java.util.List");
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
+        assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
+
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+                Arrays.asList("java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>",
+                        "int"), false);
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+        method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+                "java.util.Map,int");
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+    }
+
+    // test intdef!
+
+
+    public void testAnnotationAttributes() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
+                + "      <val name=\"value\" val=\"3\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
+                + "      <val name=\"value\" val=\"3\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Canvas void drawLines(float[], android.graphics.Paint) 0\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation7\">\n"
+                + "      <val name=\"min\" val=\"4\" />\n"
+                + "      <val name=\"multiple\" val=\"2\" />\n"
+                + "    </annotation>\n"
+                + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Canvas int saveLayer(android.graphics.RectF, android.graphics.Paint, int) 2\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation8\">\n"
+                + "      <val name=\"value\" val=\"{android.graphics.Canvas.MATRIX_SAVE_FLAG, android.graphics.Canvas.CLIP_SAVE_FLAG, android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG, android.graphics.Canvas.FULL_COLOR_LAYER_SAVE_FLAG, android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG, android.graphics.Canvas.ALL_SAVE_FLAG}\" />\n"
+                + "      <val name=\"flag\" val=\"true\" />\n"
+                + "    </annotation>\n"
+                + "  </item>\n"
+                + "</root>\n");
+        assertNotNull(manager);
+        ResolvedMethod method;
+        ResolvedAnnotation annotation;
+
+        // Size 1
+        method = createMethod("android.graphics.Color", "int", "HSVToColor", "int, float[]");
+        assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation7"));
+        assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
+        annotation = manager.getAnnotation(method, 1, "android.support.annotation.Annotation7");
+        assertNotNull(annotation);
+        assertEquals(3L, annotation.getValue());
+        assertEquals(3L, annotation.getValue("value"));
+        //noinspection ConstantConditions
+        assertEquals(3, ((Number)annotation.getValue("value")).intValue());
+        assertNotNull(annotation);
+
+        // Size 2
+        method = createMethod("android.graphics.Canvas", "void", "drawLines", "float[], android.graphics.Paint");
+        assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+        annotation = manager.getAnnotation(method, 0, "android.support.annotation.Annotation7");
+        assertNotNull(annotation);
+        assertEquals(4L, annotation.getValue("min"));
+        assertEquals(2L, annotation.getValue("multiple"));
+        assertNotNull(annotation);
+
+        // Intdef
+        method = createMethod("android.graphics.Canvas", "int", "saveLayer",
+                "android.graphics.RectF, android.graphics.Paint, int");
+        annotation = manager.getAnnotation(method, 2, "android.support.annotation.Annotation8");
+        assertNotNull(annotation);
+        assertEquals(true, annotation.getValue("flag"));
+        Object[] values = (Object[]) annotation.getValue("value");
+        assertNotNull(values);
+        assertEquals(6, values.length);
+        assertTrue(values[0] instanceof ResolvedField);
+        assertFalse(values[0].equals(createField("android.graphics.Canvas", "WRONG_NAME")));
+        assertEquals(values[0], createField("android.graphics.Canvas", "MATRIX_SAVE_FLAG"));
+        assertEquals(values[1], createField("android.graphics.Canvas", "CLIP_SAVE_FLAG"));
+        assertEquals(values[2], createField("android.graphics.Canvas", "HAS_ALPHA_LAYER_SAVE_FLAG"));
+        assertEquals(values[3], createField("android.graphics.Canvas", "FULL_COLOR_LAYER_SAVE_FLAG"));
+        assertEquals(values[4], createField("android.graphics.Canvas", "CLIP_TO_LAYER_SAVE_FLAG"));
+        assertEquals(values[5], createField("android.graphics.Canvas", "ALL_SAVE_FLAG"));
+
+        ResolvedField field = (ResolvedField)values[0];
+        assertEquals("android.graphics.Canvas.MATRIX_SAVE_FLAG", field.getSignature());
+        assertEquals("android.graphics.Canvas", field.getContainingClassName());
+        assertEquals("MATRIX_SAVE_FLAG", field.getName());
+    }
+
+    public void testConstructors() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "</root>\n");
+        assertNotNull(manager);
+        ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
+                "RadialGradient",
+                "float, float, float, int[], float[], android.graphics.Shader.TileMode");
+        assertNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation4"));
+        assertNotNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation5"));
+    }
+
+    public void testVarArgs() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int...) 3\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "  <item name=\"android.graphics.Bitmap android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]) 2\">\n"
+                + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                + "  </item>\n"
+                + "</root>\n");
+        assertNotNull(manager);
+        // Match "..." in external annotation with ... in code lookup
+        ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
+                "RadialGradient",
+                "float, float, float, int...");
+        assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
+        // Match "..." in external annotation with [] in code lookup
+        method = createConstructor("android.graphics.RadialGradient",
+                "RadialGradient",
+                "float, float, float, int[]");
+        assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
+
+        // Match "[]" in external annotation with [] in code lookup
+        method = createMethod("android.graphics.Bitmap",
+                "android.graphics.Bitmap",
+                "extractAlpha",
+                "android.graphics.Paint, int[]");
+        assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
+
+        // Match "[]" in external annotation with ... in code lookup
+        method = createMethod("android.graphics.Bitmap",
+                "android.graphics.Bitmap",
+                "extractAlpha",
+                "android.graphics.Paint, int...");
+        assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
+    }
+
+    public void testPackage() throws Exception {
+        ExternalAnnotationRepository manager = getExternalAnnotations("foo.bar.baz", ""
+                + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<root>\n"
+                + "  <item name=\"foo.bar.baz.package-info\">\n"
+                + "    <annotation name=\"my.pkg.MyAnnotation\"/>\n"
+                + "  </item>\n"
+                + "</root>\n");
+        assertNotNull(manager);
+        ResolvedClass cls = createClass("foo.bar.baz.AdView");
+        ResolvedPackage pkg = cls.getPackage();
+        assertNotNull(pkg);
+        assertNull(manager.getAnnotation(pkg, "foo.bar.Baz"));
+        assertNotNull(manager.getAnnotation(pkg, "my.pkg.MyAnnotation"));
+    }
+
+    public void testMatchWithEcj() throws Exception {
+        try {
+            ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
+                    + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                    + "<root>\n"
+                    + "  <item name=\"test.pkg.Test\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation1\" />\n"
+                    + "  </item>\n"
+                    + "  <item name=\"test.pkg.Test.Inner\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation2\" />\n"
+                    + "  </item>\n"
+                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...)\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation6\" />\n"
+                    + "  </item>\n"
+                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...) 0\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                    + "  </item>\n"
+                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...) 1\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation3\" />\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation4\" />\n"
+                    + "  </item>\n"
+                    + "  <item name=\"test.pkg.Test void foo(int, int[], int...) 2\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation5\" />\n"
+                    + "  </item>\n"
+                    + "</root>\n");
+            assertNotNull(manager);
+            ExternalAnnotationRepository.set(manager);
+
+            String source =
+                    "package test.pkg;\n" +
+                    "\n" +
+                    "public class Test {\n" +
+                    "    public void foo(int a, int[] b, int... c) {\n" +
+                    "    }\n" +
+                    "    public static class Inner {\n" +
+                    "    }\n" +
+                    "}\n";
+
+            final JavaContext context = LintUtilsTest.parse(source,
+                    new File("src/test/pkg/Test.java"));
+            assertNotNull(context);
+            Node unit = context.getCompilationUnit();
+            assertNotNull(unit);
+            unit.accept(new ForwardingAstVisitor() {
+                @Override
+                public boolean visitClassDeclaration(ClassDeclaration node) {
+                    ResolvedNode resolved = context.resolve(node);
+                    assertNotNull(resolved);
+                    assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedClass);
+                    ResolvedClass cls = (ResolvedClass) resolved;
+                    if (cls.getName().endsWith(".Inner")) {
+                        assertNull(cls.getAnnotation("android.support.annotation.Annotation1"));
+                        assertNotNull(cls.getAnnotation("android.support.annotation.Annotation2"));
+                    } else {
+                        assertNotNull(cls.getAnnotation("android.support.annotation.Annotation1"));
+                        assertNull(cls.getAnnotation("android.support.annotation.Annotation2"));
+                    }
+
+                    return super.visitClassDeclaration(node);
+                }
+
+                @Override
+                public boolean visitMethodDeclaration(MethodDeclaration node) {
+                    ResolvedNode resolved = context.resolve(node);
+                    assertNotNull(resolved);
+                    assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedMethod);
+                    ResolvedMethod method = (ResolvedMethod) resolved;
+                    assertNull(method.getAnnotation("android.support.annotation.Annotation5"));
+                    assertNotNull(method.getAnnotation("android.support.annotation.Annotation6"));
+                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 0));
+                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation4", 1));
+                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 1));
+                    assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation5", 2));
+
+                    return super.visitMethodDeclaration(node);
+                }
+            });
+        } finally {
+            ExternalAnnotationRepository.set(null);
+        }
+    }
+
+    public void testMergeParameters() throws Exception {
+        try {
+            ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
+                    + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                    + "<root>\n"
+                    + "  <item name=\"test.pkg.Test.Parent void testMethod(int)\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation1\" />\n"
+                    + "  </item>\n"
+                    + "  <item name=\"test.pkg.Test.Child void testMethod(int)\">\n"
+                    + "    <annotation name=\"android.support.annotation.Annotation2\" />\n"
+                    + "  </item>\n"
+                    + "</root>\n");
+            assertNotNull(manager);
+            ExternalAnnotationRepository.set(manager);
+
+            String source = ""
+                    + "package test.pkg;\n"
+                    + "\n"
+                    + "public class Test {\n"
+                    + "    public void test(Child child) {\n"
+                    + "        child.testMethod(5);\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    public static class Parent {\n"
+                    + "        public void testMethod(int parameter) {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "\n"
+                    + "    public static class Child extends Parent {\n"
+                    + "        @Override\n"
+                    + "        public void testMethod(int parameter) {\n"
+                    + "        }\n"
+                    + "    }\n"
+                    + "}\n";
+
+            final JavaContext context = LintUtilsTest.parse(source,
+                    new File("src/test/pkg/Test.java"));
+            assertNotNull(context);
+            Node unit = context.getCompilationUnit();
+            assertNotNull(unit);
+            final AtomicBoolean foundMethod = new AtomicBoolean();
+            unit.accept(new ForwardingAstVisitor() {
+                @Override
+                public boolean visitMethodInvocation(MethodInvocation node) {
+                    foundMethod.set(true);
+                    assertEquals("testMethod", node.astName().astValue());
+                    ResolvedNode resolved = context.resolve(node);
+                    assertTrue(resolved instanceof ResolvedMethod);
+                    ResolvedMethod method = (ResolvedMethod)resolved;
+                    List<ResolvedAnnotation> annotations =
+                            Lists.newArrayList(method.getAnnotations());
+                    assertEquals(3, annotations.size());
+                    Collections.sort(annotations,
+                            new Comparator<ResolvedAnnotation>() {
+                                @Override
+                                public int compare(ResolvedAnnotation a1,
+                                        ResolvedAnnotation a2) {
+                                    return a1.getName().compareTo(a2.getName());
+                                }
+                            });
+                    assertEquals("android.support.annotation.Annotation1", annotations.get(0).getName());
+                    assertEquals("android.support.annotation.Annotation2", annotations.get(1).getName());
+                    assertEquals("java.lang.Override", annotations.get(2).getName());
+                    return super.visitMethodInvocation(node);
+                }
+            });
+            assertTrue(foundMethod.get());
+        } finally {
+            ExternalAnnotationRepository.set(null);
+        }
+    }
+
+    public void testSdkAnnotations() throws Exception {
+        ExternalAnnotationRepository manager = getSdkAnnotations();
+        if (manager == null) {
+            // Can't find it when running from Gradle; ignore for now
+            return;
+        }
+        ResolvedMethod method = createMethod("android.view.LayoutInflater", "android.view.View",
+                "createView", "java.lang.String, java.lang.String, android.util.AttributeSet");
+        assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.NonNull"));
+    }
+
+    private static ResolvedClass createClass(String name) {
+        ResolvedClass mock = mock(ResolvedClass.class);
+        when(mock.getName()).thenReturn(name);
+        when(mock.getSignature()).thenReturn(name);
+        assertTrue(name, name.indexOf('.') != -1);
+        ResolvedPackage pkg = createPackage(name.substring(0, name.lastIndexOf('.')));
+        when(mock.getPackage()).thenReturn(pkg);
+        return mock;
+    }
+
+    private static ResolvedMethod createConstructor(String containingClass, String name,
+            String parameters) {
+        return createMethod(containingClass, null, name, parameters, true);
+    }
+
+    public static ResolvedMethod createMethod(String containingClass, String returnType,
+            String name, String parameters) {
+        return createMethod(containingClass, returnType, name, parameters, false);
+    }
+
+    public static ResolvedMethod createMethod(String containingClass, String returnType,
+            String name, String parameters, boolean isConstructor) {
+        return createMethod(containingClass, returnType, name,
+                Splitter.on(',').trimResults().split(parameters), isConstructor);
+    }
+
+    public static ResolvedMethod createMethod(String containingClass, String returnType,
+            String name, Iterable<String> parameters, boolean isConstructor) {
+        ResolvedMethod mock = mock(ResolvedMethod.class);
+        when(mock.isConstructor()).thenReturn(isConstructor);
+        when(mock.getName()).thenReturn(name);
+        if (!isConstructor) {
+            DefaultTypeDescriptor typeDescriptor = new DefaultTypeDescriptor(returnType);
+            when(mock.getReturnType()).thenReturn(typeDescriptor);
+        }
+        ResolvedClass cls = createClass(containingClass);
+        when(mock.getContainingClass()).thenReturn(cls);
+        int index = 0;
+        for (String argument : parameters) {
+            TypeDescriptor typeDescriptor = new DefaultTypeDescriptor(argument);
+            when(mock.getArgumentType(index)).thenReturn(typeDescriptor);
+            index++;
+        }
+        when(mock.getArgumentCount()).thenReturn(index);
+        return mock;
+    }
+
+    public static ResolvedField createField(String containingClass, String name) {
+        ResolvedField mock = mock(ResolvedField.class);
+        when(mock.getName()).thenReturn(name);
+        ResolvedClass cls = createClass(containingClass);
+        when(mock.getContainingClass()).thenReturn(cls);
+        return mock;
+    }
+
+    public static ResolvedPackage createPackage(String pkgName) {
+        ResolvedPackage mock = mock(ResolvedPackage.class);
+        when(mock.getName()).thenReturn(pkgName);
+        return mock;
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/HtmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/HtmlReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/HtmlReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/HtmlReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/MainTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MainTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/MainTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/MainTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/ReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/ReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/ReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/TextReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/TextReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/WarningTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/WarningTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/XmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/XmlReporterTest.java
new file mode 100644
index 0000000..ea2efe8
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/XmlReporterTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.tools.lint;
+
+import com.android.tools.lint.checks.AbstractCheckTest;
+import com.android.tools.lint.checks.HardcodedValuesDetector;
+import com.android.tools.lint.checks.ManifestDetector;
+import com.android.tools.lint.checks.TypographyDetector;
+import com.android.tools.lint.detector.api.DefaultPosition;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.TextFormat;
+import com.android.utils.PositionXmlParser;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("javadoc")
+public class XmlReporterTest extends AbstractCheckTest {
+    public void test() throws Exception {
+        File file = new File(getTargetDir(), "report");
+        try {
+            LintCliClient client = new LintCliClient() {
+                @Override
+                String getRevision() {
+                    return "unittest"; // Hardcode version to keep unit test output stable
+                }
+            };
+            //noinspection ResultOfMethodCallIgnored
+            file.getParentFile().mkdirs();
+            XmlReporter reporter = new XmlReporter(client, file);
+            Project project = Project.create(client, new File("/foo/bar/Foo"),
+                    new File("/foo/bar/Foo"));
+
+            Warning warning1 = new Warning(ManifestDetector.USES_SDK,
+                    "<uses-sdk> tag should specify a target API level (the highest verified " +
+                    "version; when running on later versions, compatibility behaviors may " +
+                    "be enabled) with android:targetSdkVersion=\"?\"",
+                    Severity.WARNING, project);
+            warning1.line = 6;
+            warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
+            warning1.errorLine = "    <uses-sdk android:minSdkVersion=\"8\" />\n    ^\n";
+            warning1.path = "AndroidManifest.xml";
+            warning1.location = Location.create(warning1.file,
+                    new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
+
+            Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
+                    "[I18N] Hardcoded string \"Fooo\", should use @string resource",
+                    Severity.WARNING, project);
+            warning2.line = 11;
+            warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
+            warning2.errorLine = "        android:text=\"Fooo\" />\n" +
+                          "        ~~~~~~~~~~~~~~~~~~~\n";
+            warning2.path = "res/layout/main.xml";
+            warning2.location = Location.create(warning2.file,
+                    new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
+
+            List<Warning> warnings = new ArrayList<Warning>();
+            warnings.add(warning1);
+            warnings.add(warning2);
+
+            reporter.write(0, 2, warnings);
+
+            String report = Files.toString(file, Charsets.UTF_8);
+            assertEquals(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<issues format=\"4\" by=\"lint unittest\">\n" +
+                "\n" +
+                "    <issue\n" +
+                "        id=\"UsesMinSdkAttributes\"\n" +
+                "        severity=\"Warning\"\n" +
+                "        message=\"&lt;uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=&quot;?&quot;\"\n" +
+                "        category=\"Correctness\"\n" +
+                "        priority=\"9\"\n" +
+                "        summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
+                "        explanation=\"The manifest should contain a `&lt;uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
+                "        url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+                "        urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+                "        errorLine1=\"    &lt;uses-sdk android:minSdkVersion=&quot;8&quot; />\"\n" +
+                "        errorLine2=\"    ^\">\n" +
+                "        <location\n" +
+                "            file=\"AndroidManifest.xml\"\n" +
+                "            line=\"7\"\n" +
+                "            column=\"5\"/>\n" +
+                "    </issue>\n" +
+                "\n" +
+                "    <issue\n" +
+                "        id=\"HardcodedText\"\n" +
+                "        severity=\"Warning\"\n" +
+                "        message=\"[I18N] Hardcoded string &quot;Fooo&quot;, should use @string resource\"\n" +
+                "        category=\"Internationalization\"\n" +
+                "        priority=\"5\"\n" +
+                "        summary=\"Hardcoded text\"\n" +
+                "        explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+                "\n" +
+                "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
+                "\n" +
+                "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
+                "\n" +
+                "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
+                "        errorLine1=\"        android:text=&quot;Fooo&quot; />\"\n" +
+                "        errorLine2=\"        ~~~~~~~~~~~~~~~~~~~\">\n" +
+                "        <location\n" +
+                "            file=\"res/layout/main.xml\"\n" +
+                "            line=\"12\"\n" +
+                "            column=\"9\"/>\n" +
+                "    </issue>\n" +
+                "\n" +
+                "</issues>\n",
+                report);
+
+            // Make sure the XML is valid
+            Document document = PositionXmlParser.parse(report);
+            assertNotNull(document);
+            assertEquals(2, document.getElementsByTagName("issue").getLength());
+        } finally {
+            //noinspection ResultOfMethodCallIgnored
+            file.delete();
+        }
+    }
+
+    public void testFullPaths() throws Exception {
+        File file = new File(getTargetDir(), "report");
+        try {
+            LintCliClient client = new LintCliClient() {
+                @Override
+                String getRevision() {
+                    return "unittest"; // Hardcode version to keep unit test output stable
+                }
+            };
+            client.mFlags.setFullPath(true);
+
+            //noinspection ResultOfMethodCallIgnored
+            file.getParentFile().mkdirs();
+            XmlReporter reporter = new XmlReporter(client, file);
+            Project project = Project.create(client, new File("/foo/bar/Foo"),
+                    new File("/foo/bar/Foo"));
+
+            Warning warning1 = new Warning(ManifestDetector.USES_SDK,
+                    "<uses-sdk> tag should specify a target API level (the highest verified " +
+                    "version; when running on later versions, compatibility behaviors may " +
+                    "be enabled) with android:targetSdkVersion=\"?\"",
+                    Severity.WARNING, project);
+            warning1.line = 6;
+            warning1.file = new File("/foo/bar/../Foo/AndroidManifest.xml");
+            warning1.errorLine = "    <uses-sdk android:minSdkVersion=\"8\" />\n    ^\n";
+            warning1.path = "AndroidManifest.xml";
+            warning1.location = Location.create(warning1.file,
+                    new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
+
+            Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
+                    "[I18N] Hardcoded string \"Fooo\", should use @string resource",
+                    Severity.WARNING, project);
+            warning2.line = 11;
+            warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
+            warning2.errorLine = "        android:text=\"Fooo\" />\n" +
+                          "        ~~~~~~~~~~~~~~~~~~~\n";
+            warning2.path = "res/layout/main.xml";
+            warning2.location = Location.create(warning2.file,
+                    new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
+
+            List<Warning> warnings = new ArrayList<Warning>();
+            warnings.add(warning1);
+            warnings.add(warning2);
+
+            reporter.write(0, 2, warnings);
+
+            String report = Files.toString(file, Charsets.UTF_8);
+            assertEquals(
+                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+                "<issues format=\"4\" by=\"lint unittest\">\n" +
+                "\n" +
+                "    <issue\n" +
+                "        id=\"UsesMinSdkAttributes\"\n" +
+                "        severity=\"Warning\"\n" +
+                "        message=\"&lt;uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=&quot;?&quot;\"\n" +
+                "        category=\"Correctness\"\n" +
+                "        priority=\"9\"\n" +
+                "        summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
+                "        explanation=\"The manifest should contain a `&lt;uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
+                "        url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+                "        urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+                "        errorLine1=\"    &lt;uses-sdk android:minSdkVersion=&quot;8&quot; />\"\n" +
+                "        errorLine2=\"    ^\">\n" +
+                "        <location\n" +
+                "            file=\"/foo/Foo/AndroidManifest.xml\"\n" +
+                "            line=\"7\"\n" +
+                "            column=\"5\"/>\n" +
+                "    </issue>\n" +
+                "\n" +
+                "    <issue\n" +
+                "        id=\"HardcodedText\"\n" +
+                "        severity=\"Warning\"\n" +
+                "        message=\"[I18N] Hardcoded string &quot;Fooo&quot;, should use @string resource\"\n" +
+                "        category=\"Internationalization\"\n" +
+                "        priority=\"5\"\n" +
+                "        summary=\"Hardcoded text\"\n" +
+                "        explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+                "\n" +
+                "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
+                "\n" +
+                "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
+                "\n" +
+                "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
+                "        errorLine1=\"        android:text=&quot;Fooo&quot; />\"\n" +
+                "        errorLine2=\"        ~~~~~~~~~~~~~~~~~~~\">\n" +
+                "        <location\n" +
+                "            file=\"/foo/bar/Foo/res/layout/main.xml\"\n" +
+                "            line=\"12\"\n" +
+                "            column=\"9\"/>\n" +
+                "    </issue>\n" +
+                "\n" +
+                "</issues>\n",
+                report);
+
+            // Make sure the XML is valid
+            Document document = PositionXmlParser.parse(report);
+            assertNotNull(document);
+            assertEquals(2, document.getElementsByTagName("issue").getLength());
+        } finally {
+            //noinspection ResultOfMethodCallIgnored
+            file.delete();
+        }
+    }
+
+    public void testNonPrintableChars() throws Exception {
+        // See https://code.google.com/p/android/issues/detail?id=56205
+        File file = new File(getTargetDir(), "report");
+        try {
+            LintCliClient client = new LintCliClient() {
+                @Override
+                String getRevision() {
+                    return "unittest"; // Hardcode version to keep unit test output stable
+                }
+            };
+            //noinspection ResultOfMethodCallIgnored
+            file.getParentFile().mkdirs();
+            XmlReporter reporter = new XmlReporter(client, file);
+            Project project = Project.create(client, new File("/foo/bar/Foo"),
+                    new File("/foo/bar/Foo"));
+
+            Warning warning1 = new Warning(TypographyDetector.FRACTIONS,
+                    String.format("Use fraction character %1$c (%2$s) instead of %3$s ?",
+                            '\u00BC', "&#188;", "1/4"), Severity.WARNING, project);
+            warning1.line = 592;
+            warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
+            warning1.errorLine =
+                    "        <string name=\"user_registration_name3_3\">Register 3/3</string>\n" +
+                    "                                             ^";
+            warning1.path = "res/values-en/common_strings.xml";
+            warning1.location = Location.create(warning1.file,
+                    new DefaultPosition(592, 46, -1), null);
+
+            List<Warning> warnings = new ArrayList<Warning>();
+            warnings.add(warning1);
+
+            reporter.write(0, 2, warnings);
+
+            String report = Files.toString(file, Charsets.UTF_8);
+            assertEquals(""
+                    + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                    + "<issues format=\"4\" by=\"lint unittest\">\n"
+                    + "\n"
+                    + "    <issue\n"
+                    + "        id=\"TypographyFractions\"\n"
+                    + "        severity=\"Warning\"\n"
+                    + "        message=\"Use fraction character ¼ (&amp;#188;) instead of 1/4 ?\"\n"
+                    + "        category=\"Usability:Typography\"\n"
+                    + "        priority=\"5\"\n"
+                    + "        summary=\"Fraction string can be replaced with fraction character\"\n"
+                    + "        explanation=\"You can replace certain strings, such as 1/2, and 1/4, with dedicated characters for these, such as ½ (&amp;#189;) and ¼ (&amp;#188;). This can help make the text more readable.\"\n"
+                    + "        url=\"http://en.wikipedia.org/wiki/Number_Forms\"\n"
+                    + "        urls=\"http://en.wikipedia.org/wiki/Number_Forms\">\n"
+                    + "        <location\n"
+                    + "            file=\"AndroidManifest.xml\"\n"
+                    + "            line=\"593\"\n"
+                    + "            column=\"47\"/>\n"
+                    + "    </issue>\n"
+                    + "\n"
+                    + "</issues>\n",
+                    report);
+
+            // Make sure the XML is valid
+            Document document = PositionXmlParser.parse(report);
+            assertNotNull(document);
+            assertEquals(1, document.getElementsByTagName("issue").getLength());
+            String explanation =  ((Element)document.getElementsByTagName("issue").item(0)).
+                    getAttribute("explanation");
+            assertEquals(TypographyDetector.FRACTIONS.getExplanation(TextFormat.RAW),
+                    explanation);
+        } finally {
+            //noinspection ResultOfMethodCallIgnored
+            file.delete();
+        }
+    }
+
+    @Override
+    protected Detector getDetector() {
+        fail("Not used in this test");
+        return null;
+    }
+}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
new file mode 100644
index 0000000..d50ffe4
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractCheckTest extends LintDetectorTest {
+    @Override
+    protected List<Issue> getIssues() {
+        List<Issue> issues = new ArrayList<Issue>();
+        Class<? extends Detector> detectorClass = getDetectorInstance().getClass();
+        // Get the list of issues from the registry and filter out others, to make sure
+        // issues are properly registered
+        List<Issue> candidates = new BuiltinIssueRegistry().getIssues();
+        for (Issue issue : candidates) {
+            if (issue.getImplementation().getDetectorClass() == detectorClass) {
+                issues.add(issue);
+            }
+        }
+
+        return issues;
+    }
+
+    @Override
+    protected InputStream getTestResource(String relativePath, boolean expectExists) {
+        String path = "data" + File.separator + relativePath; //$NON-NLS-1$
+        InputStream stream = AbstractCheckTest.class.getResourceAsStream(path);
+        if (stream == null) {
+            File root = getRootDir();
+            assertNotNull(root);
+            String pkg = AbstractCheckTest.class.getName();
+            pkg = pkg.substring(0, pkg.lastIndexOf('.'));
+            File f = new File(root,
+                "tools/base/lint/libs/lint-tests/src/test/java/".replace('/', File.separatorChar)
+                            + pkg.replace('.', File.separatorChar)
+                            + File.separatorChar + path);
+            if (f.exists()) {
+                try {
+                    return new BufferedInputStream(new FileInputStream(f));
+                } catch (FileNotFoundException e) {
+                    stream = null;
+                    if (expectExists) {
+                        fail("Could not find file " + relativePath);
+                    }
+                }
+            }
+        }
+        if (!expectExists && stream == null) {
+            return null;
+        }
+        return stream;
+    }
+
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
new file mode 100644
index 0000000..1831953
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.util.List;
+
+@SuppressWarnings("javadoc")
+public class AnnotationDetectorTest extends AbstractCheckTest {
+    public void test() throws Exception {
+        assertEquals(
+            "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+            "    public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
+            "                                         ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+            "        @SuppressLint(\"NewApi\") // Invalid\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+            "        @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+            "        @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+            "        @SuppressLint(\"NewApi\")\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "5 errors, 0 warnings\n",
+
+            lintProject(
+                "src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
+            ));
+    }
+
+    @SuppressWarnings("ClassNameDiffersFromFileName")
+    public void testUniqueValues() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/IntDefTest.java:9: Error: Constants STYLE_NO_INPUT and STYLE_NO_FRAME specify the same exact value (2); this is usually a cut & paste or merge error [UniqueConstants]\n"
+                + "    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+                + "                                                           ~~~~~~~~~~~~~~\n"
+                + "    src/test/pkg/IntDefTest.java:9: Previous same value\n"
+                + "src/test/pkg/IntDefTest.java:28: Error: Constants FLAG3 and FLAG2 specify the same exact value (562949953421312); this is usually a cut & paste or merge error [UniqueConstants]\n"
+                + "    @IntDef({FLAG2, FLAG3, FLAG1})\n"
+                + "                    ~~~~~\n"
+                + "    src/test/pkg/IntDefTest.java:28: Previous same value\n"
+                + "2 errors, 0 warnings\n",
+
+                lintProject(
+                        java("src/test/pkg/IntDefTest.java", ""
+                                + "package test.pkg;\n"
+                                + "import android.support.annotation.IntDef;\n"
+                                + "\n"
+                                + "import java.lang.annotation.Retention;\n"
+                                + "import java.lang.annotation.RetentionPolicy;\n"
+                                + "\n"
+                                + "@SuppressLint(\"UnusedDeclaration\")\n"
+                                + "public class IntDefTest {\n"
+                                + "    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface DialogStyle {}\n"
+                                + "\n"
+                                + "    public static final int STYLE_NORMAL = 0;\n"
+                                + "    public static final int STYLE_NO_TITLE = 1;\n"
+                                + "    public static final int STYLE_NO_FRAME = 2;\n"
+                                + "    public static final int STYLE_NO_INPUT = 2;\n"
+                                + "\n"
+                                + "    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+                                + "    @SuppressWarnings(\"UniqueConstants\")\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface SuppressedDialogStyle {}\n"
+                                + "\n"
+                                + "\n"
+                                + "    public static final long FLAG1 = 0x100000000000L;\n"
+                                + "    public static final long FLAG2 = 0x0002000000000000L;\n"
+                                + "    public static final long FLAG3 = 0x2000000000000L;\n"
+                                + "\n"
+                                + "    @IntDef({FLAG2, FLAG3, FLAG1})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface Flags {}\n"
+                                + "\n"
+
+                                + ""
+                                + "}"),
+                        copy("src/android/support/annotation/IntDef.java.txt",
+                                "src/android/support/annotation/IntDef.java")));
+    }
+
+    @SuppressWarnings("ClassNameDiffersFromFileName")
+    public void testFlagStyle() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/IntDefTest.java:13: Warning: Consider declaring this constant using 1 << 44 instead [ShiftFlags]\n"
+                + "    public static final long FLAG5 = 0x100000000000L;\n"
+                + "                                     ~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:14: Warning: Consider declaring this constant using 1 << 49 instead [ShiftFlags]\n"
+                + "    public static final long FLAG6 = 0x0002000000000000L;\n"
+                + "                                     ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:15: Warning: Consider declaring this constant using 1 << 3 instead [ShiftFlags]\n"
+                + "    public static final long FLAG7 = 8L;\n"
+                + "                                     ~~\n"
+                + "0 errors, 3 warnings\n",
+                lintProject(
+                        java("src/test/pkg/IntDefTest.java", ""
+                                + "package test.pkg;\n"
+                                + "import android.support.annotation.IntDef;\n"
+                                + "\n"
+                                + "import java.lang.annotation.Retention;\n"
+                                + "import java.lang.annotation.RetentionPolicy;\n"
+                                + "\n"
+                                + "@SuppressWarnings(\"unused\")\n"
+                                + "public class IntDefTest {\n"
+                                + "    public static final long FLAG1 = 1;\n"
+                                + "    public static final long FLAG2 = 2;\n"
+                                + "    public static final long FLAG3 = 1 << 2;\n"
+                                + "    public static final long FLAG4 = 1 << 3;\n"
+                                + "    public static final long FLAG5 = 0x100000000000L;\n"
+                                + "    public static final long FLAG6 = 0x0002000000000000L;\n"
+                                + "    public static final long FLAG7 = 8L;\n"
+                                + "    public static final long FLAG8 = 9L;\n"
+                                + "    public static final long FLAG9 = 0;\n"
+                                + "    public static final long FLAG10 = 1;\n"
+                                + "    public static final long FLAG11 = -1;\n"
+                                + "\n"
+                                // Not a flag (missing flag=true)
+                                + "    @IntDef({FLAG1, FLAG2, FLAG3})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface Flags1 {}\n"
+                                + "\n"
+                                // OK: Too few values
+                                + "    @IntDef(flag = true, value={FLAG1, FLAG2})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface Flags2 {}\n"
+                                + "\n"
+                                // OK: Allow 0, 1, -1
+                                + "    @IntDef(flag = true, value={FLAG9, FLAG10, FLAG11})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface Flags3 {}\n"
+                                + "\n"
+                                // OK: Already using shifts
+                                + "    @IntDef(flag = true, value={FLAG1, FLAG3, FLAG4})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface Flags4 {}\n"
+                                + "\n"
+                                // Wrong: should be flagged
+                                + "    @IntDef(flag = true, value={FLAG5, FLAG6, FLAG7, FLAG8})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    private @interface Flags5 {}\n"
+                                + "}"),
+                        copy("src/android/support/annotation/IntDef.java.txt",
+                                "src/android/support/annotation/IntDef.java")));
+    }
+
+    @Override
+    protected Detector getDetector() {
+        return new AnnotationDetector();
+    }
+
+    @Override
+    protected List<Issue> getIssues() {
+        List<Issue> issues = super.getIssues();
+
+        // Need these issues on to be found by the registry as well to look up scope
+        // in id references (these ids are referenced in the unit test java file below)
+        issues.add(ApiDetector.UNSUPPORTED);
+        issues.add(SdCardDetector.ISSUE);
+
+        return issues;
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppIndexingApiDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppIndexingApiDetectorTest.java
new file mode 100644
index 0000000..4c0a56a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppIndexingApiDetectorTest.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class AppIndexingApiDetectorTest extends AbstractCheckTest {
+
+    @Override
+    protected Detector getDetector() {
+        return new AppIndexingApiDetector();
+    }
+
+    public void testOk() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                    android:host=\"example.com\"\n"
+                        + "                    android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testDataMissing() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:15: Error: Missing data node? [AppIndexingError]\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "            ^\n"
+                        + "1 errors, 0 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testNoUrl() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:17: Error: Missing URL for the intent filter? [AppIndexingError]\n"
+                        + "                <data />\n"
+                        + "                ~~~~~~~~\n"
+                        + "1 errors, 0 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testMimeType() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:mimeType=\"mimetype\" /> "
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+
+    public void testNoActivity() throws Exception {
+        assertEquals(
+                "No warnings.",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testNotBrowsable() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:25: Warning: Activity supporting ACTION_VIEW is not set as BROWSABLE [AppIndexingWarning]\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "            ^\n"
+                        + "0 errors, 1 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".MainActivity\"\n"
+                        + "            android:label=\"@string/app_name\" >\n"
+                        + "            <intent-filter>\n"
+                        + "                <action android:name=\"android.intent.action.MAIN\" />\n"
+                        + "\n"
+                        + "                <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                    android:host=\"example.com\"\n"
+                        + "                    android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testWrongPathPrefix() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:19: Error: android:pathPrefix attribute should start with '/', but it is : gizmos [AppIndexingError]\n"
+                        + "                    android:pathPrefix=\"gizmos\" />\n"
+                        + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "1 errors, 0 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                    android:host=\"example.com\"\n"
+                        + "                    android:pathPrefix=\"gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testWrongPort() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:19: Error: android:port is not a legal number [AppIndexingError]\n"
+                        + "                    android:port=\"ABCD\"\n"
+                        + "                    ~~~~~~~~~~~~~~~~~~~\n"
+                        + "1 errors, 0 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                    android:host=\"example.com\"\n"
+                        + "                    android:port=\"ABCD\"\n"
+                        + "                    android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testSchemeAndHostMissing() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:17: Error: Missing URL for the intent filter? [AppIndexingError]\n"
+                        + "                <data android:pathPrefix=\"/gizmos\" />\n"
+                        + "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "AndroidManifest.xml:17: Error: android:host missing [AppIndexingError]\n"
+                        + "                <data android:pathPrefix=\"/gizmos\" />\n"
+                        + "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "AndroidManifest.xml:17: Error: android:scheme missing [AppIndexingError]\n"
+                        + "                <data android:pathPrefix=\"/gizmos\" />\n"
+                        + "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "3 errors, 0 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testMultiData() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\" />\n"
+                        + "                <data android:host=\"example.com\" />\n"
+                        + "                <data android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testMultiIntent() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter>\n"
+                        + "                <action android:name=\"android.intent.action.MAIN\" />\n"
+                        + "                <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+                        + "            </intent-filter>"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                    android:host=\"example.com\"\n"
+                        + "                    android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testMultiIntentWithError() throws Exception {
+        assertEquals(""
+                        + "AndroidManifest.xml:20: Error: android:host missing [AppIndexingError]\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                ^\n"
+                        + "1 errors, 0 warnings\n",
+                lintProject(xml("AndroidManifest.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    package=\"com.example.helloworld\" >\n"
+                        + "\n"
+                        + "    <application\n"
+                        + "        android:allowBackup=\"true\"\n"
+                        + "        android:icon=\"@mipmap/ic_launcher\"\n"
+                        + "        android:label=\"@string/app_name\"\n"
+                        + "        android:theme=\"@style/AppTheme\" >\n"
+                        + "        <activity\n"
+                        + "            android:name=\".FullscreenActivity\"\n"
+                        + "            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+                        + "            android:label=\"@string/title_activity_fullscreen\"\n"
+                        + "            android:theme=\"@style/FullscreenTheme\" >\n"
+                        + "            <intent-filter>\n"
+                        + "                <action android:name=\"android.intent.action.MAIN\" />\n"
+                        + "                <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+                        + "            </intent-filter>"
+                        + "            <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+                        + "                <action android:name=\"android.intent.action.VIEW\" />\n"
+                        + "                <data android:scheme=\"http\"\n"
+                        + "                    android:pathPrefix=\"/gizmos\" />\n"
+                        + "                <category android:name=\"android.intent.category.DEFAULT\" />\n"
+                        + "                <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+                        + "            </intent-filter>\n"
+                        + "        </activity>\n"
+                        + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testOkWithResource() throws Exception {
+        assertEquals("No warnings.",
+                lintProjectIncrementally(
+                        "AndroidManifest.xml",
+                        "appindexing_manifest.xml=>AndroidManifest.xml",
+                        "res/values/appindexing_strings.xml"));
+    }
+
+    public void testWrongWithResource() throws Exception {
+        assertEquals("" + "AndroidManifest.xml:18: Error: android:pathPrefix attribute should start with '/', but it is : pathprefix [AppIndexingError]\n"
+                        + "                      android:pathPrefix=\"@string/path_prefix\"\n"
+                        + "                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "AndroidManifest.xml:19: Error: android:port is not a legal number [AppIndexingError]\n"
+                        + "                      android:port=\"@string/port\"/>\n"
+                        + "                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "2 errors, 0 warnings\n",
+                lintProjectIncrementally(
+                        "AndroidManifest.xml",
+                        "appindexing_manifest.xml=>AndroidManifest.xml",
+                        "res/values/appindexing_wrong_strings.xml"));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
new file mode 100644
index 0000000..98405cc
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
@@ -0,0 +1,481 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+
+@SuppressWarnings("javadoc")
+public class ButtonDetectorTest extends AbstractCheckTest {
+    private static Issue sTestIssue;
+
+    @Override
+    protected boolean isEnabled(Issue issue) {
+        return super.isEnabled(issue) && sTestIssue == null || issue == sTestIssue;
+    }
+
+    @Override
+    protected Detector getDetector() {
+        return new ButtonDetector();
+    }
+
+    public void testButtonOrder() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "res/layout/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 7 warnings\n" +
+            "",
+
+            lintProject(
+                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrder2() throws Exception {
+        // If the layout is in v14, it had better have the right order
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "res/layout-v14/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v14/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v14/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v14/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v14/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v14/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v14/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 7 warnings\n" +
+            "",
+
+            lintProject(
+                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml=>res/layout-v14/buttonbar.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrder3() throws Exception {
+        // Similar to test 3, but also complain if the -v version is *higher* than 14
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "res/layout-v16/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v16/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v16/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v16/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v16/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v16/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-v16/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 7 warnings\n" +
+            "",
+
+            lintProject(
+                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml=>res/layout-v16/buttonbar.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrder4() throws Exception {
+        // Targeting 14 but using a layout that also needs to work for older platforms:
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "res/layout/buttonbar.xml:12: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:44: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:92: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:124: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:140: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:156: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:177: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 7 warnings\n" +
+            "",
+
+            lintProject(
+                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrder5() throws Exception {
+        // If the layout is in a non-ICS folder and has the wrong button order,
+        // but there is a v14 version of the layout, don't complain about the non-v14 version
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml",
+                    "res/layout/layout1.xml=>res/layout-v14/buttonbar.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testSuppressed() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar_suppressed.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrderRelativeLayout() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "No warnings.",
+
+            lintProject("res/layout/buttonbar2.xml", "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrderRelativeLayout2() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "res/layout/buttonbar3.xml:27: Warning: Cancel button should be on the left [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 1 warnings\n" +
+            "",
+
+            lintProject(
+                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar3.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonOrderRelativeLayout3() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "No warnings.",
+
+            lintProject("res/layout/buttonbar4.xml", "res/values/buttonbar-values.xml"));
+    }
+
+
+    public void testCase() throws Exception {
+        sTestIssue = ButtonDetector.CASE;
+        assertEquals(
+            "res/values/buttonbar-values.xml:9: Warning: The standard Android way to capitalize Ok is \"OK\" (tip: use @android:string/ok instead) [ButtonCase]\n" +
+            "    <string name=\"resume2\"> Ok </string>\n" +
+            "                            ^\n" +
+            "res/values/buttonbar-values.xml:10: Warning: The standard Android way to capitalize CANCEL is \"Cancel\" (tip: use @android:string/cancel instead) [ButtonCase]\n" +
+            "    <string name=\"giveup2\">\"CANCEL\"</string>\n" +
+            "                           ^\n" +
+            "0 errors, 2 warnings\n" +
+            "",
+
+            lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
+    }
+
+    public void testBack() throws Exception {
+        sTestIssue = ButtonDetector.BACK_BUTTON;
+        assertEquals(
+            "res/layout/buttonbar.xml:183: Warning: Back buttons are not standard on Android; see design guide's navigation section [BackButton]\n" +
+            "    <Button\n" +
+            "    ^\n" +
+            "0 errors, 1 warnings\n" +
+            "",
+
+            lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
+    }
+
+    public void testOldApp() throws Exception {
+        // Target SDK < 14 - no warnings on button order
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+                "No warnings.",
+
+        lintProject(
+                "minsdk5targetsdk9.xml=>AndroidManifest.xml",
+                "res/layout/buttonbar.xml",
+                "res/values/buttonbar-values.xml"));
+    }
+
+    public void testEnglishLocales() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            "res/layout-en-rGB/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-en-rGB/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-en-rGB/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-en-rGB/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-en-rGB/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-en-rGB/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-en-rGB/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 7 warnings\n" +
+            "",
+
+            lintProject(
+                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml=>res/layout-en-rGB/buttonbar.xml",
+                    "res/values/buttonbar-values.xml=>res/values-en-rGB/buttonbar-values.xml"));
+    }
+
+    public void testOtherLocales() throws Exception {
+        sTestIssue = ButtonDetector.ORDER;
+        assertEquals(
+            // Hardcoded values only
+            "res/layout-de/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout-de/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 2 warnings\n" +
+            "",
+
+            lintProject(
+                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
+                    "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
+    }
+
+    public void testOtherLocales2() throws Exception {
+        sTestIssue = ButtonDetector.CASE;
+        assertEquals(
+                "No warnings.",
+
+                lintProject("res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
+                        "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
+    }
+
+    public void testButtonStyle() throws Exception {
+        sTestIssue = ButtonDetector.STYLE;
+        assertEquals(
+            "res/layout/buttonbar.xml:12: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:17: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:28: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:33: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:44: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:49: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:60: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:65: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:76: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:81: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:92: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:97: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:108: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:113: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:124: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:129: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:140: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:145: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:156: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "res/layout/buttonbar.xml:161: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+            "        <Button\n" +
+            "        ^\n" +
+            "0 errors, 20 warnings\n",
+
+            lintProject(
+                    "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml",
+                    "res/layout/buttonbar2.xml",
+                    "res/layout/buttonbar3.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testButtonStyleOldMinSdk() throws Exception {
+        sTestIssue = ButtonDetector.STYLE;
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "res/layout/buttonbar.xml",
+                    "res/layout/buttonbar2.xml",
+                    "res/layout/buttonbar3.xml",
+                    "res/values/buttonbar-values.xml"));
+    }
+
+    public void testYesNo() throws Exception {
+        sTestIssue = ButtonDetector.CASE;
+        assertEquals(""
+                + "res/layout/yesno.xml:10: Warning: @android:string/yes actually returns \"OK\", not \"Yes\"; use @android:string/ok instead or create a local string resource for Yes [ButtonCase]\n"
+                + "        android:text=\"@android:string/yes\" />\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "res/layout/yesno.xml:15: Warning: @android:string/no actually returns \"Cancel\", not \"No\"; use @android:string/cancel instead or create a local string resource for No [ButtonCase]\n"
+                + "        android:text=\"@android:string/no\" />\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 2 warnings\n",
+
+                lintProject(
+                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                        "res/layout/yesno.xml"));
+    }
+
+    public void testIssue180417() throws Exception {
+        // Regression test for
+        //   https://code.google.com/p/android/issues/detail?id=180417
+        sTestIssue = ButtonDetector.CASE;
+        assertEquals(""
+                        + "res/values/buttonbar-values.xml:4: Warning: The standard Android way to capitalize Ok is \"OK\" (tip: use @android:string/ok instead) [ButtonCase]\n"
+                        + "    <string name=\"ok\">\"Ok\"</string>\n"
+                        + "                      ^\n"
+                        + "0 errors, 1 warnings\n",
+                lintProject(
+                        xml("res/values/buttonbar-values.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<resources>\n"
+                                + "\n"
+                                + "    <string name=\"ok\">\"Ok\"</string>\n"
+                                + "\n"
+                                + "</resources>\n"),
+                        xml("res/layout/buttonbar", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    android:layout_width=\"match_parent\"\n"
+                                + "    android:layout_height=\"match_parent\"\n"
+                                + "    android:orientation=\"vertical\" >\n"
+                                + "    <LinearLayout\n"
+                                + "        android:layout_width=\"match_parent\"\n"
+                                + "        android:layout_height=\"wrap_content\" >\n"
+                                + "\n"
+                                + "        <Button\n"
+                                + "            android:layout_width=\"wrap_content\"\n"
+                                + "            android:layout_height=\"wrap_content\"\n"
+                                + "            android:text=\"@string/cancel\" />\n"
+                                + "\n"
+                                + "        <Button\n"
+                                + "            android:layout_width=\"wrap_content\"\n"
+                                + "            android:layout_height=\"wrap_content\"\n"
+                                + "            android:text=\"@string/ok\" />\n"
+                                + "    </LinearLayout>\n"
+                                + "</LinearLayout>\n"))
+                );
+    }
+
+    public void testIssue101279() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=101279
+        sTestIssue = ButtonDetector.STYLE;
+        assertEquals(
+                "No warnings.",
+
+                lintProject(
+                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                        "res/layout/buttonbar5.xml"));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
new file mode 100644
index 0000000..1b885f3
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+public class CallSuperDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new CallSuperDetector();
+    }
+
+    public void testCallSuper() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/CallSuperTest.java:11: Error: Overriding method should call super.test1 [MissingSuperCall]\n"
+                + "        protected void test1() { // ERROR\n"
+                + "                       ~~~~~~~\n"
+                + "src/test/pkg/CallSuperTest.java:14: Error: Overriding method should call super.test2 [MissingSuperCall]\n"
+                + "        protected void test2() { // ERROR\n"
+                + "                       ~~~~~~~\n"
+                + "src/test/pkg/CallSuperTest.java:17: Error: Overriding method should call super.test3 [MissingSuperCall]\n"
+                + "        protected void test3() { // ERROR\n"
+                + "                       ~~~~~~~\n"
+                + "src/test/pkg/CallSuperTest.java:20: Error: Overriding method should call super.test4 [MissingSuperCall]\n"
+                + "        protected void test4(int arg) { // ERROR\n"
+                + "                       ~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/CallSuperTest.java:26: Error: Overriding method should call super.test5 [MissingSuperCall]\n"
+                + "        protected void test5(int arg1, boolean arg2, Map<List<String>,?> arg3,  // ERROR\n"
+                + "                       ^\n"
+                + "src/test/pkg/CallSuperTest.java:30: Error: Overriding method should call super.test5 [MissingSuperCall]\n"
+                + "        protected void test5() { // ERROR\n"
+                + "                       ~~~~~~~\n"
+                + "6 errors, 0 warnings\n",
+
+                lintProject("src/test/pkg/CallSuperTest.java.txt=>src/test/pkg/CallSuperTest.java",
+                        "src/android/support/annotation/CallSuper.java.txt=>src/android/support/annotation/CallSuper.java"));
+    }
+
+    @SuppressWarnings("ClassNameDiffersFromFileName")
+    public void testCallSuperIndirect() throws Exception {
+        // Ensure that when the @CallSuper is on an indirect super method,
+        // we correctly check that you call the direct super method, not the ancestor.
+        //
+        // Regression test for
+        //    https://code.google.com/p/android/issues/detail?id=174964
+        assertEquals("No warnings.",
+            lintProject(
+                java("src/test/pkg/CallSuperTest.java", ""
+                + "package test.pkg;\n"
+                + "\n"
+                + "import android.support.annotation.CallSuper;\n"
+                + "\n"
+                + "import java.util.List;\n"
+                + "import java.util.Map;\n"
+                + "\n"
+                + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+                + "public class CallSuperTest {\n"
+                + "    private static class Child extends Parent {\n"
+                + "        @Override\n"
+                + "        protected void test1() {\n"
+                + "            super.test1();\n"
+                + "        }\n"
+                + "    }\n"
+                + "\n"
+                + "    private static class Parent extends ParentParent {\n"
+                + "        @Override\n"
+                + "        protected void test1() {\n"
+                + "            super.test1();\n"
+                + "        }\n"
+                + "    }\n"
+                + "\n"
+                + "    private static class ParentParent extends ParentParentParent {\n"
+                + "        @CallSuper\n"
+                + "        protected void test1() {\n"
+                + "        }\n"
+                + "    }\n"
+                + "\n"
+                + "    private static class ParentParentParent {\n"
+                + "\n"
+                + "    }\n"
+                + "}\n"),
+                copy("src/android/support/annotation/CallSuper.java.txt",
+                        "src/android/support/annotation/CallSuper.java")));
+    }
+
+    public void testDetachFromWindow() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/DetachedFromWindow.java:7: Error: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
+                + "        protected void onDetachedFromWindow() {\n"
+                + "                       ~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/DetachedFromWindow.java:26: Error: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
+                + "        protected void onDetachedFromWindow() {\n"
+                + "                       ~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "2 errors, 0 warnings\n",
+
+                lintProject("src/test/pkg/DetachedFromWindow.java.txt=>" +
+                        "src/test/pkg/DetachedFromWindow.java"));
+    }
+
+    public void testWatchFaceVisibility() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/WatchFaceTest.java:9: Error: Overriding method should call super.onVisibilityChanged [MissingSuperCall]\n"
+                + "        public void onVisibilityChanged(boolean visible) { // ERROR: Missing super call\n"
+                + "                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+
+                lintProject(
+                        "src/test/pkg/WatchFaceTest.java.txt=>src/test/pkg/WatchFaceTest.java",
+                        "stubs/WatchFaceService.java.txt=>src/android/support/wearable/watchface/WatchFaceService.java",
+                        "stubs/CanvasWatchFaceService.java.txt=>src/android/support/wearable/watchface/CanvasWatchFaceService.java"
+                ));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
new file mode 100644
index 0000000..ed10742
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class DetectMissingPrefixTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new DetectMissingPrefix();
+    }
+
+    public void test() throws Exception {
+        assertEquals(
+            "res/layout/namespace.xml:2: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:other=\"http://foo.bar\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" orientation=\"true\">\n" +
+            "                                                                                                                                                                                                                                          ~~~~~~~~~~~~~~~~~~\n" +
+            "res/layout/namespace.xml:3: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "    <Button style=\"@style/setupWizardOuterFrame\" android.text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
+            "                                                 ~~~~~~~~~~~~~~~~~~~~~\n" +
+            "res/layout/namespace.xml:5: Error: Unexpected namespace prefix \"other\" found for tag LinearLayout [MissingPrefix]\n" +
+            "    <LinearLayout other:orientation=\"horizontal\"/>\n" +
+            "                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "3 errors, 0 warnings\n",
+
+            lintFiles("res/layout/namespace.xml"));
+    }
+
+    public void testCustomNamespace() throws Exception {
+        assertEquals(
+            "res/layout/namespace2.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "    orientation=\"true\">\n" +
+            "    ~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintFiles("res/layout/namespace2.xml"));
+    }
+
+    public void testManifest() throws Exception {
+        assertEquals(
+            "AndroidManifest.xml:4: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "    versionCode=\"1\"\n" +
+            "    ~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:11: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "        android.label=\"@string/app_name\" >\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:18: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "                <category name=\"android.intent.category.LAUNCHER\" />\n" +
+            "                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "3 errors, 0 warnings\n",
+
+            lintFiles("missingprefix.xml=>AndroidManifest.xml"));
+    }
+
+    public void testLayoutAttributes() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintFiles("res/layout/namespace3.xml"));
+    }
+
+    public void testLayoutAttributes2() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintFiles("res/layout/namespace4.xml"));
+    }
+
+    public void testUnusedNamespace() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintProject("res/layout/message_edit_detail.xml"));
+    }
+
+    public void testMissingLayoutAttribute() throws Exception {
+        assertEquals(
+            "res/layout/rtl.xml:7: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "        layout_gravity=\"left\"\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~\n" +
+            "res/layout/rtl.xml:8: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "        layout_alignParentLeft=\"true\"\n" +
+            "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"  +
+            "res/layout/rtl.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+            "        editable=\"false\"\n" +
+            "        ~~~~~~~~~~~~~~~~\n" +
+            "3 errors, 0 warnings\n",
+
+            lintProject(
+                    "overdraw/project.properties=>project.properties",
+                    "rtl/minsdk5targetsdk17.xml=>AndroidManifest.xml",
+                    "rtl/rtl_noprefix.xml=>res/layout/rtl.xml"
+            ));
+    }
+
+    public void testDataBinding() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(xml("res/layout/test.xml", "\n"
+                        + "<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "    xmlns:bind=\"http://schemas.android.com/apk/res-auto\"\n"
+                        + "    xmlns:tools=\"http://schemas.android.com/tools\">\n"
+                        + "    <data>\n"
+                        + "        <variable name=\"activity\" type=\"com.android.example.bindingdemo.MainActivity\"/>\n"
+                        + "        <!---->\n"
+                        + "        <import\n"
+                        + "            type=\"android.view.View\"\n"
+                        + "            />\n"
+                        + "        <!---->\n"
+                        + "        <import type=\"com.android.example.bindingdemo.R.string\" alias=\"Strings\"/>\n"
+                        + "        <import type=\"com.android.example.bindingdemo.vo.User\"/>\n"
+                        + "    </data>\n"
+                        + "    <LinearLayout\n"
+                        + "        android:layout_width=\"match_parent\"\n"
+                        + "        android:layout_height=\"match_parent\"\n"
+                        + "        android:orientation=\"vertical\"\n"
+                        + "        android:id=\"@+id/activityRoot\"\n"
+                        + "        android:clickable=\"true\"\n"
+                        + "        android:onClickListener=\"@{activity.onUnselect}\">\n"
+                        + "        <android.support.v7.widget.CardView\n"
+                        + "            android:id=\"@+id/selected_card\"\n"
+                        + "            bind:contentPadding=\"@{activity.selected == null ? 5 : activity.selected.name.length()}\"\n"
+                        + "            android:layout_width=\"match_parent\"\n"
+                        + "            android:layout_height=\"wrap_content\"\n"
+                        + "            bind:visibility=\"@{activity.selected == null ? View.INVISIBLE : View.VISIBLE}\">\n"
+                        + "\n"
+                        + "            <GridLayout\n"
+                        + "                android:layout_width=\"match_parent\"\n"
+                        + "                android:layout_height=\"wrap_content\"\n"
+                        + "                android:columnCount=\"2\"\n"
+                        + "                android:rowCount=\"4\">\n"
+                        + "                <Button\n"
+                        + "                    android:id=\"@+id/edit_button\"\n"
+                        + "                    bind:onClickListener=\"@{activity.onSave}\"\n"
+                        + "                    android:text='@{\"Save changes to \" + activity.selected.name}'\n"
+                        + "                    android:layout_width=\"wrap_content\"\n"
+                        + "                    android:layout_height=\"wrap_content\"\n"
+                        + "                    android:layout_column=\"1\"\n"
+                        + "                    android:layout_gravity=\"right\"\n"
+                        + "                    android:layout_row=\"2\"/>\n"
+                        + "            </GridLayout>\n"
+                        + "        </android.support.v7.widget.CardView>"
+                        + "    </LinearLayout>\n"
+                        + "</layout>")));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FullBackupContentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FullBackupContentDetectorTest.java
new file mode 100644
index 0000000..844e5cb
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FullBackupContentDetectorTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+public class FullBackupContentDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new FullBackupContentDetector();
+    }
+
+    public void testOk() throws Exception {
+        assertEquals("No warnings.",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content>\n"
+                        + "     <include domain=\"file\" path=\"dd\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+                        + "</full-backup-content>")));
+    }
+
+    public void test20890435() throws Exception {
+        assertEquals(""
+                + "res/xml/backup.xml:6: Error: foo.xml is not in an included path [FullBackupContent]\n"
+                + "     <exclude domain=\"sharedpref\" path=\"foo.xml\"/>\n"
+                + "                                        ~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content>\n"
+                        + "     <include domain=\"file\" path=\"dd\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+                        + "     <exclude domain=\"sharedpref\" path=\"foo.xml\"/>\n"
+                        + "</full-backup-content>")));
+    }
+
+    public void testImplicitInclude() throws Exception {
+        // If there is no include, then everything is considered included
+        assertEquals("No warnings.",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+                        + "</full-backup-content>")));
+    }
+
+    public void testImplicitPath() throws Exception {
+        // If you specify an include, but no path attribute, that's defined to mean include
+        // everything
+        assertEquals("No warnings.",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content>\n"
+                        + "     <include domain=\"file\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+                        + "     <include domain=\"sharedpref\" path=\"something\"/>\n"
+                        + "</full-backup-content>")));
+    }
+
+    public void testSuppressed() throws Exception {
+        assertEquals("No warnings.",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content xmlns:tools=\"http://schemas.android.com/tools\">\n"
+                        + "     <include domain=\"file\" path=\"dd\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+                        + "     <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+                        + "     <exclude domain=\"sharedpref\" path=\"foo.xml\" tools:ignore=\"FullBackupContent\"/>\n"
+                        + "</full-backup-content>")));
+    }
+
+    public void testIncludeWrongDomain() throws Exception {
+        // Ensure that the path prefix check is done independently for each domain
+        assertEquals(""
+                + "res/xml/backup.xml:4: Error: abc/def.txt is not in an included path [FullBackupContent]\n"
+                + "     <exclude domain=\"external\" path=\"abc/def.txt\"/>\n"
+                + "                                      ~~~~~~~~~~~\n"
+                + "res/xml/backup.xml:6: Error: def/ghi.txt is not in an included path [FullBackupContent]\n"
+                + "     <exclude domain=\"external\" path=\"def/ghi.txt\"/>\n"
+                + "                                      ~~~~~~~~~~~\n"
+                + "2 errors, 0 warnings\n",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content>\n"
+                        + "     <include domain=\"file\" path=\"abc\"/>\n"
+                        + "     <exclude domain=\"external\" path=\"abc/def.txt\"/>\n"
+                        + "     <include domain=\"file\" path=\"def\"/>\n"
+                        + "     <exclude domain=\"external\" path=\"def/ghi.txt\"/>\n"
+                        + "</full-backup-content>")));
+    }
+
+    public void testValidation() throws Exception {
+        assertEquals(""
+                + "res/xml/backup.xml:7: Error: Subdirectories are not allowed for domain sharedpref [FullBackupContent]\n"
+                + "     <include domain=\"sharedpref\" path=\"dd/subdir\"/>\n"
+                + "                                        ~~~~~~~~~\n"
+                + "res/xml/backup.xml:8: Error: Paths are not allowed to contain .. [FullBackupContent]\n"
+                + "     <include domain=\"file\" path=\"../outside\"/>\n"
+                + "                                  ~~~~~~~~~~\n"
+                + "res/xml/backup.xml:9: Error: Paths are not allowed to contain // [FullBackupContent]\n"
+                + "     <include domain=\"file\" path=\"//wrong\"/>\n"
+                + "                                  ~~~~~~~\n"
+                + "res/xml/backup.xml:11: Error: Include dd is also excluded [FullBackupContent]\n"
+                + "     <exclude domain=\"external\" path=\"dd\"/>\n"
+                + "                                      ~~\n"
+                + "    res/xml/backup.xml:10: Unnecessary/conflicting <include>\n"
+                + "res/xml/backup.xml:12: Error: Unexpected domain unknown-domain, expected one of root, file, database, sharedpref, external [FullBackupContent]\n"
+                + "     <exclude domain=\"unknown-domain\" path=\"dd\"/>\n"
+                + "                      ~~~~~~~~~~~~~~\n"
+                + "res/xml/backup.xml:12: Error: dd is not in an included path [FullBackupContent]\n"
+                + "     <exclude domain=\"unknown-domain\" path=\"dd\"/>\n"
+                + "                                            ~~\n"
+                + "res/xml/backup.xml:13: Error: Missing domain attribute, expected one of root, file, database, sharedpref, external [FullBackupContent]\n"
+                + "     <include path=\"dd\"/>\n"
+                + "     ~~~~~~~~~~~~~~~~~~~~\n"
+                + "res/xml/backup.xml:15: Error: Unexpected element <wrongtag> [FullBackupContent]\n"
+                + "     <wrongtag />\n"
+                + "      ~~~~~~~~\n"
+                + "8 errors, 0 warnings\n",
+
+                lintProject(xml("res/xml/backup.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<full-backup-content>\n"
+                        + "     <include domain=\"root\" path=\"dd\"/>\n" // OK
+                        + "     <include domain=\"file\" path=\"dd\"/>\n" // OK
+                        + "     <include domain=\"database\" path=\"dd\"/>\n" // OK
+                        + "     <include domain=\"sharedpref\" path=\"dd\"/>\n" // OK
+                        + "     <include domain=\"sharedpref\" path=\"dd/subdir\"/>\n" // Not allowed
+                        + "     <include domain=\"file\" path=\"../outside\"/>\n" // Not allowed
+                        + "     <include domain=\"file\" path=\"//wrong\"/>\n" // Not allowed
+                        + "     <include domain=\"external\" path=\"dd\"/>\n" // OK
+                        + "     <exclude domain=\"external\" path=\"dd\"/>\n" // same as included
+                        + "     <exclude domain=\"unknown-domain\" path=\"dd\"/>\n"
+                        + "     <include path=\"dd\"/>\n" // No domain
+                        + "     <include domain=\"root\" />\n" // OK (means include everything
+                        + "     <wrongtag />\n"
+                        + "</full-backup-content>")));
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
new file mode 100644
index 0000000..71e07a2
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2014 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.tools.lint.checks;
+
+import static com.android.SdkConstants.GRADLE_PLUGIN_MINIMUM_VERSION;
+import static com.android.SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION;
+import static com.android.tools.lint.checks.GradleDetector.ACCIDENTAL_OCTAL;
+import static com.android.tools.lint.checks.GradleDetector.COMPATIBILITY;
+import static com.android.tools.lint.checks.GradleDetector.DEPENDENCY;
+import static com.android.tools.lint.checks.GradleDetector.DEPRECATED;
+import static com.android.tools.lint.checks.GradleDetector.GRADLE_GETTER;
+import static com.android.tools.lint.checks.GradleDetector.GRADLE_PLUGIN_COMPATIBILITY;
+import static com.android.tools.lint.checks.GradleDetector.PATH;
+import static com.android.tools.lint.checks.GradleDetector.PLUS;
+import static com.android.tools.lint.checks.GradleDetector.REMOTE_VERSION;
+import static com.android.tools.lint.checks.GradleDetector.STRING_INTEGER;
+import static com.android.tools.lint.checks.GradleDetector.getNamedDependency;
+import static com.android.tools.lint.checks.GradleDetector.getNewValue;
+import static com.android.tools.lint.checks.GradleDetector.getOldValue;
+import static com.android.tools.lint.detector.api.TextFormat.TEXT;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+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.Dependencies;
+import com.android.builder.model.MavenCoordinates;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.DefaultPosition;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.utils.Pair;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <b>NOTE</b>: Most GradleDetector unit tests are in the Studio plugin, as tests
+ * for IntellijGradleDetector
+ */
+public class GradleDetectorTest extends AbstractCheckTest {
+
+    private File mSdkDir;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        if (mSdkDir != null) {
+            deleteFile(mSdkDir);
+            mSdkDir = null;
+        }
+    }
+
+    /** Creates a mock SDK installation structure, containing a fixed set of dependencies */
+    private File getMockSupportLibraryInstallation() {
+        if (mSdkDir == null) {
+            // Make fake SDK "installation" such that we can predict the set
+            // of Maven repositories discovered by this test
+            mSdkDir = Files.createTempDir();
+
+            String[] paths = new String[]{
+                    // Android repository
+                    "extras/android/m2repository/com/android/support/appcompat-v7/18.0.0/appcompat-v7-18.0.0.aar",
+                    "extras/android/m2repository/com/android/support/appcompat-v7/19.0.0/appcompat-v7-19.0.0.aar",
+                    "extras/android/m2repository/com/android/support/appcompat-v7/19.0.1/appcompat-v7-19.0.1.aar",
+                    "extras/android/m2repository/com/android/support/appcompat-v7/19.1.0/appcompat-v7-19.1.0.aar",
+                    "extras/android/m2repository/com/android/support/appcompat-v7/20.0.0/appcompat-v7-20.0.0.aar",
+                    "extras/android/m2repository/com/android/support/appcompat-v7/21.0.0/appcompat-v7-21.0.0.aar",
+                    "extras/android/m2repository/com/android/support/appcompat-v7/21.0.2/appcompat-v7-21.0.2.aar",
+                    "extras/android/m2repository/com/android/support/cardview-v7/21.0.0/cardview-v7-21.0.0.aar",
+                    "extras/android/m2repository/com/android/support/cardview-v7/21.0.2/cardview-v7-21.0.2.aar",
+                    "extras/android/m2repository/com/android/support/support-v13/20.0.0/support-v13-20.0.0.aar",
+                    "extras/android/m2repository/com/android/support/support-v13/21.0.0/support-v13-21.0.0.aar",
+                    "extras/android/m2repository/com/android/support/support-v13/21.0.2/support-v13-21.0.2.aar",
+                    "extras/android/m2repository/com/android/support/support-v4/20.0.0/support-v4-20.0.0.aar",
+                    "extras/android/m2repository/com/android/support/support-v4/21.0.0/support-v4-21.0.0.aar",
+                    "extras/android/m2repository/com/android/support/support-v4/21.0.2/support-v4-21.0.2.aar",
+
+                    // Google repository
+                    "extras/google/m2repository/com/google/android/gms/play-services/3.1.36/play-services-3.1.36.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/3.1.59/play-services-3.1.59.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/3.2.25/play-services-3.2.25.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/3.2.65/play-services-3.2.65.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/4.0.30/play-services-4.0.30.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/4.1.32/play-services-4.1.32.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/4.2.42/play-services-4.2.42.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/4.3.23/play-services-4.3.23.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/4.4.52/play-services-4.4.52.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/5.0.89/play-services-5.0.89.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/6.1.11/play-services-6.1.11.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services/6.1.71/play-services-6.1.71.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services-wearable/5.0.77/play-services-wearable-5.0.77.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.11/play-services-wearable-6.1.11.aar",
+                    "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.71/play-services-wearable-6.1.71.aar",
+                    "extras/google/m2repository/com/google/android/support/wearable/1.0.0/wearable-1.0.0.aar"
+            };
+
+            for (String path : paths) {
+                File file = new File(mSdkDir, path.replace('/', File.separatorChar));
+                File parent = file.getParentFile();
+                if (!parent.exists()) {
+                    boolean ok = parent.mkdirs();
+                    assertTrue(ok);
+                }
+                try {
+                    boolean created = file.createNewFile();
+                    assertTrue(created);
+                } catch (IOException e) {
+                    fail(e.toString());
+                }
+            }
+        }
+
+        return mSdkDir;
+    }
+
+    public void testGetOldValue() {
+        assertEquals("11.0.2", getOldValue(DEPENDENCY,
+                "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
+                TEXT));
+        assertNull(getOldValue(DEPENDENCY, "Bogus", TEXT));
+        assertNull(getOldValue(DEPENDENCY, "bogus", TEXT));
+        // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
+        assertEquals("20", getOldValue(DEPENDENCY,
+                "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
+                TEXT));
+        assertEquals("'19'", getOldValue(STRING_INTEGER,
+                "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
+        assertEquals("android", getOldValue(DEPRECATED,
+                "'android' is deprecated; use 'com.android.application' instead", TEXT));
+        assertEquals("android-library", getOldValue(DEPRECATED,
+                "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
+        assertEquals("packageName", getOldValue(DEPRECATED,
+                "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
+        assertEquals("packageNameSuffix", getOldValue(DEPRECATED,
+                "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
+        assertEquals("18.0.0", getOldValue(DEPENDENCY,
+                "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
+    }
+
+    public void testGetNewValue() {
+        assertEquals("17.0.0", getNewValue(DEPENDENCY,
+                "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
+                TEXT));
+        assertNull(getNewValue(DEPENDENCY,
+                "A newer version of com.google.guava:guava than 11.0.2 is available", TEXT));
+        assertNull(getNewValue(DEPENDENCY, "bogus", TEXT));
+        // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
+        assertEquals("19", getNewValue(DEPENDENCY,
+                "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
+                TEXT));
+        assertEquals("19", getNewValue(STRING_INTEGER,
+                "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
+        assertEquals("com.android.application", getNewValue(DEPRECATED,
+                "'android' is deprecated; use 'com.android.application' instead", TEXT));
+        assertEquals("com.android.library", getNewValue(DEPRECATED,
+                "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
+        assertEquals("applicationId", getNewValue(DEPRECATED,
+                "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
+        assertEquals("applicationIdSuffix", getNewValue(DEPRECATED,
+                "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
+        assertEquals("19.1", getNewValue(DEPENDENCY,
+                "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
+    }
+
+    public void test() throws Exception {
+        mEnabled = Sets.newHashSet(COMPATIBILITY, DEPRECATED, DEPENDENCY, PLUS);
+        assertEquals(""
+            + "build.gradle:25: Error: This support library should not use a lower version (13) than the targetSdkVersion (17) [GradleCompatible]\n"
+            + "    compile 'com.android.support:appcompat-v7:13.0.0'\n"
+            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "build.gradle:1: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
+            + "apply plugin: 'android'\n"
+            + "~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
+            + "    buildToolsVersion \"19.0.0\"\n"
+            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
+            + "    freeCompile 'com.google.guava:guava:11.0.2'\n"
+            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
+            + "    compile 'com.android.support:appcompat-v7:13.0.0'\n"
+            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "build.gradle:23: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
+            + "    compile 'com.android.support:appcompat-v7:+'\n"
+            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "1 errors, 5 warnings\n",
+
+            lintProject("gradle/Dependencies.gradle=>build.gradle"));
+    }
+
+    public void testCompatibility() throws Exception {
+        mEnabled = Collections.singleton(COMPATIBILITY);
+        assertEquals(""
+                + "build.gradle:16: Error: This support library should not use a lower version (18) than the targetSdkVersion (19) [GradleCompatible]\n"
+                + "    compile 'com.android.support:support-v4:18.0.0'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+
+                lintProject("gradle/Compatibility.gradle=>build.gradle"));
+    }
+
+    public void testIncompatiblePlugin() throws Exception {
+        mEnabled = Collections.singleton(GRADLE_PLUGIN_COMPATIBILITY);
+        assertEquals(""
+                + "build.gradle:6: Error: You must use a newer version of the Android Gradle plugin. The minimum supported version is " + GRADLE_PLUGIN_MINIMUM_VERSION + " and the recommended version is " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [AndroidGradlePluginVersion]\n"
+                + "    classpath 'com.android.tools.build:gradle:0.1.0'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+
+                lintProject("gradle/IncompatiblePlugin.gradle=>build.gradle"));
+    }
+
+    public void testSetter() throws Exception {
+        mEnabled = Collections.singleton(GRADLE_GETTER);
+        assertEquals(""
+                        + "build.gradle:18: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
+                        + "        versionCode getVersionCode\n"
+                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "build.gradle:19: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
+                        + "        versionName getVersionName\n"
+                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "2 errors, 0 warnings\n",
+
+                lintProject("gradle/Setter.gradle=>build.gradle"));
+    }
+
+    public void testDependencies() throws Exception {
+        mEnabled = Collections.singleton(DEPENDENCY);
+        assertEquals(""
+                + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
+                + "    buildToolsVersion \"19.0.0\"\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
+                + "    freeCompile 'com.google.guava:guava:11.0.2'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
+                + "    compile 'com.android.support:appcompat-v7:13.0.0'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 3 warnings\n",
+
+                lintProject("gradle/Dependencies.gradle=>build.gradle"));
+    }
+
+    public void testLongHandDependencies() throws Exception {
+        mEnabled = Collections.singleton(DEPENDENCY);
+        assertEquals(""
+                + "build.gradle:9: Warning: A newer version of com.android.support:support-v4 than 19.0 is available: 21.0.2 [GradleDependency]\n"
+                + "    compile group: 'com.android.support', name: 'support-v4', version: '19.0'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject("gradle/DependenciesProps.gradle=>build.gradle"));
+    }
+
+    public void testDependenciesMinSdkVersion() throws Exception {
+        mEnabled = Collections.singleton(DEPENDENCY);
+        assertEquals(""
+                + "build.gradle:13: Warning: Using the appcompat library when minSdkVersion >= 14 and compileSdkVersion < 21 is not necessary [GradleDependency]\n"
+                + "    compile 'com.android.support:appcompat-v7:+'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject("gradle/Dependencies14.gradle=>build.gradle"));
+    }
+
+    public void testDependenciesMinSdkVersionLollipop() throws Exception {
+        mEnabled = Collections.singleton(DEPENDENCY);
+        assertEquals("No warnings.",
+                lintProject("gradle/Dependencies14_21.gradle=>build.gradle"));
+    }
+
+    public void testDependenciesNoMicroVersion() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=77594
+        mEnabled = Collections.singleton(DEPENDENCY);
+        assertEquals(""
+                + "build.gradle:13: Warning: A newer version of com.google.code.gson:gson than 2.2 is available: 2.3 [GradleDependency]\n"
+                + "    compile 'com.google.code.gson:gson:2.2'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject("gradle/DependenciesGson.gradle=>build.gradle"));
+    }
+
+    public void testPaths() throws Exception {
+        mEnabled = Collections.singleton(PATH);
+        assertEquals(""
+                        + "build.gradle:4: Warning: Do not use Windows file separators in .gradle files; use / instead [GradlePath]\n"
+                        + "    compile files('my\\\\libs\\\\http.jar')\n"
+                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "build.gradle:5: Warning: Avoid using absolute paths in .gradle files [GradlePath]\n"
+                        + "    compile files('/libs/android-support-v4.jar')\n"
+                        + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 2 warnings\n",
+
+                lintProject("gradle/Paths.gradle=>build.gradle"));
+    }
+
+    public void testIdSuffix() throws Exception {
+        mEnabled = Collections.singleton(PATH);
+        assertEquals(""
+                        + "build.gradle:6: Warning: Package suffix should probably start with a \".\" [GradlePath]\n"
+                        + "            applicationIdSuffix \"debug\"\n"
+                        + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 1 warnings\n",
+
+                lintProject("gradle/IdSuffix.gradle=>build.gradle"));
+    }
+
+    public void testPackage() throws Exception {
+        mEnabled = Collections.singleton(DEPRECATED);
+        assertEquals(""
+                + "build.gradle:5: Warning: Deprecated: Replace 'packageName' with 'applicationId' [GradleDeprecated]\n"
+                + "        packageName 'my.pkg'\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~\n"
+                + "build.gradle:9: Warning: Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix' [GradleDeprecated]\n"
+                + "            packageNameSuffix \".debug\"\n"
+                + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 2 warnings\n",
+
+                lintProject("gradle/Package.gradle=>build.gradle"));
+    }
+
+    public void testPlus() throws Exception {
+        mEnabled = Collections.singleton(PLUS);
+        assertEquals(""
+                + "build.gradle:9: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
+                + "    compile 'com.android.support:appcompat-v7:+'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "build.gradle:10: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:support-v4:21.0.+) [GradleDynamicVersion]\n"
+                + "    compile group: 'com.android.support', name: 'support-v4', version: '21.0.+'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 2 warnings\n",
+
+                lintProject("gradle/Plus.gradle=>build.gradle"));
+    }
+
+    public void testStringInt() throws Exception {
+        mEnabled = Collections.singleton(STRING_INTEGER);
+        assertEquals(""
+                        + "build.gradle:4: Error: Use an integer rather than a string here (replace '19' with just 19) [StringShouldBeInt]\n"
+                        + "    compileSdkVersion '19'\n"
+                        + "    ~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "build.gradle:7: Error: Use an integer rather than a string here (replace '8' with just 8) [StringShouldBeInt]\n"
+                        + "        minSdkVersion '8'\n"
+                        + "        ~~~~~~~~~~~~~~~~~\n"
+                        + "build.gradle:8: Error: Use an integer rather than a string here (replace '16' with just 16) [StringShouldBeInt]\n"
+                        + "        targetSdkVersion '16'\n"
+                        + "        ~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "3 errors, 0 warnings\n",
+
+                lintProject("gradle/StringInt.gradle=>build.gradle"));
+    }
+
+    public void testSuppressLine2() throws Exception {
+        mEnabled = null;
+        assertEquals("No warnings.",
+
+                lintProject("gradle/SuppressLine2.gradle=>build.gradle"));
+    }
+
+    public void testDeprecatedPluginId() throws Exception {
+        mEnabled = Sets.newHashSet(DEPRECATED);
+        assertEquals(""
+                        + "build.gradle:4: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
+                        + "apply plugin: 'android'\n"
+                        + "~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "build.gradle:5: Warning: 'android-library' is deprecated; use 'com.android.library' instead [GradleDeprecated]\n"
+                        + "apply plugin: 'android-library'\n"
+                        + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 2 warnings\n",
+
+                lintProject("gradle/DeprecatedPluginId.gradle=>build.gradle"));
+    }
+
+    public void testIgnoresGStringsInDependencies() throws Exception {
+        mEnabled = null;
+        assertEquals("No warnings.",
+
+                lintProject("gradle/IgnoresGStringsInDependencies.gradle=>build.gradle"));
+    }
+
+    public void testAccidentalOctal() throws Exception {
+        mEnabled = Collections.singleton(ACCIDENTAL_OCTAL);
+        assertEquals(""
+                + "build.gradle:13: Error: The leading 0 turns this number into octal which is probably not what was intended (interpreted as 8) [AccidentalOctal]\n"
+                + "        versionCode 010\n"
+                + "        ~~~~~~~~~~~~~~~\n"
+                + "build.gradle:16: Error: The leading 0 turns this number into octal which is probably not what was intended (and it is not a valid octal number) [AccidentalOctal]\n"
+                + "        versionCode 01 // line suffix comments are not handled correctly\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "2 errors, 0 warnings\n",
+
+                lintProject("gradle/AccidentalOctal.gradle=>build.gradle"));
+    }
+
+    public void testBadPlayServicesVersion() throws Exception {
+        mEnabled = Collections.singleton(COMPATIBILITY);
+        assertEquals(""
+                + "build.gradle:5: Error: Version 5.2.08 should not be used; the app can not be published with this version. Use version 6.1.71 instead. [GradleCompatible]\n"
+                + "    compile 'com.google.android.gms:play-services:5.2.08'\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+
+                lintProject("gradle/PlayServices.gradle=>build.gradle"));
+    }
+
+    public void testRemoteVersions() throws Exception {
+        mEnabled = Collections.singleton(REMOTE_VERSION);
+        try {
+            HashMap<String, String> data = Maps.newHashMap();
+            GradleDetector.sMockData = data;
+            data.put("http://search.maven.org/solrsearch/select?q=g:%22joda-time%22+AND+a:%22joda-time%22&core=gav&rows=1&wt=json",
+                    "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"joda-time\\\" AND a:\\\"joda-time\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":17,\"start\":0,\"docs\":[{\"id\":\"joda-time:joda-time:2.3\",\"g\":\"joda-time\",\"a\":\"joda-time\",\"v\":\"2.3\",\"p\":\"jar\",\"timestamp\":1376674285000,\"tags\":[\"replace\",\"time\",\"library\",\"date\",\"handling\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\".pom\"]}]}}");
+            data.put("http://search.maven.org/solrsearch/select?q=g:%22com.squareup.dagger%22+AND+a:%22dagger%22&core=gav&rows=1&wt=json",
+                    "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.squareup.dagger\\\" AND a:\\\"dagger\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":5,\"start\":0,\"docs\":[{\"id\":\"com.squareup.dagger:dagger:1.2.1\",\"g\":\"com.squareup.dagger\",\"a\":\"dagger\",\"v\":\"1.2.1\",\"p\":\"jar\",\"timestamp\":1392614597000,\"tags\":[\"dependency\",\"android\",\"injector\",\"java\",\"fast\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\"-tests.jar\",\".jar\",\".pom\"]}]}}");
+
+            assertEquals(""
+                    + "build.gradle:9: Warning: A newer version of joda-time:joda-time than 2.1 is available: 2.3 [NewerVersionAvailable]\n"
+                    + "    compile 'joda-time:joda-time:2.1'\n"
+                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                    + "build.gradle:10: Warning: A newer version of com.squareup.dagger:dagger than 1.2.0 is available: 1.2.1 [NewerVersionAvailable]\n"
+                    + "    compile 'com.squareup.dagger:dagger:1.2.0'\n"
+                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                    + "0 errors, 2 warnings\n",
+
+                    lintProject("gradle/RemoteVersions.gradle=>build.gradle"));
+        } finally {
+            GradleDetector.sMockData = null;
+        }
+    }
+
+    public void testRemoteVersionsWithPreviews() throws Exception {
+        // If the most recent version is a rc version, query for all versions
+        mEnabled = Collections.singleton(REMOTE_VERSION);
+        try {
+            HashMap<String, String> data = Maps.newHashMap();
+            GradleDetector.sMockData = data;
+            data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&rows=1&wt=json",
+                    "{\"responseHeader\":{\"status\":0,\"QTime\":0,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
+            data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&wt=json",
+                    "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0\",\"p\":\"bundle\",\"timestamp\":1398199666000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc2\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc2\",\"p\":\"bundle\",\"timestamp\":1397162341000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc1\",\"p\":\"bundle\",\"timestamp\":1396985408000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0.1\",\"p\":\"bundle\",\"timestamp\":1391467528000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0\",\"p\":\"bundle\",\"timestamp\":1389995088000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0-rc1\",\"p\":\"bundle\",\"timestamp\":1387495574000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:15.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0\",\"p\":\"bundle\",\"timestamp\":1378497169000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\",\"-cdi1.0.jar\"]},{\"id\":\"com.google.guava:guava:15.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0-rc1\",\"p\":\"bundle\",\"timestamp\":1377542588000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:14.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"14.0.1\",\"p\":\"bundle\",\"timestamp\":1363305439000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
+
+            assertEquals(""
+                    + "build.gradle:9: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 17.0 [NewerVersionAvailable]\n"
+                    + "    compile 'com.google.guava:guava:11.0.2'\n"
+                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                    + "build.gradle:10: Warning: A newer version of com.google.guava:guava than 16.0-rc1 is available: 18.0.0-rc1 [NewerVersionAvailable]\n"
+                    + "    compile 'com.google.guava:guava:16.0-rc1'\n"
+                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                    + "0 errors, 2 warnings\n",
+
+                    lintProject("gradle/RemoteVersions2.gradle=>build.gradle"));
+        } finally {
+            GradleDetector.sMockData = null;
+        }
+    }
+
+    public void testPreviewVersions() throws Exception {
+        mEnabled = Collections.singleton(DEPENDENCY);
+        // This test only works when SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION contains
+        // a preview string:
+        if (!GRADLE_PLUGIN_RECOMMENDED_VERSION.startsWith("1.0.0-rc")) {
+            return;
+        }
+        assertEquals(""
+                        + "build.gradle:6: Warning: A newer version of com.android.tools.build:gradle than 1.0.0-rc0 is available: " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [GradleDependency]\n"
+                        + "        classpath 'com.android.tools.build:gradle:1.0.0-rc0'\n"
+                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 1 warnings\n",
+
+                lintProject("gradle/PreviewDependencies.gradle=>build.gradle"));
+    }
+
+
+    public void testDependenciesInVariables() throws Exception {
+        mEnabled = Collections.singleton(DEPENDENCY);
+        assertEquals(""
+                    + "build.gradle:10: Warning: A newer version of com.google.android.gms:play-services-wearable than 5.0.77 is available: 6.1.71 [GradleDependency]\n"
+                    + "    compile \"com.google.android.gms:play-services-wearable:${GPS_VERSION}\"\n"
+                    + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                    + "0 errors, 1 warnings\n",
+
+                lintProject("gradle/DependenciesVariable.gradle=>build.gradle"));
+    }
+
+    @Override
+    protected void checkReportedError(@NonNull Context context, @NonNull Issue issue,
+            @NonNull Severity severity, @Nullable Location location, @NonNull String message) {
+        if (issue == DEPENDENCY && message.startsWith("Using the appcompat library when ")) {
+            // No data embedded in this specific message
+            return;
+        }
+
+        // Issues we're supporting getOldFrom
+        if (issue == DEPENDENCY
+                || issue == STRING_INTEGER
+                || issue == DEPRECATED
+                || issue == PLUS) {
+            assertNotNull("Could not extract message tokens from " + message,
+                    GradleDetector.getOldValue(issue, message, TEXT));
+        }
+
+        if (issue == DEPENDENCY
+                || issue == STRING_INTEGER
+                || issue == DEPRECATED) {
+            assertNotNull("Could not extract message tokens from " + message,
+                    GradleDetector.getNewValue(issue, message, TEXT));
+        }
+
+        if (issue == COMPATIBILITY) {
+            if (message.startsWith("Version ")) {
+                assertNotNull("Could not extract message tokens from " + message,
+                        GradleDetector.getNewValue(issue, message, TEXT));
+            }
+        }
+    }
+
+    public void testGetNamedDependency() {
+        assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
+                "group: 'com.android.support', name: 'support-v4', version: '21.0.+'"
+        ));
+        assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
+                "name:'support-v4', group: \"com.android.support\", version: '21.0.+'"
+        ));
+        assertEquals("junit:junit:4.+", getNamedDependency(
+                "group: 'junit', name: 'junit', version: '4.+'"
+        ));
+        assertEquals("com.android.support:support-v4:19.0.+", getNamedDependency(
+                "group: 'com.android.support', name: 'support-v4', version: '19.0.+'"
+        ));
+        assertEquals("com.google.guava:guava:11.0.1", getNamedDependency(
+                "group: 'com.google.guava', name: 'guava', version: '11.0.1', transitive: false"
+        ));
+        assertEquals("com.google.api-client:google-api-client:1.6.0-beta", getNamedDependency(
+                "group: 'com.google.api-client', name: 'google-api-client', version: '1.6.0-beta', transitive: false"
+        ));
+        assertEquals("org.robolectric:robolectric:2.3-SNAPSHOT", getNamedDependency(
+                "group: 'org.robolectric', name: 'robolectric', version: '2.3-SNAPSHOT'"
+        ));
+    }
+
+    // -------------------------------------------------------------------------------------------
+    // Test infrastructure below here
+    // -------------------------------------------------------------------------------------------
+
+    static final Implementation IMPLEMENTATION = new Implementation(
+            GroovyGradleDetector.class,
+            Scope.GRADLE_SCOPE);
+    static {
+        for (Issue issue : new BuiltinIssueRegistry().getIssues()) {
+            if (issue.getImplementation().getDetectorClass() == GradleDetector.class) {
+                issue.setImplementation(IMPLEMENTATION);
+            }
+        }
+    }
+
+    @Override
+    protected Detector getDetector() {
+        return new GroovyGradleDetector();
+    }
+
+    private Set<Issue> mEnabled;
+
+    @Override
+    protected TestConfiguration getConfiguration(LintClient client, Project project) {
+        return new TestConfiguration(client, project, null) {
+            @Override
+            public boolean isEnabled(@NonNull Issue issue) {
+                return super.isEnabled(issue) && (mEnabled == null || mEnabled.contains(issue));
+            }
+        };
+    }
+
+    @Override
+    protected TestLintClient createClient() {
+        return new TestLintClient() {
+            @Nullable
+            @Override
+            public File getSdkHome() {
+                return getMockSupportLibraryInstallation();
+            }
+
+            @NonNull
+            @Override
+            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                if (!"testDependenciesInVariables".equals(getName())) {
+                    return super.createProject(dir, referenceDir);
+                }
+
+                return new Project(this, dir, referenceDir) {
+                    @Override
+                    public boolean isGradleProject() {
+                        return true;
+                    }
+
+                    @Nullable
+                    @Override
+                    public Variant getCurrentVariant() {
+                        /*
+                        Simulate variant which has an AndroidLibrary with
+                        resolved coordinates
+
+                        com.google.android.gms:play-services-wearable:5.0.77"
+                         */
+                        MavenCoordinates coordinates = mock(MavenCoordinates.class);
+                        when(coordinates.getGroupId()).thenReturn("com.google.android.gms");
+                        when(coordinates.getArtifactId()).thenReturn("play-services-wearable");
+                        when(coordinates.getVersion()).thenReturn("5.0.77");
+
+                        AndroidLibrary library = mock(AndroidLibrary.class);
+                        when(library.getResolvedCoordinates()).thenReturn(coordinates);
+                        List<AndroidLibrary> libraries = Collections.singletonList(library);
+
+                        Dependencies dependencies = mock(Dependencies.class);
+                        when(dependencies.getLibraries()).thenReturn(libraries);
+
+                        AndroidArtifact artifact = mock(AndroidArtifact.class);
+                        when(artifact.getDependencies()).thenReturn(dependencies);
+
+                        Variant variant = mock(Variant.class);
+                        when(variant.getMainArtifact()).thenReturn(artifact);
+                        return variant;
+                    }
+                };
+            }
+        };
+    }
+
+    // Copy of com.android.build.gradle.tasks.GroovyGradleDetector (with "static" added as
+    // a modifier, and the unused field IMPLEMENTATION removed, and with fail(t.toString())
+    // inserted into visitBuildScript's catch handler.
+    //
+    // THIS CODE DUPLICATION IS NOT AN IDEAL SITUATION! But, it's preferable to a lack of
+    // tests.
+    //
+    // A more proper fix would be to extract the groovy detector into a library shared by
+    // the testing framework and the gradle plugin.
+
+    public static class GroovyGradleDetector extends GradleDetector {
+        @Override
+        public void visitBuildScript(@NonNull final Context context, Map<String, Object> sharedData) {
+            try {
+                visitQuietly(context, sharedData);
+            } catch (Throwable t) {
+                // ignore
+                // Parsing the build script can involve class loading that we sometimes can't
+                // handle. This happens for example when running lint in build-system/tests/api/.
+                // This is a lint limitation rather than a user error, so don't complain
+                // about these. Consider reporting a Issue#LINT_ERROR.
+                fail(t.toString());
+            }
+        }
+
+        private void visitQuietly(@NonNull final Context context,
+                @SuppressWarnings("UnusedParameters") Map<String, Object> sharedData) {
+            String source = context.getContents();
+            if (source == null) {
+                return;
+            }
+
+            List<ASTNode> astNodes = new AstBuilder().buildFromString(source);
+            GroovyCodeVisitor visitor = new CodeVisitorSupport() {
+                private List<MethodCallExpression> mMethodCallStack = Lists.newArrayList();
+                @Override
+                public void visitMethodCallExpression(MethodCallExpression expression) {
+                    mMethodCallStack.add(expression);
+                    super.visitMethodCallExpression(expression);
+                    Expression arguments = expression.getArguments();
+                    String parent = expression.getMethodAsString();
+                    String parentParent = getParentParent();
+                    if (arguments instanceof ArgumentListExpression) {
+                        ArgumentListExpression ale = (ArgumentListExpression)arguments;
+                        List<Expression> expressions = ale.getExpressions();
+                        if (expressions.size() == 1 &&
+                                expressions.get(0) instanceof ClosureExpression) {
+                            if (isInterestingBlock(parent, parentParent)) {
+                                ClosureExpression closureExpression =
+                                        (ClosureExpression)expressions.get(0);
+                                Statement block = closureExpression.getCode();
+                                if (block instanceof BlockStatement) {
+                                    BlockStatement bs = (BlockStatement)block;
+                                    for (Statement statement : bs.getStatements()) {
+                                        if (statement instanceof ExpressionStatement) {
+                                            ExpressionStatement e = (ExpressionStatement)statement;
+                                            if (e.getExpression() instanceof MethodCallExpression) {
+                                                checkDslProperty(parent,
+                                                        (MethodCallExpression)e.getExpression(),
+                                                        parentParent);
+                                            }
+                                        } else if (statement instanceof ReturnStatement) {
+                                            // Single item in block
+                                            ReturnStatement e = (ReturnStatement)statement;
+                                            if (e.getExpression() instanceof MethodCallExpression) {
+                                                checkDslProperty(parent,
+                                                        (MethodCallExpression)e.getExpression(),
+                                                        parentParent);
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    } else if (arguments instanceof TupleExpression) {
+                        if (isInterestingStatement(parent, parentParent)) {
+                            TupleExpression te = (TupleExpression) arguments;
+                            Map<String, String> namedArguments = Maps.newHashMap();
+                            List<String> unnamedArguments = Lists.newArrayList();
+                            for (Expression subExpr : te.getExpressions()) {
+                                if (subExpr instanceof NamedArgumentListExpression) {
+                                    NamedArgumentListExpression nale = (NamedArgumentListExpression) subExpr;
+                                    for (MapEntryExpression mae : nale.getMapEntryExpressions()) {
+                                        namedArguments.put(mae.getKeyExpression().getText(),
+                                                mae.getValueExpression().getText());
+                                    }
+                                }
+                            }
+                            checkMethodCall(context, parent, parentParent, namedArguments, unnamedArguments, expression);
+                        }
+                    }
+                    assert !mMethodCallStack.isEmpty();
+                    assert mMethodCallStack.get(mMethodCallStack.size() - 1) == expression;
+                    mMethodCallStack.remove(mMethodCallStack.size() - 1);
+                }
+
+                private String getParentParent() {
+                    for (int i = mMethodCallStack.size() - 2; i >= 0; i--) {
+                        MethodCallExpression expression = mMethodCallStack.get(i);
+                        Expression arguments = expression.getArguments();
+                        if (arguments instanceof ArgumentListExpression) {
+                            ArgumentListExpression ale = (ArgumentListExpression)arguments;
+                            List<Expression> expressions = ale.getExpressions();
+                            if (expressions.size() == 1 &&
+                                    expressions.get(0) instanceof ClosureExpression) {
+                                return expression.getMethodAsString();
+                            }
+                        }
+                    }
+
+                    return null;
+                }
+
+                private void checkDslProperty(String parent, MethodCallExpression c,
+                        String parentParent) {
+                    String property = c.getMethodAsString();
+                    if (isInterestingProperty(property, parent, getParentParent())) {
+                        String value = getText(c.getArguments());
+                        checkDslPropertyAssignment(context, property, value, parent, parentParent, c, c);
+                    }
+                }
+
+                private String getText(ASTNode node) {
+                    String source = context.getContents();
+                    Pair<Integer, Integer> offsets = getOffsets(node, context);
+                    return source.substring(offsets.getFirst(), offsets.getSecond());
+                }
+            };
+
+            for (ASTNode node : astNodes) {
+                node.visit(visitor);
+            }
+        }
+
+        @NonNull
+        private static Pair<Integer, Integer> getOffsets(ASTNode node, Context context) {
+            if (node.getLastLineNumber() == -1 && node instanceof TupleExpression) {
+                // Workaround: TupleExpressions yield bogus offsets, so use its
+                // children instead
+                TupleExpression exp = (TupleExpression) node;
+                List<Expression> expressions = exp.getExpressions();
+                if (!expressions.isEmpty()) {
+                    return Pair.of(
+                        getOffsets(expressions.get(0), context).getFirst(),
+                        getOffsets(expressions.get(expressions.size() - 1), context).getSecond());
+                }
+            }
+            String source = context.getContents();
+            assert source != null; // because we successfully parsed
+            int start = 0;
+            int end = source.length();
+            int line = 1;
+            int startLine = node.getLineNumber();
+            int startColumn = node.getColumnNumber();
+            int endLine = node.getLastLineNumber();
+            int endColumn = node.getLastColumnNumber();
+            int column = 1;
+            for (int index = 0, len = end; index < len; index++) {
+                if (line == startLine && column == startColumn) {
+                    start = index;
+                }
+                if (line == endLine && column == endColumn) {
+                    end = index;
+                    break;
+                }
+
+                char c = source.charAt(index);
+                if (c == '\n') {
+                    line++;
+                    column = 1;
+                } else {
+                    column++;
+                }
+            }
+
+            return Pair.of(start, end);
+        }
+
+        @Override
+        protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
+            ASTNode node = (ASTNode) cookie;
+            Pair<Integer, Integer> offsets = getOffsets(node, context);
+            return offsets.getFirst();
+        }
+
+        @Override
+        protected Location createLocation(@NonNull Context context, @NonNull Object cookie) {
+            ASTNode node = (ASTNode) cookie;
+            Pair<Integer, Integer> offsets = getOffsets(node, context);
+            int fromLine = node.getLineNumber() - 1;
+            int fromColumn = node.getColumnNumber() - 1;
+            int toLine = node.getLastLineNumber() - 1;
+            int toColumn = node.getLastColumnNumber() - 1;
+            return Location.create(context.file,
+                    new DefaultPosition(fromLine, fromColumn, offsets.getFirst()),
+                    new DefaultPosition(toLine, toColumn, offsets.getSecond()));
+        }
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
new file mode 100644
index 0000000..aa52ba2
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
@@ -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.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings({"javadoc", "ClassNameDiffersFromFileName"})
+public class HandlerDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new HandlerDetector();
+    }
+
+    public void testRegistered() throws Exception {
+        assertEquals(
+            "src/test/pkg/HandlerTest.java:12: Warning: This Handler class should be static or leaks might occur (test.pkg.HandlerTest.Inner) [HandlerLeak]\n" +
+            "    public class Inner extends Handler { // ERROR\n" +
+            "                 ~~~~~\n" +
+            "src/test/pkg/HandlerTest.java:18: Warning: This Handler class should be static or leaks might occur (new android.os.Handler(){}) [HandlerLeak]\n" +
+            "        Handler anonymous = new Handler() { // ERROR\n" +
+            "                                          ^\n" +
+            "0 errors, 2 warnings\n",
+
+            lintProject(
+                "bytecode/HandlerTest.java.txt=>src/test/pkg/HandlerTest.java",
+                "bytecode/HandlerTest.class.data=>bin/classes/test/pkg/HandlerTest.class",
+                "bytecode/HandlerTest$Inner.class.data=>bin/classes/test/pkg/HandlerTest$Inner.class",
+                "bytecode/HandlerTest$StaticInner.class.data=>bin/classes/test/pkg/HandlerTest$StaticInner.class",
+                "bytecode/HandlerTest$WithArbitraryLooper.class.data=>bin/classes/test/pkg/HandlerTest$WithArbitraryLooper.class",
+                "bytecode/HandlerTest$1.class.data=>bin/classes/test/pkg/HandlerTest$1.class",
+                "bytecode/HandlerTest$2.class.data=>bin/classes/test/pkg/HandlerTest$2.class"));
+    }
+
+    public void testSuppress() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(
+                        java("src/test/pkg/CheckActivity.java", ""
+                                + "package test.pkg;\n"
+                                + "import android.annotation.SuppressLint;\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.os.Handler;\n"
+                                + "import android.os.Message;\n"
+                                + "\n"
+                                + "public class CheckActivity extends Activity {\n"
+                                + "\n"
+                                + "    @SuppressWarnings(\"unused\")\n"
+                                + "    @SuppressLint(\"HandlerLeak\")\n"
+                                + "    Handler handler = new Handler() {\n"
+                                + "\n"
+                                + "        public void handleMessage(Message msg) {\n"
+                                + "\n"
+                                + "        }\n"
+                                + "    };\n"
+                                + "\n"
+                                + "}")
+                ));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
new file mode 100644
index 0000000..17703af
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
@@ -0,0 +1,812 @@
+/*
+ * 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.tools.lint.checks;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.FilterData;
+import com.android.build.OutputFile;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidArtifactOutput;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.ProductFlavorContainer;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.client.api.LintDriver;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.mockito.stubbing.OngoingStubbing;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("javadoc")
+public class IconDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new IconDetector();
+    }
+
+    private Set<Issue> mEnabled = new HashSet<Issue>();
+    private boolean mAbbreviate;
+
+    private static final Set<Issue> ALL = new HashSet<Issue>();
+    static {
+        ALL.add(IconDetector.DUPLICATES_CONFIGURATIONS);
+        ALL.add(IconDetector.DUPLICATES_NAMES);
+        ALL.add(IconDetector.GIF_USAGE);
+        ALL.add(IconDetector.ICON_DENSITIES);
+        ALL.add(IconDetector.ICON_DIP_SIZE);
+        ALL.add(IconDetector.ICON_EXTENSION);
+        ALL.add(IconDetector.ICON_LOCATION);
+        ALL.add(IconDetector.ICON_MISSING_FOLDER);
+        ALL.add(IconDetector.ICON_NODPI);
+        ALL.add(IconDetector.ICON_COLORS);
+        ALL.add(IconDetector.ICON_XML_AND_PNG);
+        ALL.add(IconDetector.ICON_LAUNCHER_SHAPE);
+        ALL.add(IconDetector.ICON_MIX_9PNG);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mAbbreviate = true;
+    }
+
+    @Override
+    protected void configureDriver(LintDriver driver) {
+        driver.setAbbreviating(mAbbreviate);
+    }
+
+    @Override
+    protected TestConfiguration getConfiguration(LintClient client, Project project) {
+        return new TestConfiguration(client, project, null) {
+            @Override
+            public boolean isEnabled(@NonNull Issue issue) {
+                return super.isEnabled(issue) && mEnabled.contains(issue);
+            }
+        };
+    }
+
+    public void test() throws Exception {
+        mEnabled = ALL;
+        assertEquals(
+            "res/drawable-mdpi/sample_icon.gif: Warning: Using the .gif format for bitmaps is discouraged [GifUsage]\n" +
+            "res/drawable/ic_launcher.png: Warning: The ic_launcher.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable [IconDuplicatesConfig]\n" +
+            "    res/drawable-mdpi/ic_launcher.png: <No location-specific message\n" +
+            "res/drawable/ic_launcher.png: Warning: Found bitmap drawable res/drawable/ic_launcher.png in densityless folder [IconLocation]\n" +
+            "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n" +
+            "res: Warning: Missing density variation folders in res: drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+            "0 errors, 5 warnings\n" +
+            "",
+
+            lintProject(
+                    // Use minSDK4 to ensure that we get warnings about missing drawables
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "res/drawable/ic_launcher.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
+                    "res/drawable-mdpi/sample_icon.gif",
+                    // Make a dummy file named .svn to make sure it doesn't get seen as
+                    // an icon name
+                    "res/drawable-mdpi/sample_icon.gif=>res/drawable-hdpi/.svn",
+                    "res/drawable-hdpi/ic_launcher.png"));
+    }
+
+    public void testMixed() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_XML_AND_PNG);
+        assertEquals(
+            "res/drawable/background.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/background.png, res/drawable/background.xml [IconXmlAndPng]\n" +
+            "    res/drawable-mdpi/background.png: <No location-specific message\n" +
+            "0 errors, 1 warnings\n",
+
+            lintProject(
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "apicheck/minsdk4.xml=>res/drawable/background.xml",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/background.png"));
+    }
+
+    public void testApi1() throws Exception {
+        mEnabled = ALL;
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    // manifest file which specifies uses sdk = 2
+                    "apicheck/minsdk2.xml=>AndroidManifest.xml",
+                    "res/drawable/ic_launcher.png"));
+    }
+
+    public void test2() throws Exception {
+        mEnabled = ALL;
+        assertEquals(
+            "res/drawable-hdpi/other.9.png: Warning: The following unrelated icon files have identical contents: appwidget_bg.9.png, other.9.png [IconDuplicates]\n" +
+            "    res/drawable-hdpi/appwidget_bg.9.png: <No location-specific message\n" +
+            "res/drawable-hdpi/unrelated.png: Warning: The following unrelated icon files have identical contents: ic_launcher.png, unrelated.png [IconDuplicates]\n" +
+            "    res/drawable-hdpi/ic_launcher.png: <No location-specific message\n" +
+            "res: Warning: Missing density variation folders in res: drawable-mdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+            "0 errors, 3 warnings\n",
+
+            lintProject(
+                    "res/drawable-hdpi/unrelated.png",
+                    "res/drawable-hdpi/appwidget_bg.9.png",
+                    "res/drawable-hdpi/appwidget_bg_focus.9.png",
+                    "res/drawable-hdpi/other.9.png",
+                    "res/drawable-hdpi/ic_launcher.png"
+                    ));
+    }
+
+    public void testNoDpi() throws Exception {
+        mEnabled = ALL;
+        assertEquals(
+            "res/drawable-mdpi/frame.png: Warning: The following images appear in both -nodpi and in a density folder: frame.png [IconNoDpi]\n" +
+            "res/drawable-xlarge-nodpi-v11/frame.png: Warning: The frame.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable-nodpi, drawable-xlarge-nodpi-v11 [IconDuplicatesConfig]\n" +
+            "    res/drawable-nodpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
+            "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+            "0 errors, 3 warnings\n" +
+            "",
+
+            lintProject(
+                "res/drawable-mdpi/frame.png",
+                "res/drawable-nodpi/frame.png",
+                "res/drawable-xlarge-nodpi-v11/frame.png"));
+    }
+
+    public void testNoDpi2() throws Exception {
+        mEnabled = ALL;
+        // Having additional icon names in the no-dpi folder should not cause any complaints
+        assertEquals(
+            "res/drawable-xxxhdpi/frame.png: Warning: The image frame.png varies significantly in its density-independent (dip) size across the various density versions: drawable-ldpi/frame.png: 629x387 dp (472x290 px), drawable-mdpi/frame.png: 472x290 dp (472x290 px), drawable-hdpi/frame.png: 315x193 dp (472x290 px), drawable-xhdpi/frame.png: 236x145 dp (472x290 px), drawable-xxhdpi/frame.png: 157x97 dp (472x290 px), drawable-xxxhdpi/frame.png: 118x73 dp (472x290 px) [IconDipSize]\n" +
+            "    res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-xhdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-hdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-ldpi/frame.png: <No location-specific message\n" +
+            "res/drawable-xxxhdpi/frame.png: Warning: The following unrelated icon files have identical contents: frame.png, frame.png, frame.png, file1.png, file2.png, frame.png, frame.png, frame.png [IconDuplicates]\n" +
+            "    res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-xhdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-nodpi/file2.png: <No location-specific message\n" +
+            "    res/drawable-nodpi/file1.png: <No location-specific message\n" +
+            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-ldpi/frame.png: <No location-specific message\n" +
+            "    res/drawable-hdpi/frame.png: <No location-specific message\n" +
+            "0 errors, 2 warnings\n" +
+            "",
+
+            lintProject(
+                    "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-hdpi/frame.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-ldpi/frame.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-xhdpi/frame.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-xxhdpi/frame.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-xxxhdpi/frame.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file1.png",
+                    "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file2.png"));
+    }
+
+    public void testNoDpiMix() throws Exception {
+        mEnabled = ALL;
+        assertEquals(
+            "res/drawable-mdpi/frame.xml: Warning: The following images appear in both -nodpi and in a density folder: frame.png, frame.xml [IconNoDpi]\n" +
+            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
+            "res/drawable-nodpi/frame.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/frame.png, res/drawable-nodpi/frame.xml [IconXmlAndPng]\n" +
+            "    res/drawable-mdpi/frame.png: <No location-specific message\n" +
+            "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+            "0 errors, 3 warnings\n",
+
+            lintProject(
+                "res/drawable-mdpi/frame.png",
+                "res/drawable/states.xml=>res/drawable-nodpi/frame.xml"));
+    }
+
+
+    public void testMixedFormat() throws Exception {
+        mEnabled = ALL;
+        // Test having a mixture of .xml and .png resources for the same name
+        // Make sure we don't get:
+        // drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: f.png (found in drawable-mdpi)
+        // drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: f.png (found in drawable-mdpi)
+        assertEquals(
+            "res/drawable-xxxhdpi/f.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-hdpi/f.xml, res/drawable-mdpi/f.png [IconXmlAndPng]\n" +
+            "    res/drawable-xxhdpi/f.xml: <No location-specific message\n" +
+            "    res/drawable-xhdpi/f.xml: <No location-specific message\n" +
+            "    res/drawable-mdpi/f.png: <No location-specific message\n" +
+            "    res/drawable-hdpi/f.xml: <No location-specific message\n" +
+            "0 errors, 1 warnings\n",
+
+            lintProject(
+                    "res/drawable-mdpi/frame.png=>res/drawable-mdpi/f.png",
+                    "res/drawable/states.xml=>res/drawable-hdpi/f.xml",
+                    "res/drawable/states.xml=>res/drawable-xhdpi/f.xml",
+                    "res/drawable/states.xml=>res/drawable-xxhdpi/f.xml",
+                    "res/drawable/states.xml=>res/drawable-xxxhdpi/f.xml"));
+    }
+
+    public void testMisleadingFileName() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_EXTENSION);
+        assertEquals(
+            "res/drawable-mdpi/frame.gif: Warning: Misleading file extension; named .gif but the file format is png [IconExtension]\n" +
+            "res/drawable-mdpi/frame.jpg: Warning: Misleading file extension; named .jpg but the file format is png [IconExtension]\n" +
+            "res/drawable-mdpi/myjpg.png: Warning: Misleading file extension; named .png but the file format is JPEG [IconExtension]\n" +
+            "res/drawable-mdpi/sample_icon.jpeg: Warning: Misleading file extension; named .jpeg but the file format is gif [IconExtension]\n" +
+            "res/drawable-mdpi/sample_icon.jpg: Warning: Misleading file extension; named .jpg but the file format is gif [IconExtension]\n" +
+            "res/drawable-mdpi/sample_icon.png: Warning: Misleading file extension; named .png but the file format is gif [IconExtension]\n" +
+            "0 errors, 6 warnings\n",
+
+            lintProject(
+                "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpg", // VALID
+                "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpeg", // VALID
+                "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.gif",
+                "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.jpg",
+                "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.png",
+                "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpg",
+                "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpeg",
+                "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.png"));
+    }
+
+    public void testColors() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals(
+            "res/drawable-mdpi/ic_menu_my_action.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
+            "res/drawable-mdpi-v11/ic_stat_my_notification.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "res/drawable-mdpi-v9/ic_stat_my_notification2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "0 errors, 3 warnings\n",
+
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_menu_my_action.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi-v11/ic_stat_my_notification.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi-v9/ic_stat_my_notification2.png",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+    }
+
+    public void testNotActionBarIcons() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals(
+            "No warnings.",
+
+            // No Java code designates the menu as an action bar menu
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "res/menu/menu.xml",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+    }
+
+    public void testActionBarIcons() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals(
+            "res/drawable-mdpi/icon1.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
+            "res/drawable-mdpi/icon2.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
+            "0 errors, 2 warnings\n",
+
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "res/menu/menu.xml",
+                "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+    }
+
+    public void testOkActionBarIcons() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "res/menu/menu.xml",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png"));
+    }
+
+    public void testNotificationIcons() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals(
+            "res/drawable-mdpi/icon1.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "res/drawable-mdpi/icon2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "res/drawable-mdpi/icon3.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "res/drawable-mdpi/icon4.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "res/drawable-mdpi/icon5.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+            "0 errors, 5 warnings\n",
+
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon4.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon5.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon6.png", // not a notification
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon7.png", // ditto
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+    }
+
+    public void testOkNotificationIcons() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon3.png",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon4.png",
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon5.png"));
+    }
+
+    public void testExpectedSize() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_EXPECTED_SIZE);
+        assertEquals(
+            "res/drawable-mdpi/ic_launcher.png: Warning: Incorrect icon size for drawable-mdpi/ic_launcher.png: expected 48x48, but was 24x24 [IconExpectedSize]\n" +
+            "res/drawable-mdpi/icon1.png: Warning: Incorrect icon size for drawable-mdpi/icon1.png: expected 32x32, but was 48x48 [IconExpectedSize]\n" +
+            "res/drawable-mdpi/icon3.png: Warning: Incorrect icon size for drawable-mdpi/icon3.png: expected 24x24, but was 48x48 [IconExpectedSize]\n" +
+            "0 errors, 3 warnings\n",
+
+            lintProject(
+                "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
+                "res/menu/menu.xml",
+                "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
+
+                // 3 wrong-sized icons:
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
+                "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/ic_launcher.png",
+
+                // OK sizes
+                "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
+                "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/icon4.png",
+                "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
+            ));
+    }
+
+    public void testAbbreviate() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
+        assertEquals(
+            "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
+            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
+            "ic_launcher3.png... (6 more) [IconDensities]\n" +
+            "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
+            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
+            "ic_launcher3.png... (6 more) [IconDensities]\n" +
+            "0 errors, 2 warnings\n",
+
+            lintProject(
+                    // Use minSDK4 to ensure that we get warnings about missing drawables
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.webp",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
+            ));
+    }
+
+    public void testShowAll() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
+        mAbbreviate = false;
+        assertEquals(
+            "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
+            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
+            "ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
+            "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
+            "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
+            "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png," +
+            " ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
+            "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
+            "0 errors, 2 warnings\n",
+
+            lintProject(
+                    // Use minSDK4 to ensure that we get warnings about missing drawables
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
+            ));
+    }
+
+    public void testIgnoreMissingFolders() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    // Use minSDK4 to ensure that we get warnings about missing drawables
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "ignoremissing.xml=>lint.xml",
+                    "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
+                    "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
+            ));
+    }
+
+    public void testSquareLauncher() throws Exception {
+        mEnabled = Collections.singleton(IconDetector.ICON_LAUNCHER_SHAPE);
+        assertEquals(
+            "res/drawable-hdpi/ic_launcher_filled.png: Warning: Launcher icons should not fill every pixel of their square region; see the design guide for details [IconLauncherShape]\n" +
+            "0 errors, 1 warnings\n",
+
+            lintProject(
+                    "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                    "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
+                    "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
+            ));
+    }
+
+    public void testMixNinePatch() throws Exception {
+        // https://code.google.com/p/android/issues/detail?id=43075
+        mEnabled = Collections.singleton(IconDetector.ICON_MIX_9PNG);
+        assertEquals(""
+                + "res/drawable-mdpi/ic_launcher_filled.png: Warning: The files ic_launcher_filled.png and ic_launcher_filled.9.png clash; both will map to @drawable/ic_launcher_filled [IconMixedNinePatch]\n"
+                + "    res/drawable-hdpi/ic_launcher_filled.png: <No location-specific message\n"
+                + "    res/drawable-hdpi/ic_launcher_filled.9.png: <No location-specific message\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                        "res/drawable-hdpi/filled.png=>res/drawable-mdpi/ic_launcher_filled.png",
+                        "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
+                        "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.9.png",
+                        "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
+                ));
+    }
+
+    public void test67486() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=67486
+        mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+        assertEquals("No warnings.",
+
+                lintProject(
+                        "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                        "res/drawable-xhdpi/ic_stat_notify.png=>res/drawable-xhdpi/ic_stat_notify.png"
+                ));
+    }
+
+    public void testDuplicatesWithDpNames() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=74584
+        mEnabled = Collections.singleton(IconDetector.DUPLICATES_NAMES);
+        assertEquals("No warnings.",
+
+                lintProject(
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png",
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png"
+                ));
+    }
+
+    public void testClaimedSize() throws Exception {
+        // Check that icons which declare a dp size actually correspond to that dp size
+        mEnabled = Collections.singleton(IconDetector.ICON_DIP_SIZE);
+        assertEquals(""
+                + "res/drawable-xhdpi/foo_30dp.png: Warning: Suspicious file name foo_30dp.png: The implied 30 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-xhdpi folder computes to 36\u00d736 dp) [IconDipSize]\n"
+                + "res/drawable-mdpi/foo_80dp.png: Warning: Suspicious file name foo_80dp.png: The implied 80 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-mdpi folder computes to 72\u00d772 dp) [IconDipSize]\n"
+                + "0 errors, 2 warnings\n",
+
+                lintProject(
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png", // ok
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_80dp.png", // wrong
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png",  // ok
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_35dp.png",  // ~ok
+                        "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_30dp.png"  // wrong
+                ));
+    }
+
+    public void testResConfigs1() throws Exception {
+        // resConfigs in the Gradle model sets up the specific set of resource configs
+        // that are included in the packaging: we use this to limit the set of required
+        // densities
+        mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
+        assertEquals(""
+                + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        "res/drawable-mdpi/frame.png",
+                        "res/drawable-nodpi/frame.png",
+                        "res/drawable-xlarge-nodpi-v11/frame.png"));
+    }
+
+    public void testResConfigs2() throws Exception {
+        mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
+        assertEquals(""
+                + "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        // Use minSDK4 to ensure that we get warnings about missing drawables
+                        "apicheck/minsdk4.xml=>AndroidManifest.xml",
+                        "res/drawable/ic_launcher.png",
+                        "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
+                        "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher.png",
+                        "res/drawable-mdpi/sample_icon.gif",
+                        "res/drawable-hdpi/ic_launcher.png"));
+    }
+
+    public void testSplits1() throws Exception {
+        // splits in the Gradle model sets up the specific set of resource configs
+        // that are included in the packaging: we use this to limit the set of required
+        // densities
+        mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
+        assertEquals(""
+                        + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
+                        + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        "res/drawable-mdpi/frame.png",
+                        "res/drawable-nodpi/frame.png",
+                        "res/drawable-xlarge-nodpi-v11/frame.png"));
+    }
+
+    @Override
+    protected TestLintClient createClient() {
+        String testName = getName();
+        if (testName.startsWith("testResConfigs")) {
+            return createClientForTestResConfigs();
+        } else if (testName.startsWith("testSplits")) {
+            return createClientForTestSplits();
+        } else {
+            return super.createClient();
+        }
+    }
+
+    private TestLintClient createClientForTestResConfigs() {
+
+        // Set up a mock project model for the resource configuration test(s)
+        // where we provide a subset of densities to be included
+
+        return new TestLintClient() {
+            @NonNull
+            @Override
+            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                return new Project(this, dir, referenceDir) {
+                    @Override
+                    public boolean isGradleProject() {
+                        return true;
+                    }
+
+                    @Nullable
+                    @Override
+                    public AndroidProject getGradleProjectModel() {
+                        /*
+                        Simulate variant freeBetaDebug in this setup:
+                            defaultConfig {
+                                ...
+                                resConfigs "mdpi"
+                            }
+                            flavorDimensions  "pricing", "releaseType"
+                            productFlavors {
+                                beta {
+                                    flavorDimension "releaseType"
+                                    resConfig "en"
+                                    resConfigs "nodpi", "hdpi"
+                                }
+                                normal { flavorDimension "releaseType" }
+                                free { flavorDimension "pricing" }
+                                paid { flavorDimension "pricing" }
+                            }
+                         */
+                        ProductFlavor flavorFree = mock(ProductFlavor.class);
+                        when(flavorFree.getName()).thenReturn("free");
+                        when(flavorFree.getResourceConfigurations())
+                                .thenReturn(Collections.<String>emptyList());
+
+                        ProductFlavor flavorNormal = mock(ProductFlavor.class);
+                        when(flavorNormal.getName()).thenReturn("normal");
+                        when(flavorNormal.getResourceConfigurations())
+                                .thenReturn(Collections.<String>emptyList());
+
+                        ProductFlavor flavorPaid = mock(ProductFlavor.class);
+                        when(flavorPaid.getName()).thenReturn("paid");
+                        when(flavorPaid.getResourceConfigurations())
+                                .thenReturn(Collections.<String>emptyList());
+
+                        ProductFlavor flavorBeta = mock(ProductFlavor.class);
+                        when(flavorBeta.getName()).thenReturn("beta");
+                        List<String> resConfigs = Arrays.asList("hdpi", "en", "nodpi");
+                        when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
+
+                        ProductFlavor defaultFlavor = mock(ProductFlavor.class);
+                        when(defaultFlavor.getName()).thenReturn("main");
+                        when(defaultFlavor.getResourceConfigurations()).thenReturn(
+                                Collections.singleton("mdpi"));
+
+                        ProductFlavorContainer containerBeta =
+                                mock(ProductFlavorContainer.class);
+                        when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
+
+                        ProductFlavorContainer containerFree =
+                                mock(ProductFlavorContainer.class);
+                        when(containerFree.getProductFlavor()).thenReturn(flavorFree);
+
+                        ProductFlavorContainer containerPaid =
+                                mock(ProductFlavorContainer.class);
+                        when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
+
+                        ProductFlavorContainer containerNormal =
+                                mock(ProductFlavorContainer.class);
+                        when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
+
+                        ProductFlavorContainer defaultContainer =
+                                mock(ProductFlavorContainer.class);
+                        when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
+
+                        List<ProductFlavorContainer> containers = Arrays.asList(
+                                containerPaid, containerFree, containerNormal, containerBeta
+                        );
+
+                        AndroidProject project = mock(AndroidProject.class);
+                        when(project.getProductFlavors()).thenReturn(containers);
+                        when(project.getDefaultConfig()).thenReturn(defaultContainer);
+                        return project;
+                    }
+
+                    @Nullable
+                    @Override
+                    public Variant getCurrentVariant() {
+                        List<String> productFlavorNames = Arrays.asList("free", "beta");
+                        Variant mock = mock(Variant.class);
+                        when(mock.getProductFlavors()).thenReturn(productFlavorNames);
+                        return mock;
+                    }
+                };
+            }
+        };
+    }
+
+    private TestLintClient createClientForTestSplits() {
+
+        // Set up a mock project model for the resource configuration test(s)
+        // where we provide a subset of densities to be included
+
+        return new TestLintClient() {
+            @NonNull
+            @Override
+            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                return new Project(this, dir, referenceDir) {
+                    @Override
+                    public boolean isGradleProject() {
+                        return true;
+                    }
+
+                    @Nullable
+                    @Override
+                    public AndroidProject getGradleProjectModel() {
+                        /*
+                            Simulate variant debug in this setup:
+                            splits {
+                                density {
+                                    enable true
+                                    reset()
+                                    include "mdpi", "hdpi"
+                                }
+                            }
+                         */
+
+                        ProductFlavor defaultFlavor = mock(ProductFlavor.class);
+                        when(defaultFlavor.getName()).thenReturn("main");
+                        when(defaultFlavor.getResourceConfigurations()).thenReturn(
+                                Collections.<String>emptyList());
+
+                        ProductFlavorContainer defaultContainer =
+                                mock(ProductFlavorContainer.class);
+                        when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
+
+                        AndroidProject project = mock(AndroidProject.class);
+                        when(project.getProductFlavors()).thenReturn(
+                                Collections.<ProductFlavorContainer>emptyList());
+                        when(project.getDefaultConfig()).thenReturn(defaultContainer);
+                        return project;
+                    }
+
+                    @Nullable
+                    @Override
+                    public Variant getCurrentVariant() {
+                        Collection<AndroidArtifactOutput> outputs = Lists.newArrayList();
+
+                        outputs.add(createAndroidArtifactOutput("", ""));
+                        outputs.add(createAndroidArtifactOutput("DENSITY", "mdpi"));
+                        outputs.add(createAndroidArtifactOutput("DENSITY", "hdpi"));
+
+                        AndroidArtifact mainArtifact = mock(AndroidArtifact.class);
+                        when(mainArtifact.getOutputs()).thenReturn(outputs);
+
+                        List<String> productFlavorNames = Collections.emptyList();
+                        Variant mock = mock(Variant.class);
+                        when(mock.getProductFlavors()).thenReturn(productFlavorNames);
+                        when(mock.getMainArtifact()).thenReturn(mainArtifact);
+                        return mock;
+                    }
+
+                    private AndroidArtifactOutput createAndroidArtifactOutput(
+                            @NonNull String filterType,
+                            @NonNull String identifier) {
+                        AndroidArtifactOutput artifactOutput = mock(
+                                AndroidArtifactOutput.class);
+
+                        OutputFile outputFile = mock(OutputFile.class);
+                        if (filterType.isEmpty()) {
+                            when(outputFile.getFilterTypes())
+                                    .thenReturn(Collections.<String>emptyList());
+                            when(outputFile.getFilters())
+                                    .thenReturn(Collections.<FilterData>emptyList());
+                        } else {
+                            when(outputFile.getFilterTypes())
+                                    .thenReturn(Collections.singletonList(filterType));
+                            List<FilterData> filters = Lists.newArrayList();
+                            FilterData filter = mock(FilterData.class);
+                            when(filter.getFilterType()).thenReturn(filterType);
+                            when(filter.getIdentifier()).thenReturn(identifier);
+                            filters.add(filter);
+                            when(outputFile.getFilters()).thenReturn(filters);
+                        }
+
+                        // Work around wildcard capture
+                        //when(artifactOutput.getOutputs()).thenReturn(outputFiles);
+                        List<OutputFile> outputFiles = Collections.singletonList(outputFile);
+                        OngoingStubbing<? extends Collection<? extends OutputFile>> when = when(
+                                artifactOutput.getOutputs());
+                        //noinspection unchecked,RedundantCast
+                        ((OngoingStubbing<Collection<? extends OutputFile>>) (OngoingStubbing<?>) when)
+                                .thenReturn(outputFiles);
+
+                        return artifactOutput;
+                    }
+                };
+            }
+        };
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
new file mode 100644
index 0000000..31fedb1
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
@@ -0,0 +1,945 @@
+/*
+ * 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.tools.lint.checks;
+
+import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.BuildType;
+import com.android.builder.model.BuildTypeContainer;
+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 com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("javadoc")
+public class ManifestDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new ManifestDetector();
+    }
+
+    private Set<Issue> mEnabled = new HashSet<Issue>();
+
+    @Override
+    protected TestConfiguration getConfiguration(LintClient client, Project project) {
+        return new TestConfiguration(client, project, null) {
+            @Override
+            public boolean isEnabled(@NonNull Issue issue) {
+                return super.isEnabled(issue) && mEnabled.contains(issue);
+            }
+        };
+    }
+
+    public void testOrderOk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ORDER);
+        assertEquals(
+                "No warnings.",
+                lintProject(
+                        "AndroidManifest.xml",
+                        "res/values/strings.xml"));
+    }
+
+    public void testBrokenOrder() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ORDER);
+        assertEquals(
+            "AndroidManifest.xml:16: Warning: <uses-sdk> tag appears after <application> tag [ManifestOrder]\n" +
+            "   <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
+            "   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "0 errors, 1 warnings\n" +
+            "",
+
+            lintProject(
+                    "broken-manifest.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testMissingUsesSdk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
+        assertEquals(
+            "AndroidManifest.xml: Warning: Manifest should specify a minimum API level with <uses-sdk android:minSdkVersion=\"?\" />; if it really supports all versions of Android set it to 1. [UsesMinSdkAttributes]\n" +
+            "0 errors, 1 warnings\n",
+            lintProject(
+                    "missingusessdk.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testMissingUsesSdkInGradle() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
+        assertEquals(""
+                + "No warnings.",
+                lintProject("missingusessdk.xml=>AndroidManifest.xml",
+                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+    }
+
+    public void testMissingMinSdk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
+        assertEquals(
+            "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a minimum API level with android:minSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
+            "    <uses-sdk android:targetSdkVersion=\"10\" />\n" +
+            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "0 errors, 1 warnings\n" +
+            "",
+            lintProject(
+                    "missingmin.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testMissingTargetSdk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
+        assertEquals(
+            "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
+            "    <uses-sdk android:minSdkVersion=\"10\" />\n" +
+            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "0 errors, 1 warnings\n",
+            lintProject(
+                    "missingtarget.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testOldTargetSdk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.TARGET_NEWER);
+        assertEquals(
+            "AndroidManifest.xml:7: Warning: Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details. [OldTargetApi]\n" +
+            "    <uses-sdk android:minSdkVersion=\"10\" android:targetSdkVersion=\"14\" />\n" +
+            "                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "0 errors, 1 warnings\n",
+            lintProject(
+                    "oldtarget.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testMultipleSdk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.MULTIPLE_USES_SDK);
+        assertEquals(
+            "AndroidManifest.xml:8: Error: There should only be a single <uses-sdk> element in the manifest: merge these together [MultipleUsesSdk]\n" +
+            "    <uses-sdk android:targetSdkVersion=\"14\" />\n" +
+            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "    AndroidManifest.xml:7: Also appears here\n" +
+            "    AndroidManifest.xml:9: Also appears here\n" +
+            "1 errors, 0 warnings\n",
+
+            lintProject(
+                    "multiplesdk.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testWrongLocation() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.WRONG_PARENT);
+        assertEquals(
+            "AndroidManifest.xml:8: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:9: Error: The <uses-permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <uses-permission />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:10: Error: The <permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <permission />\n" +
+            "       ~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:11: Error: The <permission-tree> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <permission-tree />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:12: Error: The <permission-group> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <permission-group />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:14: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <uses-sdk />\n" +
+            "       ~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:15: Error: The <uses-configuration> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <uses-configuration />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:16: Error: The <uses-feature> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <uses-feature />\n" +
+            "       ~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:17: Error: The <supports-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <supports-screens />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:18: Error: The <compatible-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <compatible-screens />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:19: Error: The <supports-gl-texture> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+            "       <supports-gl-texture />\n" +
+            "       ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:24: Error: The <uses-library> element must be a direct child of the <application> element [WrongManifestParent]\n" +
+            "   <uses-library />\n" +
+            "   ~~~~~~~~~~~~~~~~\n" +
+            "AndroidManifest.xml:25: Error: The <activity> element must be a direct child of the <application> element [WrongManifestParent]\n" +
+            "   <activity android:name=\".HelloWorld\"\n" +
+            "   ^\n" +
+            "13 errors, 0 warnings\n" +
+            "",
+
+            lintProject("broken-manifest2.xml=>AndroidManifest.xml"));
+    }
+
+    public void testDuplicateActivity() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
+        assertEquals(
+            "AndroidManifest.xml:16: Error: Duplicate registration for activity com.example.helloworld.HelloWorld [DuplicateActivity]\n" +
+            "       <activity android:name=\"com.example.helloworld.HelloWorld\"\n" +
+            "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n" +
+            "",
+
+            lintProject(
+                    "duplicate-manifest.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testDuplicateActivityAcrossSourceSets() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
+        File master = getProjectDir("MasterProject",
+                // Master project
+                "AndroidManifest.xml=>AndroidManifest.xml",
+                "multiproject/main-merge.properties=>project.properties",
+                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+        );
+        File library = getProjectDir("LibraryProject",
+                // Library project
+                "AndroidManifest.xml=>AndroidManifest.xml",
+                "multiproject/library.properties=>project.properties",
+                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+                "multiproject/strings.xml=>res/values/strings.xml"
+        );
+        assertEquals("No warnings.",
+                checkLint(Arrays.asList(master, library)));
+    }
+
+    public void testIgnoreDuplicateActivity() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "duplicate-manifest-ignore.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testAllowBackup() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(
+                "AndroidManifest.xml:9: Warning: Should explicitly set android:allowBackup to " +
+                "true or false (it's true by default, and that can have some security " +
+                "implications for the application's data) [AllowBackup]\n" +
+                "    <application\n" +
+                "    ^\n" +
+                "0 errors, 1 warnings\n",
+                lintProject(
+                        "AndroidManifest.xml",
+                        "apicheck/minsdk14.xml=>AndroidManifest.xml",
+                        "res/values/strings.xml"));
+    }
+
+    public void testAllowBackupOk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(
+                "No warnings.",
+                lintProject(
+                        "allowbackup.xml=>AndroidManifest.xml",
+                        "res/values/strings.xml"));
+    }
+
+    public void testAllowBackupOk2() throws Exception {
+        // Requires build api >= 4
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(
+                "No warnings.",
+                lintProject(
+                        "apicheck/minsdk1.xml=>AndroidManifest.xml",
+                        "res/values/strings.xml"));
+    }
+
+    public void testAllowBackupOk3() throws Exception {
+        // Not flagged in library projects
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(
+                "No warnings.",
+                lintProject(
+                        "AndroidManifest.xml",
+                        "multiproject/library.properties=>project.properties",
+                        "res/values/strings.xml"));
+    }
+
+    public void testAllowIgnore() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(
+                "No warnings.",
+                lintProject(
+                        "allowbackup_ignore.xml=>AndroidManifest.xml",
+                        "res/values/strings.xml"));
+    }
+
+    public void testDuplicatePermissions() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
+        assertEquals(
+                "AndroidManifest.xml:12: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
+                "    <permission android:name=\"bar.permission.SEND_SMS\"\n" +
+                "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+                "    AndroidManifest.xml:9: Previous permission here\n" +
+                "1 errors, 0 warnings\n",
+
+                lintProject(
+                        "duplicate_permissions1.xml=>AndroidManifest.xml",
+                        "res/values/strings.xml"));
+    }
+
+    public void testDuplicatePermissionsMultiProject() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
+
+        File master = getProjectDir("MasterProject",
+                // Master project
+                "duplicate_permissions2.xml=>AndroidManifest.xml",
+                "multiproject/main-merge.properties=>project.properties",
+                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+        );
+        File library = getProjectDir("LibraryProject",
+                // Library project
+                "duplicate_permissions3.xml=>AndroidManifest.xml",
+                "multiproject/library.properties=>project.properties",
+                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+                "multiproject/strings.xml=>res/values/strings.xml"
+        );
+        assertEquals(
+                "LibraryProject/AndroidManifest.xml:9: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
+                "    <permission android:name=\"bar.permission.SEND_SMS\"\n" +
+                "                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+                "1 errors, 0 warnings\n",
+
+           checkLint(Arrays.asList(master, library)));
+    }
+
+    public void testMissingVersion() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
+        assertEquals(""
+            + "AndroidManifest.xml:2: Warning: Should set android:versionCode to specify the application version [MissingVersion]\n"
+            + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+            + "^\n"
+            + "AndroidManifest.xml:2: Warning: Should set android:versionName to specify the application version [MissingVersion]\n"
+            + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+            + "^\n"
+            + "0 errors, 2 warnings\n",
+            lintProject("no_version.xml=>AndroidManifest.xml"));
+    }
+
+    public void testVersionNotMissingInGradleProjects() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
+        assertEquals(""
+            + "No warnings.",
+            lintProject("no_version.xml=>AndroidManifest.xml",
+                    "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+    }
+
+    public void testIllegalReference() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ILLEGAL_REFERENCE);
+        assertEquals(""
+            + "AndroidManifest.xml:4: Warning: The android:versionCode cannot be a resource url, it must be a literal integer [IllegalResourceRef]\n"
+            + "    android:versionCode=\"@dimen/versionCode\"\n"
+            + "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "AndroidManifest.xml:7: Warning: The android:minSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
+            + "    <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
+            + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "AndroidManifest.xml:7: Warning: The android:targetSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
+            + "    <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
+            + "                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "0 errors, 3 warnings\n",
+
+            lintProject("illegal_version.xml=>AndroidManifest.xml"));
+    }
+
+    public void testDuplicateUsesFeature() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
+        assertEquals(
+            "AndroidManifest.xml:11: Warning: Duplicate declaration of uses-feature android.hardware.camera [DuplicateUsesFeature]\n" +
+            "    <uses-feature android:name=\"android.hardware.camera\"/>\n" +
+            "                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "0 errors, 1 warnings\n",
+            lintProject(
+                    "duplicate_uses_feature.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testDuplicateUsesFeatureOk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
+        assertEquals(
+            "No warnings.",
+            lintProject(
+                    "duplicate_uses_feature_ok.xml=>AndroidManifest.xml",
+                    "res/values/strings.xml"));
+    }
+
+    public void testMissingApplicationIcon() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
+        assertEquals(
+            "AndroidManifest.xml:9: Warning: Should explicitly set android:icon, there is no default [MissingApplicationIcon]\n" +
+            "    <application\n" +
+            "    ^\n" +
+            "0 errors, 1 warnings\n",
+            lintProject(
+                "missing_application_icon.xml=>AndroidManifest.xml",
+                "res/values/strings.xml"));
+    }
+
+    public void testMissingApplicationIconInLibrary() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
+        assertEquals(
+            "No warnings.",
+            lintProject(
+                "missing_application_icon.xml=>AndroidManifest.xml",
+                "multiproject/library.properties=>project.properties",
+                "res/values/strings.xml"));
+    }
+
+    public void testMissingApplicationIconOk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
+        assertEquals(
+            "No warnings.",
+            lintProject(
+                "AndroidManifest.xml",
+                "res/values/strings.xml"));
+    }
+
+    public void testDeviceAdmin() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.DEVICE_ADMIN);
+        assertEquals(""
+                + "AndroidManifest.xml:31: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
+                + "            <meta-data android:name=\"android.app.device_admin\"\n"
+                + "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "AndroidManifest.xml:44: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
+                + "            <meta-data android:name=\"android.app.device_admin\"\n"
+                + "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "AndroidManifest.xml:56: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
+                + "            <meta-data android:name=\"android.app.device_admin\"\n"
+                + "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 3 warnings\n",
+                lintProject("deviceadmin.xml=>AndroidManifest.xml"));
+    }
+
+    public void testMockLocations() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
+        assertEquals(""
+                + "AndroidManifest.xml:9: Error: Mock locations should only be requested in a test or debug-specific manifest file (typically src/debug/AndroidManifest.xml) [MockLocation]\n"
+                + "    <uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\" /> \n"
+                + "                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+                lintProject(
+                        "mock_location.xml=>AndroidManifest.xml",
+                        "mock_location.xml=>debug/AndroidManifest.xml",
+                        "mock_location.xml=>test/AndroidManifest.xml",
+                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+        // TODO: When we have an instantiatable gradle model, test with real model and verify
+        // that a manifest file in a debug build type does not get flagged.
+    }
+
+    public void testMockLocationsOk() throws Exception {
+        // Not a Gradle project
+        mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
+        assertEquals(""
+                + "No warnings.",
+                lintProject(
+                        "mock_location.xml=>AndroidManifest.xml"));
+    }
+
+    public void testGradleOverrides() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
+        assertEquals(""
+                + "AndroidManifest.xml:4: Warning: This versionCode value (1) is not used; it is always overridden by the value specified in the Gradle build script (2) [GradleOverrides]\n"
+                + "    android:versionCode=\"1\"\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "AndroidManifest.xml:5: Warning: This versionName value (1.0) is not used; it is always overridden by the value specified in the Gradle build script (MyName) [GradleOverrides]\n"
+                + "    android:versionName=\"1.0\" >\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "AndroidManifest.xml:7: Warning: This minSdkVersion value (14) is not used; it is always overridden by the value specified in the Gradle build script (5) [GradleOverrides]\n"
+                + "    <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
+                + "              ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "AndroidManifest.xml:7: Warning: This targetSdkVersion value (17) is not used; it is always overridden by the value specified in the Gradle build script (16) [GradleOverrides]\n"
+                + "    <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
+                + "                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 4 warnings\n",
+                lintProject(
+                        "gradle_override.xml=>AndroidManifest.xml",
+                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+    }
+
+    public void testGradleOverridesOk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
+        // (See custom logic in #createClient which returns -1/null for the merged flavor
+        // from this test, and not from testGradleOverrides)
+        assertEquals(""
+                + "No warnings.",
+                lintProject(
+                        "gradle_override.xml=>AndroidManifest.xml",
+                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+    }
+
+    public void testManifestPackagePlaceholder() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
+        assertEquals(""
+                + "AndroidManifest.xml:3: Warning: Cannot use placeholder for the package in the manifest; set applicationId in build.gradle instead [GradleOverrides]\n"
+                + "    package=\"${packageName}\" >\n"
+                + "    ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        "gradle_override_placeholder.xml=>AndroidManifest.xml",
+                        "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+    }
+
+    public void testMipMap() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
+        assertEquals("No warnings.",
+
+                lintProject(
+                        "mipmap.xml=>AndroidManifest.xml"));
+    }
+
+    public void testMipMapWithDensityFiltering() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
+        assertEquals(""
+                        + "AndroidManifest.xml:9: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
+                        + "        android:icon=\"@drawable/ic_launcher\"\n"
+                        + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "AndroidManifest.xml:14: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
+                        + "            android:icon=\"@drawable/activity1\"\n"
+                        + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                        + "0 errors, 2 warnings\n",
+
+                lintProject(
+                        "mipmap.xml=>AndroidManifest.xml"));
+    }
+
+    public void testFullBackupContentBoolean() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals("No warnings.",
+
+                lintProjectIncrementally(
+                        "AndroidManifest.xml",
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:allowBackup=\"true\"\n"
+                                + "        android:fullBackupContent=\"true\"\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >\n"
+                                + "    </application>\n"
+                                + "\n"
+                                + "</manifest>\n")));
+    }
+
+    public void testFullBackupContentMissing() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(""
+                + "AndroidManifest.xml:7: Warning: Missing <full-backup-content> resource [AllowBackup]\n"
+                + "        android:fullBackupContent=\"@xml/backup\"\n"
+                + "                                   ~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProjectIncrementally(
+                        "AndroidManifest.xml",
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:allowBackup=\"true\"\n"
+                                + "        android:fullBackupContent=\"@xml/backup\"\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >\n"
+                                + "    </application>\n"
+                        + "\n"
+                        + "</manifest>\n")));
+    }
+
+    public void testFullBackupContentOk() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals("No warnings.",
+
+                lintProjectIncrementally(
+                        "AndroidManifest.xml",
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:allowBackup=\"true\"\n"
+                                + "        android:fullBackupContent=\"@xml/backup\"\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >\n"
+                                + "    </application>\n"
+                                + "\n"
+                                + "</manifest>\n"),
+                        xml("res/xml/backup.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<full-backup-content>\n"
+                                + "     <include domain=\"file\" path=\"dd\"/>\n"
+                                + "     <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+                                + "     <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+                                + "</full-backup-content>")));
+    }
+
+    public void testHasBackupSpecifiedInTarget23() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals("No warnings.",
+
+                lintProject(
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "    <uses-sdk android:targetSdkVersion=\"23\" />"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:fullBackupContent=\"no\"\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >\n"
+                                + "    </application>\n"
+                                + "\n"
+                                + "</manifest>\n")));
+    }
+
+    public void testMissingBackupInTarget23() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(""
+                + "AndroidManifest.xml:5: Warning: Should explicitly set android:fullBackupContent to true or false to opt-in to or out of full app data back-up and restore, or alternatively to an @xml resource which specifies which files to backup [AllowBackup]\n"
+                + "    <application\n"
+                + "    ^\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "    <uses-sdk android:targetSdkVersion=\"23\" />"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >\n"
+                                + "    </application>\n"
+                                + "\n"
+                                + "</manifest>\n")));
+    }
+
+    public void testMissingBackupWithoutGcmPreTarget23() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals("No warnings.",
+
+                lintProject(
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "    <uses-sdk android:targetSdkVersion=\"21\" />"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >\n"
+                                + "    </application>\n"
+                                + "\n"
+                                + "</manifest>\n")));
+    }
+
+    public void testMissingBackupWithGcmPreTarget23() throws Exception {
+        mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+        assertEquals(""
+                + "AndroidManifest.xml:5: Warning: Should explicitly set android:fullBackupContent to avoid backing up the GCM device specific regId. [AllowBackup]\n"
+                + "    <application\n"
+                + "    ^\n"
+                + "0 errors, 1 warnings\n",
+
+                lintProject(
+                        xml("AndroidManifest.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    package=\"com.example.helloworld\" >\n"
+                                + "    <uses-sdk android:targetSdkVersion=\"21\" />"
+                                + "\n"
+                                + "    <application\n"
+                                + "        android:label=\"@string/app_name\"\n"
+                                + "        android:theme=\"@style/AppTheme\" >"
+                                + "        <receiver\n"
+                                + "            android:name=\".GcmBroadcastReceiver\"\n"
+                                + "            android:permission=\"com.google.android.c2dm.permission.SEND\" >\n"
+                                + "            <intent-filter>\n"
+                                + "                <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n"
+                                + "                <category android:name=\"com.example.gcm\" />\n"
+                                + "            </intent-filter>\n"
+                                + "        </receiver>\n"
+                                + "    </application>\n"
+                                + "\n"
+                                + "</manifest>\n")));
+    }
+
+    // Custom project which locates all manifest files in the project rather than just
+    // being hardcoded to the root level
+
+    @Override
+    protected TestLintClient createClient() {
+        if ("testMipMapWithDensityFiltering".equals(getName())) {
+            // Set up a mock project model for the resource configuration test(s)
+            // where we provide a subset of densities to be included
+            return new TestLintClient() {
+                @NonNull
+                @Override
+                protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                    return new Project(this, dir, referenceDir) {
+                        @Override
+                        public boolean isGradleProject() {
+                            return true;
+                        }
+
+                        @Nullable
+                        @Override
+                        public AndroidProject getGradleProjectModel() {
+                            /*
+                            Simulate variant freeBetaDebug in this setup:
+                                defaultConfig {
+                                    ...
+                                    resConfigs "cs"
+                                }
+                                flavorDimensions  "pricing", "releaseType"
+                                productFlavors {
+                                    beta {
+                                        flavorDimension "releaseType"
+                                        resConfig "en", "de"
+                                        resConfigs "nodpi", "hdpi"
+                                    }
+                                    normal { flavorDimension "releaseType" }
+                                    free { flavorDimension "pricing" }
+                                    paid { flavorDimension "pricing" }
+                                }
+                             */
+                            ProductFlavor flavorFree = mock(ProductFlavor.class);
+                            when(flavorFree.getName()).thenReturn("free");
+                            when(flavorFree.getResourceConfigurations())
+                                    .thenReturn(Collections.<String>emptyList());
+
+                            ProductFlavor flavorNormal = mock(ProductFlavor.class);
+                            when(flavorNormal.getName()).thenReturn("normal");
+                            when(flavorNormal.getResourceConfigurations())
+                                    .thenReturn(Collections.<String>emptyList());
+
+                            ProductFlavor flavorPaid = mock(ProductFlavor.class);
+                            when(flavorPaid.getName()).thenReturn("paid");
+                            when(flavorPaid.getResourceConfigurations())
+                                    .thenReturn(Collections.<String>emptyList());
+
+                            ProductFlavor flavorBeta = mock(ProductFlavor.class);
+                            when(flavorBeta.getName()).thenReturn("beta");
+                            List<String> resConfigs = Arrays.asList("hdpi", "en", "de", "nodpi");
+                            when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
+
+                            ProductFlavor defaultFlavor = mock(ProductFlavor.class);
+                            when(defaultFlavor.getName()).thenReturn("main");
+                            when(defaultFlavor.getResourceConfigurations()).thenReturn(
+                                    Collections.singleton("cs"));
+
+                            ProductFlavorContainer containerBeta =
+                                    mock(ProductFlavorContainer.class);
+                            when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
+
+                            ProductFlavorContainer containerFree =
+                                    mock(ProductFlavorContainer.class);
+                            when(containerFree.getProductFlavor()).thenReturn(flavorFree);
+
+                            ProductFlavorContainer containerPaid =
+                                    mock(ProductFlavorContainer.class);
+                            when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
+
+                            ProductFlavorContainer containerNormal =
+                                    mock(ProductFlavorContainer.class);
+                            when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
+
+                            ProductFlavorContainer defaultContainer =
+                                    mock(ProductFlavorContainer.class);
+                            when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
+
+                            List<ProductFlavorContainer> containers = Arrays.asList(
+                                    containerPaid, containerFree, containerNormal, containerBeta
+                            );
+
+                            AndroidProject project = mock(AndroidProject.class);
+                            when(project.getProductFlavors()).thenReturn(containers);
+                            when(project.getDefaultConfig()).thenReturn(defaultContainer);
+                            return project;
+                        }
+
+                        @Nullable
+                        @Override
+                        public Variant getCurrentVariant() {
+                            List<String> productFlavorNames = Arrays.asList("free", "beta");
+                            Variant mock = mock(Variant.class);
+                            when(mock.getProductFlavors()).thenReturn(productFlavorNames);
+                            return mock;
+                        }
+                    };
+                }
+            };
+        }
+        if (mEnabled.contains(ManifestDetector.MOCK_LOCATION)) {
+            return new TestLintClient() {
+                @NonNull
+                @Override
+                protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                    return new Project(this, dir, referenceDir) {
+                        @NonNull
+                        @Override
+                        public List<File> getManifestFiles() {
+                            if (mManifestFiles == null) {
+                                mManifestFiles = Lists.newArrayList();
+                                addManifestFiles(mDir);
+                            }
+
+                            return mManifestFiles;
+                        }
+
+                        private void addManifestFiles(File dir) {
+                            if (dir.getName().equals(ANDROID_MANIFEST_XML)) {
+                                mManifestFiles.add(dir);
+                            } else if (dir.isDirectory()) {
+                                File[] files = dir.listFiles();
+                                if (files != null) {
+                                    for (File file : files) {
+                                        addManifestFiles(file);
+                                    }
+                                }
+                            }
+                        }
+
+                        @NonNull SourceProvider createSourceProvider(File manifest) {
+                            SourceProvider provider = mock(SourceProvider.class);
+                            when(provider.getManifestFile()).thenReturn(manifest);
+                            return provider;
+                        }
+
+                        @Nullable
+                        @Override
+                        public AndroidProject getGradleProjectModel() {
+                            if (!isGradleProject()) {
+                                return null;
+                            }
+
+                            File main = new File(mDir, ANDROID_MANIFEST_XML);
+                            File debug = new File(mDir, "debug" + File.separator + ANDROID_MANIFEST_XML);
+                            File test = new File(mDir, "test" + File.separator + ANDROID_MANIFEST_XML);
+
+                            SourceProvider defaultSourceProvider = createSourceProvider(main);
+                            SourceProvider debugSourceProvider = createSourceProvider(debug);
+                            SourceProvider testSourceProvider = createSourceProvider(test);
+
+                            ProductFlavorContainer defaultConfig = mock(ProductFlavorContainer.class);
+                            when(defaultConfig.getSourceProvider()).thenReturn(defaultSourceProvider);
+
+                            BuildType buildType = mock(BuildType.class);
+                            when(buildType.isDebuggable()).thenReturn(true);
+
+                            BuildTypeContainer buildTypeContainer = mock(BuildTypeContainer.class);
+                            when(buildTypeContainer.getBuildType()).thenReturn(buildType);
+                            when(buildTypeContainer.getSourceProvider()).thenReturn(debugSourceProvider);
+                            List<BuildTypeContainer> buildTypes = Lists.newArrayList(buildTypeContainer);
+
+                            SourceProviderContainer extraProvider = mock(SourceProviderContainer.class);
+                            when(extraProvider.getArtifactName()).thenReturn(AndroidProject.ARTIFACT_ANDROID_TEST);
+                            when(extraProvider.getSourceProvider()).thenReturn(testSourceProvider);
+                            List<SourceProviderContainer> extraProviders = Lists.newArrayList(extraProvider);
+
+                            ProductFlavorContainer productFlavorContainer = mock(ProductFlavorContainer.class);
+                            when(productFlavorContainer.getExtraSourceProviders()).thenReturn(extraProviders);
+                            List<ProductFlavorContainer> productFlavors = Lists.newArrayList(productFlavorContainer);
+
+                            AndroidProject project = mock(AndroidProject.class);
+                            when(project.getDefaultConfig()).thenReturn(defaultConfig);
+                            when(project.getBuildTypes()).thenReturn(buildTypes);
+                            when(project.getProductFlavors()).thenReturn(productFlavors);
+                            return project;
+                        }
+                    };
+                }
+            };
+        } else if (mEnabled.contains(ManifestDetector.GRADLE_OVERRIDES)) {
+            return new TestLintClient() {
+                @NonNull
+                @Override
+                protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                    return new Project(this, dir, referenceDir) {
+                        @Override
+                        public boolean isGradleProject() {
+                            return true;
+                        }
+
+                        @Nullable
+                        @Override
+                        public Variant getCurrentVariant() {
+                            ProductFlavor flavor = mock(ProductFlavor.class);
+                            if (getName().equals("ManifestDetectorTest_testGradleOverridesOk") ||
+                                    getName().equals(
+                                        "ManifestDetectorTest_testManifestPackagePlaceholder")) {
+                                when(flavor.getMinSdkVersion()).thenReturn(null);
+                                when(flavor.getTargetSdkVersion()).thenReturn(null);
+                                when(flavor.getVersionCode()).thenReturn(null);
+                                when(flavor.getVersionName()).thenReturn(null);
+                            } else {
+                                assertEquals(getName(), "ManifestDetectorTest_testGradleOverrides");
+
+                                ApiVersion apiMock = mock(ApiVersion.class);
+                                when(apiMock.getApiLevel()).thenReturn(5);
+                                when(apiMock.getApiString()).thenReturn("5");
+                                when(flavor.getMinSdkVersion()).thenReturn(apiMock);
+
+                                apiMock = mock(ApiVersion.class);
+                                when(apiMock.getApiLevel()).thenReturn(16);
+                                when(apiMock.getApiString()).thenReturn("16");
+                                when(flavor.getTargetSdkVersion()).thenReturn(apiMock);
+
+                                when(flavor.getVersionCode()).thenReturn(2);
+                                when(flavor.getVersionName()).thenReturn("MyName");
+                            }
+
+                            Variant mock = mock(Variant.class);
+                            when(mock.getMergedFlavor()).thenReturn(flavor);
+                            return mock;
+                        }
+                    };
+                }
+            };
+
+        }
+        return super.createClient();
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
new file mode 100644
index 0000000..6759dbc
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class NamespaceDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new NamespaceDetector();
+    }
+
+    public void testCustom() throws Exception {
+        assertEquals(
+            "res/layout/customview.xml:5: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
+            "    xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n" +
+            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintProject(
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                    "multiproject/library.properties=>project.properties",
+                    "res/layout/customview.xml"
+            ));
+    }
+
+    public void testGradle() throws Exception {
+        assertEquals(""
+                + "res/layout/customview.xml:5: Error: In Gradle projects, always use http://schemas.android.com/apk/res-auto for custom attributes [ResAuto]\n"
+                + "    xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n"
+                + "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+
+                lintProject(
+                        "multiproject/library.properties=>build.gradle", // dummy; only name counts
+                        "res/layout/customview.xml"
+                ));
+    }
+
+    public void testCustomOk() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+
+                    // Use a standard project properties instead: no warning since it's
+                    // not a library project:
+                    //"multiproject/library.properties=>project.properties",
+
+                    "res/layout/customview.xml"
+            ));
+    }
+
+    public void testCustomOk2() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                    "multiproject/library.properties=>project.properties",
+                    // This project already uses the res-auto package
+                    "res/layout/customview2.xml"
+            ));
+    }
+
+    public void testTypo() throws Exception {
+        assertEquals(
+            "res/layout/wrong_namespace.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/andriod, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
+            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/andriod\"\n" +
+            "                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintProject("res/layout/wrong_namespace.xml"));
+    }
+
+    public void testTypo2() throws Exception {
+        assertEquals(
+            "res/layout/wrong_namespace2.xml:2: Error: URI is case sensitive: was \"http://schemas.android.com/apk/res/Android\", expected \"http://schemas.android.com/apk/res/android\" [NamespaceTypo]\n" +
+            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/Android\"\n" +
+            "                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintProject("res/layout/wrong_namespace2.xml"));
+    }
+
+    public void testTypo3() throws Exception {
+        assertEquals(
+            "res/layout/wrong_namespace3.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/androi, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
+            "<LinearLayout xmlns:a=\"http://schemas.android.com/apk/res/androi\"\n" +
+            "                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintProject("res/layout/wrong_namespace3.xml"));
+    }
+
+    public void testTypo4() throws Exception {
+        assertEquals(
+            "res/layout/wrong_namespace5.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n" +
+            "    xmlns:noturi=\"tp://schems.android.com/apk/res/com.my.package\"\n" +
+            "                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "res/layout/wrong_namespace5.xml:3: Error: Possible typo in URL: was \"http://schems.android.com/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
+            "    xmlns:typo1=\"http://schems.android.com/apk/res/com.my.package\"\n" +
+            "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "res/layout/wrong_namespace5.xml:4: Error: Possible typo in URL: was \"http://schems.android.comm/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
+            "    xmlns:typo2=\"http://schems.android.comm/apk/res/com.my.package\"\n" +
+            "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "3 errors, 0 warnings\n",
+
+            lintProject("res/layout/wrong_namespace5.xml"));
+    }
+
+    public void testMisleadingPrefix() throws Exception {
+        assertEquals(""
+                + "res/layout/layout.xml:3: Error: Suspicious namespace and prefix combination [NamespaceTypo]\n"
+                + "    xmlns:app=\"http://schemas.android.com/tools\"\n"
+                + "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "res/layout/layout.xml:4: Error: Suspicious namespace and prefix combination [NamespaceTypo]\n"
+                + "    xmlns:tools=\"http://schemas.android.com/apk/res/android\"\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "2 errors, 0 warnings\n",
+
+                lintProject(
+                        xml("res/layout/layout.xml", ""
+                                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                                + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    xmlns:app=\"http://schemas.android.com/tools\"\n"
+                                + "    xmlns:tools=\"http://schemas.android.com/apk/res/android\"\n"
+                                + "    android:layout_width=\"match_parent\"\n"
+                                + "    android:layout_height=\"match_parent\"\n"
+                                + "    android:orientation=\"vertical\"\n"
+                                + "    app:foo=\"true\"\n"
+                                + "    tools:bar=\"true\" />\n")));
+    }
+
+    public void testTypoOk() throws Exception {
+        assertEquals(
+                "No warnings.",
+
+                lintProject("res/layout/wrong_namespace4.xml"));
+    }
+
+    public void testUnused() throws Exception {
+        assertEquals(
+            "res/layout/unused_namespace.xml:3: Warning: Unused namespace unused1 [UnusedNamespace]\n" +
+            "    xmlns:unused1=\"http://schemas.android.com/apk/res/unused1\"\n" +
+            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "res/layout/unused_namespace.xml:4: Warning: Unused namespace unused2 [UnusedNamespace]\n" +
+            "    xmlns:unused2=\"http://schemas.android.com/apk/res/unused1\"\n" +
+            "    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "0 errors, 2 warnings\n" +
+            "",
+
+            lintProject("res/layout/unused_namespace.xml"));
+    }
+
+    public void testUnusedOk() throws Exception {
+        assertEquals(
+                "No warnings.",
+
+                lintProject("res/layout/layout1.xml"));
+    }
+
+    public void testLayoutAttributesOk() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintFiles("res/layout/namespace3.xml"));
+    }
+
+    public void testLayoutAttributesOk2() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintFiles("res/layout/namespace4.xml"));
+    }
+
+    public void testLayoutAttributes() throws Exception {
+        assertEquals(
+            "res/layout/namespace3.xml:2: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
+            "    xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
+            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintFiles("res/layout/namespace3.xml",
+                      "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                      "multiproject/library.properties=>project.properties"));
+    }
+
+    public void testLayoutAttributes2() throws Exception {
+        assertEquals(
+            "res/layout/namespace4.xml:3: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
+            "    xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
+            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintFiles("res/layout/namespace4.xml",
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                    "multiproject/library.properties=>project.properties"));
+    }
+
+    public void testWrongResAutoUrl() throws Exception {
+        assertEquals(
+            "res/layout/namespace5.xml:3: Error: Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto? [ResAuto]\n" +
+            "    xmlns:app=\"http://schemas.android.com/apk/auto-res/\"\n" +
+            "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+            "1 errors, 0 warnings\n",
+
+            lintFiles("res/layout/namespace5.xml",
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                    "multiproject/library.properties=>project.properties"));
+    }
+
+    public void testWrongResUrl() throws Exception {
+        assertEquals(""
+            + "AndroidManifest.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n"
+            + "<manifest xmlns:android=\"https://schemas.android.com/apk/res/android\"\n"
+            + "                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+            + "1 errors, 0 warnings\n",
+
+            lintFiles("https_namespace.xml=>AndroidManifest.xml"));
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionHolderTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionHolderTest.java
new file mode 100644
index 0000000..6151455
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionHolderTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
+import com.google.common.collect.Sets;
+
+import junit.framework.TestCase;
+
+import java.util.Set;
+
+public class PermissionHolderTest extends TestCase {
+    public void test() {
+        assertTrue(new SetPermissionLookup(Sets.newHashSet("foo")).hasPermission("foo"));
+        assertTrue(new SetPermissionLookup(Sets.newHashSet("foo","bar")).hasPermission("foo"));
+        assertTrue(new SetPermissionLookup(Sets.newHashSet("foo","bar")).hasPermission("bar"));
+        assertFalse(new SetPermissionLookup(Sets.newHashSet("foo")).hasPermission("bar"));
+        assertFalse(new SetPermissionLookup(Sets.newHashSet("foo")).hasPermission(""));
+        assertFalse(new SetPermissionLookup(Sets.<String>newHashSet()).hasPermission(""));
+
+        Set<String> empty = Sets.newHashSet();
+        assertFalse(new SetPermissionLookup(Sets.newHashSet("foo"), empty).isRevocable("foo"));
+        assertTrue(new SetPermissionLookup(empty, Sets.newHashSet("foo")).isRevocable("foo"));
+        assertFalse(new SetPermissionLookup(empty, Sets.newHashSet("foo")).isRevocable("bar"));
+
+        SetPermissionLookup lookup1 = new SetPermissionLookup(Sets.newHashSet("foo", "bar"));
+        assertTrue(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("foo"));
+        assertTrue(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("bar"));
+        assertTrue(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("baz"));
+        assertFalse(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("a"));
+
+        AndroidVersion version = new AndroidVersion(5, null);
+        assertSame(version, new SetPermissionLookup(Sets.newHashSet("foo"), Sets.newHashSet("bar"),
+                version, AndroidVersion.DEFAULT).getMinSdkVersion());
+        assertSame(version, new SetPermissionLookup(Sets.newHashSet("foo"), Sets.newHashSet("bar"),
+                AndroidVersion.DEFAULT, version).getTargetSdkVersion());
+    }
+}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
new file mode 100644
index 0000000..7992903
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.TAG_PERMISSION;
+import static com.android.tools.lint.checks.PermissionRequirement.REVOCABLE_PERMISSION_NAMES;
+import static com.android.tools.lint.checks.PermissionRequirement.isRevocableSystemPermission;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.sdklib.AndroidVersion;
+import com.android.testutils.SdkTestCase;
+import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.utils.XmlUtils;
+import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import lombok.ast.BinaryOperator;
+
+public class PermissionRequirementTest extends TestCase {
+    private static ResolvedAnnotation createAnnotation(
+            @NonNull String name,
+            @NonNull ResolvedAnnotation.Value... values) {
+        ResolvedAnnotation annotation = mock(ResolvedAnnotation.class);
+        when(annotation.getName()).thenReturn(name);
+        when(annotation.getValues()).thenReturn(Arrays.asList(values));
+        for (ResolvedAnnotation.Value value : values) {
+            when(annotation.getValue(value.name)).thenReturn(value.value);
+        }
+        return annotation;
+    }
+
+    public void testSingle() {
+        ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("value",
+                "android.permission.ACCESS_FINE_LOCATION");
+        Set<String> emptySet = Collections.emptySet();
+        Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
+        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PermissionRequirement req = PermissionRequirement.create(null, annotation);
+        assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
+
+        assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(Collections.singleton(""))));
+        assertTrue(req.isSatisfied(new SetPermissionLookup(fineSet)));
+        assertEquals("android.permission.ACCESS_FINE_LOCATION",
+                req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
+        assertEquals(fineSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
+        assertEquals(emptySet, req.getMissingPermissions(new SetPermissionLookup(fineSet)));
+        assertEquals(fineSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
+        assertNull(req.getOperator());
+        assertFalse(req.getChildren().iterator().hasNext());
+    }
+
+    public void testAny() {
+        ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("anyOf",
+                new String[]{"android.permission.ACCESS_FINE_LOCATION",
+                        "android.permission.ACCESS_COARSE_LOCATION"});
+        Set<String> emptySet = Collections.emptySet();
+        Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
+        Set<String> coarseSet = Collections.singleton("android.permission.ACCESS_COARSE_LOCATION");
+        Set<String> bothSet = Sets.newHashSet(
+                "android.permission.ACCESS_FINE_LOCATION",
+                "android.permission.ACCESS_COARSE_LOCATION");
+
+        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PermissionRequirement req = PermissionRequirement.create(null, annotation);
+        assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(Collections.singleton(""))));
+        assertTrue(req.isSatisfied(new SetPermissionLookup(fineSet)));
+        assertTrue(req.isSatisfied(new SetPermissionLookup(coarseSet)));
+        assertEquals(
+                "android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION",
+                req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
+        assertEquals(bothSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
+        assertEquals(bothSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
+        assertSame(BinaryOperator.LOGICAL_OR, req.getOperator());
+    }
+
+    public void testAll() {
+        ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("allOf",
+                new String[]{"android.permission.ACCESS_FINE_LOCATION",
+                        "android.permission.ACCESS_COARSE_LOCATION"});
+        Set<String> emptySet = Collections.emptySet();
+        Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
+        Set<String> coarseSet = Collections.singleton("android.permission.ACCESS_COARSE_LOCATION");
+        Set<String> bothSet = Sets.newHashSet(
+                "android.permission.ACCESS_FINE_LOCATION",
+                "android.permission.ACCESS_COARSE_LOCATION");
+
+        ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+        PermissionRequirement req = PermissionRequirement.create(null, annotation);
+        assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(Collections.singleton(""))));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(fineSet)));
+        assertFalse(req.isSatisfied(new SetPermissionLookup(coarseSet)));
+        assertTrue(req.isSatisfied(new SetPermissionLookup(bothSet)));
+        assertEquals(
+                "android.permission.ACCESS_FINE_LOCATION and android.permission.ACCESS_COARSE_LOCATION",
+                req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
+        assertEquals(bothSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
+        assertEquals(
+                "android.permission.ACCESS_COARSE_LOCATION",
+                req.describeMissingPermissions(new SetPermissionLookup(fineSet)));
+        assertEquals(coarseSet, req.getMissingPermissions(new SetPermissionLookup(fineSet)));
+        assertEquals(
+                "android.permission.ACCESS_FINE_LOCATION",
+                req.describeMissingPermissions(new SetPermissionLookup(coarseSet)));
+        assertEquals(fineSet, req.getMissingPermissions(new SetPermissionLookup(coarseSet)));
+        assertEquals(bothSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
+        assertSame(BinaryOperator.LOGICAL_AND, req.getOperator());
+    }
+
+    public void testRevocable() {
+        assertTrue(isRevocableSystemPermission("android.permission.ACCESS_FINE_LOCATION"));
+        assertTrue(isRevocableSystemPermission("android.permission.ACCESS_COARSE_LOCATION"));
+        assertFalse(isRevocableSystemPermission("android.permission.UNKNOWN_PERMISSION_NAME"));
+    }
+
+    public void testRevocable2() {
+        assertTrue(new SetPermissionLookup(Collections.<String>emptySet(),
+            Sets.newHashSet("my.permission1", "my.permission2")).isRevocable("my.permission2"));
+    }
+
+    public void testAppliesTo() {
+        ResolvedAnnotation annotation;
+        PermissionRequirement req;
+
+        // No date range applies to permission
+        annotation = createAnnotation(PERMISSION_ANNOTATION,
+                new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"));
+        req = PermissionRequirement.create(null, annotation);
+        assertTrue(req.appliesTo(getHolder(15, 1)));
+        assertTrue(req.appliesTo(getHolder(15, 19)));
+        assertTrue(req.appliesTo(getHolder(15, 23)));
+        assertTrue(req.appliesTo(getHolder(22, 23)));
+        assertTrue(req.appliesTo(getHolder(23, 23)));
+
+        // Permission discontinued in API 23:
+        annotation = createAnnotation(PERMISSION_ANNOTATION,
+                new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"),
+                new ResolvedAnnotation.Value("apis", "..22"));
+        req = PermissionRequirement.create(null, annotation);
+        assertTrue(req.appliesTo(getHolder(15, 1)));
+        assertTrue(req.appliesTo(getHolder(15, 19)));
+        assertTrue(req.appliesTo(getHolder(15, 23)));
+        assertTrue(req.appliesTo(getHolder(22, 23)));
+        assertFalse(req.appliesTo(getHolder(23, 23)));
+
+        // Permission requirement started in API 23
+        annotation = createAnnotation(PERMISSION_ANNOTATION,
+                new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"),
+                new ResolvedAnnotation.Value("apis", "23.."));
+        req = PermissionRequirement.create(null, annotation);
+        assertFalse(req.appliesTo(getHolder(15, 1)));
+        assertFalse(req.appliesTo(getHolder(1, 19)));
+        assertFalse(req.appliesTo(getHolder(15, 22)));
+        assertTrue(req.appliesTo(getHolder(22, 23)));
+        assertTrue(req.appliesTo(getHolder(23, 30)));
+
+        // Permission requirement applied from API 14 through API 18
+        annotation = createAnnotation(PERMISSION_ANNOTATION,
+                new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"),
+                new ResolvedAnnotation.Value("apis", "14..18"));
+        req = PermissionRequirement.create(null, annotation);
+        assertFalse(req.appliesTo(getHolder(1, 5)));
+        assertTrue(req.appliesTo(getHolder(15, 19)));
+    }
+
+    private static PermissionHolder getHolder(int min, int target) {
+        return new PermissionHolder.SetPermissionLookup(Collections.<String>emptySet(),
+                Collections.<String>emptySet(), new AndroidVersion(min, null),
+                new AndroidVersion(target, null));
+    }
+
+    public static void testDbUpToDate() throws Exception {
+        List<String> expected = getDangerousPermissions();
+        if (expected == null) {
+            return;
+        }
+        List<String> actual = Arrays.asList(REVOCABLE_PERMISSION_NAMES);
+        if (!expected.equals(actual)) {
+            System.out.println("Correct list of exceptions:");
+            for (String name : expected) {
+                System.out.println("            \"" + name + "\",");
+            }
+            fail("List of revocable permissions has changed:\n" +
+                // Make the diff show what it take to bring the actual results into the
+                // expected results
+                SdkTestCase.getDiff(Joiner.on('\n').join(actual),
+                    Joiner.on('\n').join(expected)));
+        }
+    }
+
+    @Nullable
+    private static List<String> getDangerousPermissions() throws IOException {
+        Pattern pattern = Pattern.compile("dangerous");
+        String top = System.getenv("ANDROID_BUILD_TOP");   //$NON-NLS-1$
+        if (top == null) {
+            top = "/Volumes/android/mnc-dev";
+        }
+
+        // TODO: We should ship this file with the SDK!
+        File file = new File(top, "frameworks/base/core/res/AndroidManifest.xml");
+        if (!file.exists()) {
+            System.out.println("Set $ANDROID_BUILD_TOP to point to the git repository to check permissions");
+            return null;
+        }
+        boolean passedRuntimeHeader = false;
+        boolean passedInstallHeader = false;
+        String xml = Files.toString(file, Charsets.UTF_8);
+        Document document = XmlUtils.parseDocumentSilently(xml, true);
+        Set<String> revocable = Sets.newHashSet();
+        if (document != null && document.getDocumentElement() != null) {
+            NodeList children = document.getDocumentElement().getChildNodes();
+            for (int i = 0, n = children.getLength(); i < n; i++) {
+                Node child = children.item(i);
+                short nodeType = child.getNodeType();
+                if (nodeType == Node.COMMENT_NODE) {
+                    String comment = child.getNodeValue();
+                    if (comment.contains("RUNTIME PERMISSIONS")) {
+                        passedRuntimeHeader = true;
+                    } else if (comment.contains("INSTALLTIME PERMISSIONS"))
+                        passedInstallHeader = true;
+                } else if (passedRuntimeHeader
+                        && !passedInstallHeader
+                        && nodeType == Node.ELEMENT_NODE
+                        && child.getNodeName().equals(TAG_PERMISSION)) {
+                    Element element = (Element) child;
+                    String protectionLevel = element.getAttributeNS(ANDROID_URI, "protectionLevel");
+                    String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+                    if (!name.isEmpty() && pattern.matcher(protectionLevel).find()) {
+                        revocable.add(name);
+                    }
+                }
+            }
+        }
+
+        List<String> expected = Lists.newArrayList(revocable);
+        Collections.sort(expected);
+        return expected;
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
new file mode 100644
index 0000000..4df2fd2
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.FN_PUBLIC_TXT;
+import static com.android.SdkConstants.FN_RESOURCE_TEXT;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+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.Dependencies;
+import com.android.builder.model.Variant;
+import com.android.testutils.TestUtils;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.mockito.stubbing.OngoingStubbing;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+@SuppressWarnings("javadoc")
+public class PrivateResourceDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new PrivateResourceDetector();
+    }
+
+    public void testPrivateInXml() throws Exception {
+        assertEquals(""
+                + "res/layout/private.xml:11: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
+                + "            android:text=\"@string/my_private_string\" />\n"
+                + "                          ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+                lintProject(xml("res/layout/private.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                        + "              android:id=\"@+id/newlinear\"\n"
+                        + "              android:orientation=\"vertical\"\n"
+                        + "              android:layout_width=\"match_parent\"\n"
+                        + "              android:layout_height=\"match_parent\">\n"
+                        + "\n"
+                        + "    <TextView\n"
+                        + "            android:layout_width=\"wrap_content\"\n"
+                        + "            android:layout_height=\"wrap_content\"\n"
+                        + "            android:text=\"@string/my_private_string\" />\n"
+                        + "\n"
+                        + "    <TextView\n"
+                        + "            android:layout_width=\"wrap_content\"\n"
+                        + "            android:layout_height=\"wrap_content\"\n"
+                        + "            android:text=\"@string/my_public_string\" />\n"
+                        + "</LinearLayout>\n")));
+    }
+
+    public void testPrivateInJava() throws Exception {
+        assertEquals(""
+                + ""
+                + "src/test/pkg/Private.java:3: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
+                + "        int x = R.string.my_private_string; // ERROR\n"
+                + "                ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 1 warnings\n",
+                lintProject(java("src/test/pkg/Private.java", ""
+                        + "public class PrivateResourceDetectorTest {\n"
+                        + "    void test() {\n"
+                        + "        int x = R.string.my_private_string; // ERROR\n"
+                        + "        int y = R.string.my_public_string; // OK\n"
+                        + "        int y = android.R.string.my_private_string; // OK (not in project namespace)\n"
+                        + "    }\n"
+                        + "}\n")));
+    }
+
+    public void testOverride() throws Exception {
+        assertEquals(""
+                + "res/layout/my_private_layout.xml: Warning: Overriding @layout/my_private_layout which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+                + "res/values/strings.xml:5: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+                + "    <string name=\"my_private_string\">String 1</string>\n"
+                + "                  ~~~~~~~~~~~~~~~~~\n"
+                + "res/values/strings.xml:9: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+                + "    <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
+                + "                              ~~~~~~~~~~~~~~~~~\n"
+                + "res/values/strings.xml:12: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+                + "    <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
+                + "                                         ~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 4 warnings\n",
+                lintProject(xml("res/values/strings.xml", ""
+                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                        + "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n"
+                        + "\n"
+                        + "    <string name=\"app_name\">LibraryProject</string>\n"
+                        + "    <string name=\"my_private_string\">String 1</string>\n"
+                        + "    <string name=\"my_public_string\">String 2</string>\n"
+                        + "    <string name=\"string3\"> @my_private_string </string>\n"
+                        + "    <string name=\"string4\"> @my_public_string </string>\n"
+                        + "    <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
+                        + "    <dimen name=\"my_private_string\">String 1</dimen>\n" // unrelated
+                        + "    <string tools:ignore=\"PrivateResource\" name=\"my_private_string\">String 2</string>\n"
+                        + "    <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
+                        + "    <string tools:override=\"true\" name=\"my_private_string\">String 2</string>\n"
+                        + "\n"
+                        + "</resources>\n"),
+                        xml("res/layout/my_private_layout.xml", "<LinearLayout/>"),
+                        xml("res/layout/my_public_layout.xml", "<LinearLayout/>")));
+    }
+
+    @Override
+    protected TestLintClient createClient() {
+        return new TestLintClient() {
+            @NonNull
+            @Override
+            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+                return new Project(this, dir, referenceDir) {
+                    @Override
+                    public boolean isGradleProject() {
+                        return true;
+                    }
+
+                    @Nullable
+                    @Override
+                    public AndroidProject getGradleProjectModel() {
+                        // First version which supported private resources; this does not
+                        // need to track later versions we release
+                        return createMockProject("1.3.0-alpha2", 3);
+                    }
+
+                    @Nullable
+                    @Override
+                    public Variant getCurrentVariant() {
+                        try {
+                            AndroidLibrary library = createMockLibrary(
+                                    ""
+                                            + "int string my_private_string 0x7f040000\n"
+                                            + "int string my_public_string 0x7f040001\n"
+                                            + "int layout my_private_layout 0x7f040002\n",
+                                    ""
+                                            + ""
+                                            + "string my_public_string\n",
+                                    Collections.<AndroidLibrary>emptyList()
+                            );
+                            AndroidArtifact artifact = createMockArtifact(
+                                    Collections.singletonList(library));
+
+                            Variant variant = mock(Variant.class);
+                            when(variant.getMainArtifact()).thenReturn(artifact);
+                            return variant;
+                        } catch (Exception e) {
+                            fail(e.toString());
+                            return null;
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    public static AndroidProject createMockProject(String modelVersion, int apiVersion) {
+        AndroidProject project = mock(AndroidProject.class);
+        when(project.getApiVersion()).thenReturn(apiVersion);
+        when(project.getModelVersion()).thenReturn(modelVersion);
+
+        return project;
+    }
+
+    public static AndroidArtifact createMockArtifact(List<AndroidLibrary> libraries) {
+        Dependencies dependencies = mock(Dependencies.class);
+        when(dependencies.getLibraries()).thenReturn(libraries);
+
+        AndroidArtifact artifact = mock(AndroidArtifact.class);
+        when(artifact.getDependencies()).thenReturn(dependencies);
+
+        return artifact;
+    }
+
+    public static AndroidLibrary createMockLibrary(String allResources, String publicResources,
+            List<AndroidLibrary> dependencies)
+            throws IOException {
+        final File tempDir = TestUtils.createTempDirDeletedOnExit();
+
+        Files.write(allResources, new File(tempDir, FN_RESOURCE_TEXT), Charsets.UTF_8);
+        File publicTxtFile = new File(tempDir, FN_PUBLIC_TXT);
+        if (publicResources != null) {
+            Files.write(publicResources, publicTxtFile, Charsets.UTF_8);
+        }
+        AndroidLibrary library = mock(AndroidLibrary.class);
+        when(library.getPublicResources()).thenReturn(publicTxtFile);
+
+        // Work around wildcard capture
+        //when(mock.getLibraryDependencies()).thenReturn(dependencies);
+        List libraryDependencies = library.getLibraryDependencies();
+        OngoingStubbing<List> setter = when(libraryDependencies);
+        setter.thenReturn(dependencies);
+        return library;
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
new file mode 100644
index 0000000..da36270
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+import java.io.File;
+
+@SuppressWarnings("javadoc")
+public class RequiredAttributeDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new RequiredAttributeDetector();
+    }
+
+    public void test() throws Exception {
+        // Simple: Only consider missing attributes in the layout xml file
+        // (though skip warnings on <merge> tags and under <GridLayout>
+        assertEquals(
+            "res/layout/size.xml:13: Error: The required layout_height attribute is missing [RequiredSize]\n" +
+            "    <RadioButton\n" +
+            "    ^\n" +
+            "res/layout/size.xml:18: Error: The required layout_width attribute is missing [RequiredSize]\n" +
+            "    <EditText\n" +
+            "    ^\n" +
+            "res/layout/size.xml:23: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
+            "    <EditText\n" +
+            "    ^\n" +
+            "3 errors, 0 warnings\n",
+
+            lintProject("res/layout/size.xml"));
+    }
+
+    public void test2() throws Exception {
+        // Consider styles (specifying sizes) and includes (providing sizes for the root tags)
+        assertEquals(
+            "res/layout/size2.xml:9: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
+            "    <Button\n" +
+            "    ^\n" +
+            "res/layout/size2.xml:18: Error: The required layout_height attribute is missing [RequiredSize]\n" +
+            "    <Button\n" +
+            "    ^\n" +
+            "2 errors, 0 warnings\n",
+
+            lintProject(
+                    "res/layout/size2.xml",
+                    "res/layout/sizeincluded.xml",
+                    "res/values/sizestyles.xml"
+                    ));
+    }
+
+    public void testInflaters() throws Exception {
+        // Consider java inflation
+        assertEquals(
+            "res/layout/size5.xml:2: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
+            "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+            "^\n" +
+            "1 errors, 0 warnings\n",
+
+            lintProject(
+                    "src/test/pkg/InflaterTest.java.txt=>src/test/pkg/InflaterTest.java",
+                    "res/layout/sizeincluded.xml=>res/layout/size1.xml",
+                    "res/layout/sizeincluded.xml=>res/layout/size2.xml",
+                    "res/layout/sizeincluded.xml=>res/layout/size3.xml",
+                    "res/layout/sizeincluded.xml=>res/layout/size4.xml",
+                    "res/layout/sizeincluded.xml=>res/layout/size5.xml",
+                    "res/layout/sizeincluded.xml=>res/layout/size6.xml",
+                    "res/layout/sizeincluded.xml=>res/layout/size7.xml"
+            ));
+    }
+
+    public void testRequestFocus() throws Exception {
+        // See http://code.google.com/p/android/issues/detail?id=38700
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "res/layout/edit_type.xml"
+            ));
+    }
+
+    public void testFrameworkStyles() throws Exception {
+        // See http://code.google.com/p/android/issues/detail?id=38958
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "res/layout/listseparator.xml"
+            ));
+    }
+
+    public void testThemeStyles() throws Exception {
+        // Check that we don't complain about cases where the size is defined in a theme
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "res/layout/size.xml",
+                    "res/values/themes.xml"
+            ));
+    }
+
+    public void testThemeStyles2() throws Exception {
+        // Check that we don't complain about cases where the size is defined in a theme
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "res/layout/size.xml",
+                    "res/values/themes2.xml"
+            ));
+    }
+
+    public void testHasLayoutVariations() throws Exception {
+        File projectDir = getProjectDir(null,
+                copy("res/layout/size.xml"),
+                copy("res/layout/size.xml", "res/layout-land/size.xml"),
+                copy("res/layout/size.xml", "res/layout/size2.xml"));
+        assertTrue(RequiredAttributeDetector.hasLayoutVariations(
+                new File(projectDir, "res/layout/size.xml".replace('/', File.separatorChar))));
+        assertTrue(RequiredAttributeDetector.hasLayoutVariations(
+                new File(projectDir, "res/layout-land/size.xml".replace('/', File.separatorChar))));
+        assertFalse(RequiredAttributeDetector.hasLayoutVariations(
+                new File(projectDir, "res/layout/size2.xml".replace('/', File.separatorChar))));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
new file mode 100644
index 0000000..518566a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
@@ -0,0 +1,1263 @@
+/*
+ * Copyright (C) 2015 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.tools.lint.checks;
+
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
+
+import com.android.tools.lint.ExternalAnnotationRepository;
+import com.android.tools.lint.ExternalAnnotationRepositoryTest;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("ClassNameDiffersFromFileName") // For embedded unit tests
+public class SupportAnnotationDetectorTest extends AbstractCheckTest {
+    private static final boolean SDK_ANNOTATIONS_AVAILABLE =
+            new SupportAnnotationDetectorTest().createClient().findResource(
+            ExternalAnnotationRepository.SDK_ANNOTATIONS_PATH) != null;
+
+    @Override
+    protected Detector getDetector() {
+        return new SupportAnnotationDetector();
+    }
+
+    public void testRange() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/RangeTest.java:32: Error: Expected length 5 (was 4) [Range]\n"
+                + "        printExact(\"1234\"); // ERROR\n"
+                + "                   ~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:34: Error: Expected length 5 (was 6) [Range]\n"
+                + "        printExact(\"123456\"); // ERROR\n"
+                + "                   ~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:36: Error: Expected length ≥ 5 (was 4) [Range]\n"
+                + "        printMin(\"1234\"); // ERROR\n"
+                + "                 ~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:43: Error: Expected length ≤ 8 (was 9) [Range]\n"
+                + "        printMax(\"123456789\"); // ERROR\n"
+                + "                 ~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:45: Error: Expected length ≥ 4 (was 3) [Range]\n"
+                + "        printRange(\"123\"); // ERROR\n"
+                + "                   ~~~~~\n"
+                + "src/test/pkg/RangeTest.java:49: Error: Expected length ≤ 6 (was 7) [Range]\n"
+                + "        printRange(\"1234567\"); // ERROR\n"
+                + "                   ~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:53: Error: Expected size 5 (was 4) [Range]\n"
+                + "        printExact(new int[]{1, 2, 3, 4}); // ERROR\n"
+                + "                   ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:55: Error: Expected size 5 (was 6) [Range]\n"
+                + "        printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR\n"
+                + "                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:57: Error: Expected size ≥ 5 (was 4) [Range]\n"
+                + "        printMin(new int[]{1, 2, 3, 4}); // ERROR\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:65: Error: Expected size ≤ 8 (was 9) [Range]\n"
+                + "        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:67: Error: Expected size ≥ 4 (was 3) [Range]\n"
+                + "        printRange(new int[] {1,2,3}); // ERROR\n"
+                + "                   ~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:71: Error: Expected size ≤ 6 (was 7) [Range]\n"
+                + "        printRange(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
+                + "                   ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:74: Error: Expected size to be a multiple of 3 (was 4 and should be either 3 or 6) [Range]\n"
+                + "        printMultiple(new int[] {1,2,3,4}); // ERROR\n"
+                + "                      ~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:75: Error: Expected size to be a multiple of 3 (was 5 and should be either 3 or 6) [Range]\n"
+                + "        printMultiple(new int[] {1,2,3,4,5}); // ERROR\n"
+                + "                      ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:77: Error: Expected size to be a multiple of 3 (was 7 and should be either 6 or 9) [Range]\n"
+                + "        printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
+                + "                      ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:80: Error: Expected size ≥ 4 (was 3) [Range]\n"
+                + "        printMinMultiple(new int[]{1, 2, 3}); // ERROR\n"
+                + "                         ~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:84: Error: Value must be ≥ 4 (was 3) [Range]\n"
+                + "        printAtLeast(3); // ERROR\n"
+                + "                     ~\n"
+                + "src/test/pkg/RangeTest.java:91: Error: Value must be ≤ 7 (was 8) [Range]\n"
+                + "        printAtMost(8); // ERROR\n"
+                + "                    ~\n"
+                + "src/test/pkg/RangeTest.java:93: Error: Value must be ≥ 4 (was 3) [Range]\n"
+                + "        printBetween(3); // ERROR\n"
+                + "                     ~\n"
+                + "src/test/pkg/RangeTest.java:98: Error: Value must be ≤ 7 (was 8) [Range]\n"
+                + "        printBetween(8); // ERROR\n"
+                + "                     ~\n"
+                + "src/test/pkg/RangeTest.java:102: Error: Value must be ≥ 2.5 (was 2.49) [Range]\n"
+                + "        printAtLeastInclusive(2.49f); // ERROR\n"
+                + "                              ~~~~~\n"
+                + "src/test/pkg/RangeTest.java:106: Error: Value must be > 2.5 (was 2.49) [Range]\n"
+                + "        printAtLeastExclusive(2.49f); // ERROR\n"
+                + "                              ~~~~~\n"
+                + "src/test/pkg/RangeTest.java:107: Error: Value must be > 2.5 (was 2.5) [Range]\n"
+                + "        printAtLeastExclusive(2.5f); // ERROR\n"
+                + "                              ~~~~\n"
+                + "src/test/pkg/RangeTest.java:113: Error: Value must be ≤ 7.0 (was 7.1) [Range]\n"
+                + "        printAtMostInclusive(7.1f); // ERROR\n"
+                + "                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:117: Error: Value must be < 7.0 (was 7.0) [Range]\n"
+                + "        printAtMostExclusive(7.0f); // ERROR\n"
+                + "                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:118: Error: Value must be < 7.0 (was 7.1) [Range]\n"
+                + "        printAtMostExclusive(7.1f); // ERROR\n"
+                + "                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:120: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
+                + "        printBetweenFromInclusiveToInclusive(2.4f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:124: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
+                + "        printBetweenFromInclusiveToInclusive(5.1f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:126: Error: Value must be > 2.5 (was 2.4) [Range]\n"
+                + "        printBetweenFromExclusiveToInclusive(2.4f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:127: Error: Value must be > 2.5 (was 2.5) [Range]\n"
+                + "        printBetweenFromExclusiveToInclusive(2.5f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:129: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
+                + "        printBetweenFromExclusiveToInclusive(5.1f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:131: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
+                + "        printBetweenFromInclusiveToExclusive(2.4f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:135: Error: Value must be < 5.0 (was 5.0) [Range]\n"
+                + "        printBetweenFromInclusiveToExclusive(5.0f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:137: Error: Value must be > 2.5 (was 2.4) [Range]\n"
+                + "        printBetweenFromExclusiveToExclusive(2.4f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:138: Error: Value must be > 2.5 (was 2.5) [Range]\n"
+                + "        printBetweenFromExclusiveToExclusive(2.5f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:141: Error: Value must be < 5.0 (was 5.0) [Range]\n"
+                + "        printBetweenFromExclusiveToExclusive(5.0f); // ERROR\n"
+                + "                                             ~~~~\n"
+                + "src/test/pkg/RangeTest.java:145: Error: Value must be ≥ 4 (was -7) [Range]\n"
+                + "        printBetween(-7); // ERROR\n"
+                + "                     ~~\n"
+                + "src/test/pkg/RangeTest.java:146: Error: Value must be > 2.5 (was -10.0) [Range]\n"
+                + "        printAtLeastExclusive(-10.0f); // ERROR\n"
+                + "                              ~~~~~~\n"
+                + "src/test/pkg/RangeTest.java:156: Error: Value must be ≥ -1 (was -2) [Range]\n"
+                + "        printIndirect(-2); // ERROR\n"
+                + "                      ~~\n"
+                + "src/test/pkg/RangeTest.java:157: Error: Value must be ≤ 42 (was 43) [Range]\n"
+                + "        printIndirect(43); // ERROR\n"
+                + "                      ~~\n"
+                + "src/test/pkg/RangeTest.java:158: Error: Expected length 5 (was 7) [Range]\n"
+                + "        printIndirectSize(\"1234567\"); // ERROR\n"
+                + "                          ~~~~~~~~~\n"
+                + "41 errors, 0 warnings\n",
+
+                lintProject("src/test/pkg/RangeTest.java.txt=>src/test/pkg/RangeTest.java",
+                        "src/android/support/annotation/Size.java.txt=>src/android/support/annotation/Size.java",
+                        "src/android/support/annotation/IntRange.java.txt=>src/android/support/annotation/IntRange.java",
+                        "src/android/support/annotation/FloatRange.java.txt=>src/android/support/annotation/FloatRange.java"
+                ));
+    }
+
+    public void testTypeDef() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/IntDefTest.java:31: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setStyle(0, 0); // ERROR\n"
+                + "                 ~\n"
+                + "src/test/pkg/IntDefTest.java:32: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setStyle(-1, 0); // ERROR\n"
+                + "                 ~~\n"
+                + "src/test/pkg/IntDefTest.java:33: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setStyle(UNRELATED, 0); // ERROR\n"
+                + "                 ~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:34: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setStyle(IntDefTest.UNRELATED, 0); // ERROR\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:35: Error: Flag not allowed here [WrongConstant]\n"
+                + "        setStyle(IntDefTest.STYLE_NORMAL|STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
+                + "                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:36: Error: Flag not allowed here [WrongConstant]\n"
+                + "        setStyle(~STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
+                + "                 ~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:55: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", UNRELATED); // ERROR\n"
+                + "                     ~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:56: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", UNRELATED|STYLE_NO_TITLE); // ERROR\n"
+                + "                     ~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:57: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", STYLE_NORMAL|STYLE_NO_TITLE|UNRELATED); // ERROR\n"
+                + "                                                 ~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:58: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", 1); // ERROR\n"
+                + "                     ~\n"
+                + "src/test/pkg/IntDefTest.java:59: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", arg < 0 ? STYLE_NORMAL : UNRELATED); // ERROR\n"
+                + "                                              ~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:60: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", arg < 0 ? UNRELATED : STYLE_NORMAL); // ERROR\n"
+                + "                               ~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:79: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
+                + "        setTitle(\"\", UNRELATED_TYPE); // ERROR\n"
+                + "                     ~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:80: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
+                + "        setTitle(\"\", \"type2\"); // ERROR\n"
+                + "                     ~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:87: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
+                + "        setTitle(\"\", type); // ERROR\n"
+                + "                     ~~~~\n"
+                + "src/test/pkg/IntDefTest.java:92: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+                + "        setFlags(\"\", flag); // ERROR\n"
+                + "                     ~~~~\n"
+                + (SDK_ANNOTATIONS_AVAILABLE ?
+                "src/test/pkg/IntDefTest.java:99: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCALE [WrongConstant]\n"
+                + "        view.setLayoutDirection(View.TEXT_DIRECTION_LTR); // ERROR\n"
+                + "                                ~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:100: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCALE [WrongConstant]\n"
+                + "        view.setLayoutDirection(0); // ERROR\n"
+                + "                                ~\n"
+                + "src/test/pkg/IntDefTest.java:101: Error: Flag not allowed here [WrongConstant]\n"
+                + "        view.setLayoutDirection(View.LAYOUT_DIRECTION_LTR|View.LAYOUT_DIRECTION_RTL); // ERROR\n"
+                + "                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/IntDefTest.java:102: Error: Must be one of: Context.POWER_SERVICE, Context.WINDOW_SERVICE, Context.LAYOUT_INFLATER_SERVICE, Context.ACCOUNT_SERVICE, Context.ACTIVITY_SERVICE, Context.ALARM_SERVICE, Context.NOTIFICATION_SERVICE, Context.ACCESSIBILITY_SERVICE, Context.CAPTIONING_SERVICE, Context.KEYGUARD_SERVICE, Context.LOCATION_SERVICE, Context.SEARCH_SERVICE, Context.SENSOR_SERVICE, Context.STORAGE_SERVICE, Context.WALLPAPER_SERVICE, Context.VIBRATOR_SERVICE, Context.CONNECTIVITY_SERVICE, Context.NETWORK_STATS_SERVICE, Context.WIFI_SERVICE, Context.WIFI_P2P_SERVICE, Context.NSD_SERVICE, Context.AUDIO_SERVICE, Context.FINGERPRINT_SERVICE, Context.MEDIA_ROUTER_SERVICE, Context.TELEPHONY_SERVICE, Context.TELEPHONY_SUBSCRIPTION_SERVICE, Context.CARRIER_CONFIG_SERVICE, Context.TELECOM_SERVICE, Context.CLIPBOARD_SERVICE, Context.INPUT_METHOD_SERVICE, Context.TEXT_SERVICES_MANAGER_SERVICE, Context.APPWIDGET_SERVICE, Context.DROPBOX_SERVICE, Context.DEVICE_POLICY_SERVICE, Context.UI_MODE_SERVICE, Context.DOWNLOAD_SERVICE, Context.NFC_SERVICE, Context.BLUETOOTH_SERVICE, Context.USB_SERVICE, Context.LAUNCHER_APPS_SERVICE, Context.INPUT_SERVICE, Context.DISPLAY_SERVICE, Context.USER_SERVICE, Context.RESTRICTIONS_SERVICE, Context.APP_OPS_SERVICE, Context.CAMERA_SERVICE, Context.PRINT_SERVICE, Context.CONSUMER_IR_SERVICE, Context.TV_INPUT_SERVICE, Context.USAGE_STATS_SERVICE, Context.MEDIA_SESSION_SERVICE, Context.BATTERY_SERVICE, Context.JOB_SCHEDULER_SERVICE, Context.MEDIA_PROJECTION_SERVICE, Context.MIDI_SERVICE [WrongConstant]\n"
+                + "        context.getSystemService(TYPE_1); // ERROR\n"
+                + "                                 ~~~~~~\n"
+                + "20 errors, 0 warnings\n" :
+                "16 errors, 0 warnings\n"),
+
+                lintProject("src/test/pkg/IntDefTest.java.txt=>src/test/pkg/IntDefTest.java",
+                        "src/android/support/annotation/IntDef.java.txt=>src/android/support/annotation/IntDef.java",
+                        "src/android/support/annotation/StringDef.java.txt=>src/android/support/annotation/StringDef.java"
+                ));
+    }
+
+    public void testColorInt() throws Exception {
+        // Needs updated annotations!
+        assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
+                + "src/test/pkg/WrongColor.java:9: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
+                + "        paint2.setColor(R.color.blue);\n"
+                + "                        ~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongColor.java:11: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.red) [ResourceAsColor]\n"
+                + "        textView.setTextColor(R.color.red);\n"
+                + "                              ~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongColor.java:12: Error: Should pass resolved color instead of resource id here: getResources().getColor(android.R.color.black) [ResourceAsColor]\n"
+                + "        textView.setTextColor(android.R.color.black);\n"
+                + "                              ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
+                + "        textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
+                + "                                                        ~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.green) [ResourceAsColor]\n"
+                + "        textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
+                + "                                        ~~~~~~~~~~~~~\n" : "")
+                + "src/test/pkg/WrongColor.java:21: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
+                + "        foo2(R.color.blue);\n"
+                + "             ~~~~~~~~~~~~\n"
+                + "src/test/pkg/WrongColor.java:20: Error: Expected resource of type color [ResourceType]\n"
+                + "        foo1(0xffff0000);\n"
+                + "             ~~~~~~~~~~\n"
+                + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "2 errors, 0 warnings\n"),
+
+                lintProject(
+                        copy("src/test/pkg/WrongColor.java.txt", "src/test/pkg/WrongColor.java"),
+                        copy("src/android/support/annotation/ColorInt.java.txt", "src/android/support/annotation/ColorInt.java"),
+                        mColorResAnnotation
+                ));
+    }
+
+    public void testColorInt2() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/ColorTest.java:23: Error: Should pass resolved color instead of resource id here: getResources().getColor(actualColor) [ResourceAsColor]\n"
+                + "        setColor2(actualColor); // ERROR\n"
+                + "                  ~~~~~~~~~~~\n"
+                + "src/test/pkg/ColorTest.java:24: Error: Should pass resolved color instead of resource id here: getResources().getColor(getColor2()) [ResourceAsColor]\n"
+                + "        setColor2(getColor2()); // ERROR\n"
+                + "                  ~~~~~~~~~~~\n"
+                + "src/test/pkg/ColorTest.java:17: Error: Expected a color resource id (R.color.) but received an RGB integer [ResourceType]\n"
+                + "        setColor1(actualColor); // ERROR\n"
+                + "                  ~~~~~~~~~~~\n"
+                + "src/test/pkg/ColorTest.java:18: Error: Expected a color resource id (R.color.) but received an RGB integer [ResourceType]\n"
+                + "        setColor1(getColor1()); // ERROR\n"
+                + "                  ~~~~~~~~~~~\n"
+                + "4 errors, 0 warnings\n",
+
+                lintProject(
+                        java("src/test/pkg/ColorTest.java", ""
+                                + "package test.pkg;\n"
+                                + "import android.content.Context;\n"
+                                + "import android.content.res.Resources;\n"
+                                + "import android.support.annotation.ColorInt;\n"
+                                + "import android.support.annotation.ColorRes;\n"
+                                + "\n"
+                                + "public abstract class ColorTest {\n"
+                                + "    @ColorInt\n"
+                                + "    public abstract int getColor1();\n"
+                                + "    public abstract void setColor1(@ColorRes int color);\n"
+                                + "    @ColorRes\n"
+                                + "    public abstract int getColor2();\n"
+                                + "    public abstract void setColor2(@ColorInt int color);\n"
+                                + "\n"
+                                + "    public void test1(Context context) {\n"
+                                + "        int actualColor = getColor1();\n"
+                                + "        setColor1(actualColor); // ERROR\n"
+                                + "        setColor1(getColor1()); // ERROR\n"
+                                + "        setColor1(getColor2()); // OK\n"
+                                + "    }\n"
+                                + "    public void test2(Context context) {\n"
+                                + "        int actualColor = getColor2();\n"
+                                + "        setColor2(actualColor); // ERROR\n"
+                                + "        setColor2(getColor2()); // ERROR\n"
+                                + "        setColor2(getColor1()); // OK\n"
+                                + "    }\n"
+                                + "}\n"),
+                        mColorResAnnotation,
+                        mColorIntAnnotation
+                ));
+    }
+
+    public void testColorInt3() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=176321
+
+        if (!SDK_ANNOTATIONS_AVAILABLE) {
+            return;
+        }
+        assertEquals(""
+                        + "src/test/pkg/ColorTest.java:11: Error: Expected a color resource id (R.color.) but received an RGB integer [ResourceType]\n"
+                        + "        setColor(actualColor);\n"
+                        + "                 ~~~~~~~~~~~\n"
+                        + "1 errors, 0 warnings\n",
+
+                lintProject(
+                        java("src/test/pkg/ColorTest.java", ""
+                                + "package test.pkg;\n"
+                                + "import android.content.Context;\n"
+                                + "import android.content.res.Resources;\n"
+                                + "import android.support.annotation.ColorRes;\n"
+                                + "\n"
+                                + "public abstract class ColorTest {\n"
+                                + "    public abstract void setColor(@ColorRes int color);\n"
+                                + "\n"
+                                + "    public void test(Context context, @ColorRes int id) {\n"
+                                + "        int actualColor = context.getResources().getColor(id, null);\n"
+                                + "        setColor(actualColor);\n"
+                                + "    }\n"
+                                + "}\n"),
+                        mColorResAnnotation
+                ));
+    }
+    public void testResourceType() throws Exception {
+        assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
+                + "src/p1/p2/Flow.java:13: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        resources.getDrawable(10); // ERROR\n"
+                + "                              ~~\n"
+                + "src/p1/p2/Flow.java:18: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        resources.getDrawable(R.string.my_string); // ERROR\n"
+                + "                              ~~~~~~~~~~~~~~~~~~\n" : "")
+                + "src/p1/p2/Flow.java:22: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        myMethod(R.string.my_string, null); // ERROR\n"
+                + "                 ~~~~~~~~~~~~~~~~~~\n"
+                + "src/p1/p2/Flow.java:26: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        resources.getDrawable(R.string.my_string); // ERROR\n"
+                + "                              ~~~~~~~~~~~~~~~~~~\n"
+                + "src/p1/p2/Flow.java:32: Error: Expected resource identifier (R.type.name) [ResourceType]\n"
+                + "        myAnyResMethod(50); // ERROR\n"
+                + "                       ~~\n"
+                + (SDK_ANNOTATIONS_AVAILABLE ? "src/p1/p2/Flow.java:60: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        resources.getDrawable(MimeTypes.getAnnotatedString()); // Error\n"
+                + "                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" : "")
+                + "src/p1/p2/Flow.java:68: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        myMethod(z, null); // ERROR\n"
+                + "                 ~\n"
+                + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "4 errors, 0 warnings\n"),
+
+                lintProject(
+                        copy("src/p1/p2/Flow.java.txt", "src/p1/p2/Flow.java"),
+                        copy("src/android/support/annotation/DrawableRes.java.txt", "src/android/support/annotation/DrawableRes.java"),
+                        mStringResAnnotation,
+                        mStyleResAnnotation,
+                        mAnyResAnnotation
+                ));
+    }
+
+    public void testTypes2() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/ActivityType.java:5: Error: Expected resource of type drawable [ResourceType]\n"
+                + "    SKI(1),\n"
+                + "        ~\n"
+                + "src/test/pkg/ActivityType.java:6: Error: Expected resource of type drawable [ResourceType]\n"
+                + "    SNOWBOARD(2);\n"
+                + "              ~\n"
+                + "2 errors, 0 warnings\n",
+
+                lintProject(
+                        java("src/test/pkg/ActivityType.java", ""
+                                + "import android.support.annotation.DrawableRes;\n"
+                                + "\n"
+                                + "enum ActivityType {\n"
+                                + "\n"
+                                + "    SKI(1),\n"
+                                + "    SNOWBOARD(2);\n"
+                                + "\n"
+                                + "    private final int mIconResId;\n"
+                                + "\n"
+                                + "    ActivityType(@DrawableRes int iconResId) {\n"
+                                + "        mIconResId = iconResId;\n"
+                                + "    }\n"
+                                + "}"),
+                        copy("src/android/support/annotation/DrawableRes.java.txt",
+                                "src/android/support/annotation/DrawableRes.java")));
+    }
+
+    @SuppressWarnings({"MethodMayBeStatic", "ResultOfObjectAllocationIgnored"})
+    public void testConstructor() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/ConstructorTest.java:14: Error: Expected resource of type drawable [ResourceType]\n"
+                + "        new ConstructorTest(1, 3);\n"
+                + "                            ~\n"
+                + "src/test/pkg/ConstructorTest.java:14: Error: Value must be ≥ 5 (was 3) [Range]\n"
+                + "        new ConstructorTest(1, 3);\n"
+                + "                               ~\n"
+                + "src/test/pkg/ConstructorTest.java:19: Error: Method test.pkg.ConstructorTest must be called from the UI thread, currently inferred thread is worker thread [WrongThread]\n"
+                + "        new ConstructorTest(res, range);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "3 errors, 0 warnings\n",
+
+                lintProject(
+                        java("src/test/pkg/ConstructorTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.support.annotation.DrawableRes;\n"
+                                + "import android.support.annotation.IntRange;\n"
+                                + "import android.support.annotation.UiThread;\n"
+                                + "import android.support.annotation.WorkerThread;\n"
+                                + "\n"
+                                + "public class ConstructorTest {\n"
+                                + "    @UiThread\n"
+                                + "    ConstructorTest(@DrawableRes int iconResId, @IntRange(from = 5) int start) {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public void testParameters() {\n"
+                                + "        new ConstructorTest(1, 3);\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    @WorkerThread\n"
+                                + "    public void testMethod(int res, int range) {\n"
+                                + "        new ConstructorTest(res, range);\n"
+                                + "    }\n"
+                                + "}\n"),
+                        mWorkerThreadPermission,
+                        mUiThreadPermission,
+                        copy("src/android/support/annotation/DrawableRes.java.txt",
+                                "src/android/support/annotation/DrawableRes.java"),
+                        copy("src/android/support/annotation/IntRange.java.txt",
+                                "src/android/support/annotation/IntRange.java")
+                ));
+    }
+
+    public void testColorAsDrawable() throws Exception {
+        assertEquals(
+                "No warnings.",
+
+                lintProject("src/p1/p2/ColorAsDrawable.java.txt=>src/p1/p2/ColorAsDrawable.java"));
+    }
+
+    public void testCheckResult() throws Exception {
+        if (!SDK_ANNOTATIONS_AVAILABLE) {
+            // Currently only tests @CheckResult on SDK annotations
+            return;
+        }
+        assertEquals(""
+                + "src/test/pkg/CheckPermissions.java:22: Warning: The result of extractAlpha is not used [CheckResult]\n"
+                + "        bitmap.extractAlpha(); // WARNING\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/CheckPermissions.java:10: Warning: The result of checkCallingOrSelfPermission is not used; did you mean to call #enforceCallingOrSelfPermission(String,String)? [UseCheckPermission]\n"
+                + "        context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // WRONG\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/CheckPermissions.java:11: Warning: The result of checkPermission is not used; did you mean to call #enforcePermission(String,int,int,String)? [UseCheckPermission]\n"
+                + "        context.checkPermission(Manifest.permission.INTERNET, 1, 1);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "0 errors, 3 warnings\n",
+
+                lintProject("src/test/pkg/CheckPermissions.java.txt=>src/test/pkg/CheckPermissions.java"));
+    }
+
+    private final TestFile mPermissionTest = java("src/test/pkg/PermissionTest.java", ""
+                + "package test.pkg;\n"
+                + "\n"
+                + "import android.location.LocationManager;\n"
+                + "\n"
+                + "public class PermissionTest {\n"
+                + "    public static void test(LocationManager locationManager, String provider) {\n"
+                + "        LocationManager.Location location = locationManager.myMethod(provider);\n"
+                + "    }\n"
+                + "}\n");
+
+    private final TestFile mLocationManagerStub = java("src/android/location/LocationManager.java", ""
+                + "package android.location;\n"
+                + "\n"
+                + "import android.support.annotation.RequiresPermission;\n"
+                + "\n"
+                + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
+                + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
+                + "\n"
+                + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+                + "public abstract class LocationManager {\n"
+                + "    @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})\n"
+                + "    public abstract Location myMethod(String provider);\n"
+                + "    public static class Location {\n"
+                + "    }\n"
+                + "}\n");
+
+        private final TestFile mComplexLocationManagerStub = java("src/android/location/LocationManager.java", ""
+                + "package android.location;\n"
+                + "\n"
+                + "import android.support.annotation.RequiresPermission;\n"
+                + "\n"
+                + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
+                + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
+                + "import static android.Manifest.permission.BLUETOOTH;\n"
+                + "import static android.Manifest.permission.READ_SMS;\n"
+                + "\n"
+                + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+                + "public abstract class LocationManager {\n"
+                + "    @RequiresPermission(\"(\" + ACCESS_FINE_LOCATION + \"|| \" + ACCESS_COARSE_LOCATION + \") && (\" + BLUETOOTH + \" ^ \" + READ_SMS + \")\")\n"
+                + "    public abstract Location myMethod(String provider);\n"
+                + "    public static class Location {\n"
+                + "    }\n"
+                + "}\n");
+
+        private final TestFile mRequirePermissionAnnotation = java("src/android/support/annotation/RequiresPermission.java", ""
+                + "/*\n"
+                + " * Copyright (C) 2015 The Android Open Source Project\n"
+                + " *\n"
+                + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
+                + " * you may not use this file except in compliance with the License.\n"
+                + " * You may obtain a copy of the License at\n"
+                + " *\n"
+                + " *      http://www.apache.org/licenses/LICENSE-2.0\n"
+                + " *\n"
+                + " * Unless required by applicable law or agreed to in writing, software\n"
+                + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
+                + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
+                + " * See the License for the specific language governing permissions and\n"
+                + " * limitations under the License.\n"
+                + " */\n"
+                + "package android.support.annotation;\n"
+                + "\n"
+                + "import java.lang.annotation.Retention;\n"
+                + "import java.lang.annotation.Target;\n"
+                + "\n"
+                + "import static java.lang.annotation.ElementType.*;\n"
+                + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+                + "@Retention(CLASS)\n"
+                + "@Target({METHOD,CONSTRUCTOR,FIELD,PARAMETER,ANNOTATION_TYPE})\n"
+                + "public @interface RequiresPermission {\n"
+                + "    String value() default \"\";\n"
+                + "    String[] allOf() default {};\n"
+                + "    String[] anyOf() default {};\n"
+                + "    boolean conditional() default false;\n"
+                + "    String notes() default \"\";\n"
+                + "    @Target({FIELD,METHOD,PARAMETER})\n"
+                + "    @interface Read {\n"
+                + "        RequiresPermission value();\n"
+                + "    }\n"
+                + "    @Target({FIELD,METHOD,PARAMETER})\n"
+                + "    @interface Write {\n"
+                + "        RequiresPermission value();\n"
+                + "    }\n"
+                + "}");
+
+    private final TestFile mUiThreadPermission = java("src/android/support/annotation/UiThread.java", ""
+            + "package android.support.annotation;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "\n"
+            + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n"
+            + "import static java.lang.annotation.ElementType.METHOD;\n"
+            + "import static java.lang.annotation.ElementType.TYPE;\n"
+            + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+            + "\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({METHOD,CONSTRUCTOR,TYPE})\n"
+            + "public @interface UiThread {\n"
+            + "}\n");
+
+    private final TestFile mMainThreadPermission = java("src/android/support/annotation/MainThread.java", ""
+            + "package android.support.annotation;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "\n"
+            + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n"
+            + "import static java.lang.annotation.ElementType.METHOD;\n"
+            + "import static java.lang.annotation.ElementType.TYPE;\n"
+            + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+            + "\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({METHOD,CONSTRUCTOR,TYPE})\n"
+            + "public @interface MainThread {\n"
+            + "}\n");
+
+    private final TestFile mWorkerThreadPermission = java("src/android/support/annotation/WorkerThread.java", ""
+            + "package android.support.annotation;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "\n"
+            + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n"
+            + "import static java.lang.annotation.ElementType.METHOD;\n"
+            + "import static java.lang.annotation.ElementType.TYPE;\n"
+            + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+            + "\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({METHOD,CONSTRUCTOR,TYPE})\n"
+            + "public @interface WorkerThread {\n"
+            + "}\n");
+
+    private TestFile createResAnnotation(String prefix) {
+        return java("src/android/support/annotation/" + prefix + "Res.java", ""
+                + "package android.support.annotation;\n"
+                + "\n"
+                + "import java.lang.annotation.Retention;\n"
+                + "import java.lang.annotation.Target;\n"
+                + "\n"
+                + "import static java.lang.annotation.ElementType.*;\n"
+                + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+                + "\n"
+                + "@Retention(CLASS)\n"
+                + "@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})\n"
+                + "public @interface " + prefix + "Res {\n"
+                + "}\n");
+    }
+
+    private final TestFile mColorResAnnotation = createResAnnotation("Color");
+    private final TestFile mStringResAnnotation = createResAnnotation("String");
+    private final TestFile mStyleResAnnotation = createResAnnotation("Style");
+    private final TestFile mAnyResAnnotation = createResAnnotation("Any");
+
+    private final TestFile mColorIntAnnotation = java("src/android/support/annotation/ColorInt.java", ""
+            + "package android.support.annotation;\n"
+            + "\n"
+            + "import java.lang.annotation.Retention;\n"
+            + "import java.lang.annotation.Target;\n"
+            + "\n"
+            + "import static java.lang.annotation.ElementType.*;\n"
+            + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+            + "\n"
+            + "@Retention(CLASS)\n"
+            + "@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})\n"
+            + "public @interface ColorInt {\n"
+            + "}\n");
+
+    private TestFile getManifestWithPermissions(int targetSdk, String... permissions) {
+        return getManifestWithPermissions(1, targetSdk, permissions);
+    }
+
+    private TestFile getManifestWithPermissions(int minSdk, int targetSdk, String... permissions) {
+        StringBuilder permissionBlock = new StringBuilder();
+        for (String permission : permissions) {
+            permissionBlock.append("    <uses-permission android:name=\"").append(permission)
+                    .append("\" />\n");
+        }
+        return xml("AndroidManifest.xml", ""
+                + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+                + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+                + "    package=\"foo.bar2\"\n"
+                + "    android:versionCode=\"1\"\n"
+                + "    android:versionName=\"1.0\" >\n"
+                + "\n"
+                + "    <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\""
+                + targetSdk + "\" />\n"
+                + "\n"
+                + permissionBlock.toString()
+                + "\n"
+                + "    <application\n"
+                + "        android:icon=\"@drawable/ic_launcher\"\n"
+                + "        android:label=\"@string/app_name\" >\n"
+                + "    </application>\n"
+                + "\n"
+                + "</manifest>");
+    }
+
+    private TestFile mRevokeTest = java("src/test/pkg/RevokeTest.java", ""
+            + "package test.pkg;\n"
+            + "\n"
+            + "import android.content.Context;\n"
+            + "import android.content.pm.PackageManager;\n"
+            + "import android.location.LocationManager;\n"
+            + "\n"
+            + "import java.security.AccessControlException;\n"
+            + "\n"
+            + "public class RevokeTest {\n"
+            + "    public static void test1(LocationManager locationManager, String provider) {\n"
+            + "        try {\n"
+            + "            // Ok: Security exception caught in one of the branches\n"
+            + "            locationManager.myMethod(provider); // OK\n"
+            + "        } catch (IllegalArgumentException ignored) {\n"
+            + "        } catch (SecurityException ignored) {\n"
+            + "        }\n"
+            + "\n"
+            + "        try {\n"
+            + "            // Ok: Security exception super class caught in one of the branches\n"
+            + "            locationManager.myMethod(provider); // OK\n"
+            + "        } catch (RuntimeException e) { // includes Security Exception\n"
+            + "        }\n"
+            + "\n"
+            + "        try {\n"
+            + "            // Ok: Caught in outer statement\n"
+            + "            try {\n"
+            + "                locationManager.myMethod(provider); // OK\n"
+            + "            } catch (IllegalArgumentException e) {\n"
+            + "                // inner\n"
+            + "            }\n"
+            + "        } catch (SecurityException ignored) {\n"
+            + "        }\n"
+            + "\n"
+            + "        try {\n"
+            + "            // Ok: Security exception super class caught in one of the branches\n"
+            + "            locationManager.myMethod(provider); // OK\n"
+            + "        } catch (Exception e) { // includes Security Exception\n"
+            + "        }\n"
+            + "\n"
+            + "        // NOT OK: Catching security exception subclass (except for dedicated ones?)\n"
+            + "\n"
+            + "        try {\n"
+            + "            // Error: catching security exception, but not all of them\n"
+            + "            locationManager.myMethod(provider); // ERROR\n"
+            + "        } catch (AccessControlException e) { // security exception but specific one\n"
+            + "        }\n"
+            + "    }\n"
+            + "\n"
+            + "    public static void test2(LocationManager locationManager, String provider) {\n"
+            + "        locationManager.myMethod(provider); // ERROR: not caught\n"
+            + "    }\n"
+            + "\n"
+            + "    public static void test3(LocationManager locationManager, String provider)\n"
+            + "            throws IllegalArgumentException {\n"
+            + "        locationManager.myMethod(provider); // ERROR: not caught by right type\n"
+            + "    }\n"
+            + "\n"
+            + "    public static void test4(LocationManager locationManager, String provider)\n"
+            + "            throws AccessControlException {  // Security exception but specific one\n"
+            + "        locationManager.myMethod(provider); // ERROR\n"
+            + "    }\n"
+            + "\n"
+            + "    public static void test5(LocationManager locationManager, String provider)\n"
+            + "            throws SecurityException {\n"
+            + "        locationManager.myMethod(provider); // OK\n"
+            + "    }\n"
+            + "\n"
+            + "    public static void test6(LocationManager locationManager, String provider)\n"
+            + "            throws Exception { // includes Security Exception\n"
+            + "        locationManager.myMethod(provider); // OK\n"
+            + "    }\n"
+            + "\n"
+            + "    public static void test7(LocationManager locationManager, String provider, Context context)\n"
+            + "            throws IllegalArgumentException {\n"
+            + "        if (context.getPackageManager().checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, context.getPackageName()) != PackageManager.PERMISSION_GRANTED) {\n"
+            + "            return;\n"
+            + "        }\n"
+            + "        locationManager.myMethod(provider); // OK: permission checked\n"
+            + "    }\n"
+            + "\n"
+            + "}\n");
+
+    public void testMissingPermissions() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION [MissingPermission]\n"
+                + "        LocationManager.Location location = locationManager.myMethod(provider);\n"
+                + "                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+                lintProject(
+                        getManifestWithPermissions(14),
+                        mPermissionTest,
+                        mLocationManagerStub,
+                        mRequirePermissionAnnotation));
+    }
+
+    public void testHasPermission() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(
+                        getManifestWithPermissions(14, "android.permission.ACCESS_FINE_LOCATION"),
+                        mPermissionTest,
+                        mLocationManagerStub,
+                        mRequirePermissionAnnotation));
+    }
+
+    public void testRevokePermissions() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/RevokeTest.java:44: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+                + "            locationManager.myMethod(provider); // ERROR\n"
+                + "            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RevokeTest.java:50: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+                + "        locationManager.myMethod(provider); // ERROR: not caught\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RevokeTest.java:55: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+                + "        locationManager.myMethod(provider); // ERROR: not caught by right type\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/RevokeTest.java:60: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+                + "        locationManager.myMethod(provider); // ERROR\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "4 errors, 0 warnings\n",
+                lintProject(
+                        getManifestWithPermissions(23, "android.permission.ACCESS_FINE_LOCATION"),
+                        mLocationManagerStub,
+                        mRequirePermissionAnnotation,
+                        mRevokeTest
+                ));
+    }
+
+    public void testImpliedPermissions() throws Exception {
+        // Regression test for
+        //   https://code.google.com/p/android/issues/detail?id=177381
+        assertEquals(""
+                + "src/test/pkg/PermissionTest2.java:11: Error: Missing permissions required by PermissionTest2.method1: my.permission.PERM2 [MissingPermission]\n"
+                + "        method1(); // ERROR\n"
+                + "        ~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+                lintProject(
+                        getManifestWithPermissions(14, 14, "android.permission.ACCESS_FINE_LOCATION"),
+                        java("src/test/pkg/PermissionTest2.java", ""
+                                + "package test.pkg;\n"
+                                + "import android.support.annotation.RequiresPermission;\n"
+                                + "\n"
+                                + "public class PermissionTest2 {\n"
+                                + "    @RequiresPermission(allOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"})\n"
+                                + "    public void method1() {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    @RequiresPermission(\"my.permission.PERM1\")\n"
+                                + "    public void method2() {\n"
+                                + "        method1(); // ERROR\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    @RequiresPermission(allOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"})\n"
+                                + "    public void method3() {\n"
+                                + "        // The above @RequiresPermission implies that we are holding these\n"
+                                + "        // permissions here, so the call to method1() should not be flagged as\n"
+                                + "        // missing a permission!\n"
+                                + "        method1(); // OK\n"
+                                + "    }\n"
+                                + "}\n"),
+                        mRequirePermissionAnnotation
+                ));
+    }
+
+    public void testRevokePermissionsPre23() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(
+                        getManifestWithPermissions(14, "android.permission.ACCESS_FINE_LOCATION"),
+                        mLocationManagerStub,
+                        mRequirePermissionAnnotation,
+                        mRevokeTest
+                ));
+    }
+
+    public void testComplexPermission1() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.BLUETOOTH xor android.permission.READ_SMS [MissingPermission]\n"
+                + "        LocationManager.Location location = locationManager.myMethod(provider);\n"
+                + "                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+                lintProject(
+                        getManifestWithPermissions(14,
+                                "android.permission.ACCESS_FINE_LOCATION"),
+                        mPermissionTest,
+                        mComplexLocationManagerStub,
+                        mRequirePermissionAnnotation));
+    }
+
+    public void testComplexPermission2() throws Exception {
+        assertEquals("No warnings.",
+                lintProject(
+                        getManifestWithPermissions(14,
+                                "android.permission.ACCESS_FINE_LOCATION",
+                                "android.permission.BLUETOOTH"),
+                        mPermissionTest,
+                        mComplexLocationManagerStub,
+                        mRequirePermissionAnnotation));
+    }
+
+    public void testComplexPermission3() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.BLUETOOTH xor android.permission.READ_SMS [MissingPermission]\n"
+                + "        LocationManager.Location location = locationManager.myMethod(provider);\n"
+                + "                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+                lintProject(
+                        getManifestWithPermissions(14,
+                                "android.permission.ACCESS_FINE_LOCATION",
+                                "android.permission.BLUETOOTH",
+                                "android.permission.READ_SMS"),
+                        mPermissionTest,
+                        mComplexLocationManagerStub,
+                        mRequirePermissionAnnotation));
+    }
+
+    public void testPermissionAnnotation() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/LocationManager.java:24: Error: Missing permissions required by LocationManager.getLastKnownLocation: android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION [MissingPermission]\n"
+                + "        Location location = manager.getLastKnownLocation(\"provider\");\n"
+                + "                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "1 errors, 0 warnings\n",
+                lintProject(
+                        java("src/test/pkg/LocationManager.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.support.annotation.RequiresPermission;\n"
+                                + "\n"
+                                + "import java.lang.annotation.Retention;\n"
+                                + "import java.lang.annotation.RetentionPolicy;\n"
+                                + "\n"
+                                + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
+                                + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
+                                + "\n"
+                                + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+                                + "public abstract class LocationManager {\n"
+                                + "    @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    @interface AnyLocationPermission {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    @AnyLocationPermission\n"
+                                + "    public abstract Location getLastKnownLocation(String provider);\n"
+                                + "    public static class Location {\n"
+                                + "    }\n"
+                                + "    \n"
+                                + "    public static void test(LocationManager manager) {\n"
+                                + "        Location location = manager.getLastKnownLocation(\"provider\");\n"
+                                + "    }\n"
+                                + "}\n"),
+                        mRequirePermissionAnnotation));
+    }
+
+    public void testThreading() throws Exception {
+        assertEquals(""
+                + "src/test/pkg/ThreadTest.java:15: Error: Method onPreExecute must be called from the main thread, currently inferred thread is worker thread [WrongThread]\n"
+                + "                onPreExecute(); // ERROR\n"
+                + "                ~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ThreadTest.java:16: Error: Method paint must be called from the UI thread, currently inferred thread is worker thread [WrongThread]\n"
+                + "                view.paint(); // ERROR\n"
+                + "                ~~~~~~~~~~~~\n"
+                + "src/test/pkg/ThreadTest.java:22: Error: Method publishProgress must be called from the worker thread, currently inferred thread is main thread [WrongThread]\n"
+                + "                publishProgress(); // ERROR\n"
+                + "                ~~~~~~~~~~~~~~~~~\n"
+                + "3 errors, 0 warnings\n",
+
+            lintProject(
+                java("src/test/pkg/ThreadTest.java", ""
+                        + "package test.pkg;\n"
+                        + "\n"
+                        + "import android.support.annotation.MainThread;\n"
+                        + "import android.support.annotation.UiThread;\n"
+                        + "import android.support.annotation.WorkerThread;\n"
+                        + "\n"
+                        + "public class ThreadTest {\n"
+                        + "    public static AsyncTask testTask() {\n"
+                        + "\n"
+                        + "        return new AsyncTask() {\n"
+                        + "            final CustomView view = new CustomView();\n"
+                        + "\n"
+                        + "            @Override\n"
+                        + "            protected void doInBackground(Object... params) {\n"
+                        + "                onPreExecute(); // ERROR\n"
+                        + "                view.paint(); // ERROR\n"
+                        + "                publishProgress(); // OK\n"
+                        + "            }\n"
+                        + "\n"
+                        + "            @Override\n"
+                        + "            protected void onPreExecute() {\n"
+                        + "                publishProgress(); // ERROR\n"
+                        + "                onProgressUpdate(); // OK\n"
+                        + "            }\n"
+                        + "        };\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    @UiThread\n"
+                        + "    public static class View {\n"
+                        + "        public void paint() {\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    public static class CustomView extends View {\n"
+                        + "        @Override public void paint() {\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "\n"
+                        + "    public abstract static class AsyncTask {\n"
+                        + "        @WorkerThread\n"
+                        + "        protected abstract void doInBackground(Object... params);\n"
+                        + "\n"
+                        + "        @MainThread\n"
+                        + "        protected void onPreExecute() {\n"
+                        + "        }\n"
+                        + "\n"
+                        + "        @MainThread\n"
+                        + "        protected void onProgressUpdate(Object... values) {\n"
+                        + "        }\n"
+                        + "\n"
+                        + "        @WorkerThread\n"
+                        + "        protected final void publishProgress(Object... values) {\n"
+                        + "        }\n"
+                        + "    }\n"
+                        + "}\n"),
+                        mUiThreadPermission,
+                        mMainThreadPermission,
+                        mWorkerThreadPermission));
+    }
+
+    public void testIntentPermission() throws Exception {
+        if (SDK_ANNOTATIONS_AVAILABLE) {
+            TestLintClient client = createClient();
+            ExternalAnnotationRepository repository = ExternalAnnotationRepository.get(client);
+            ResolvedMethod method = ExternalAnnotationRepositoryTest.createMethod(
+                    "android.content.Context", "void", "startActivity",
+                    "android.content.Intent");
+            ResolvedAnnotation a = repository.getAnnotation(method, 0, PERMISSION_ANNOTATION);
+            if (a == null) {
+                // Running tests from outside the IDE (where it can't find the
+                // bundled up to date annotations in tools/adt/idea/android/annotations)
+                // and we have the annotations.zip file available in platform-tools,
+                // but its contents are old (it's from Android M Preview 1, not including
+                // the new intent-annotation data); skip this test for now.
+                return;
+            }
+        }
+
+        assertEquals(!SDK_ANNOTATIONS_AVAILABLE ? "" // Most of the intent/content provider checks are based on framework annotations
+                + "src/test/pkg/ActionTest.java:86: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        myStartActivity(\"\", null, new Intent(ACTION_CALL));\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:87: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        myReadResolverMethod(\"\", BOOKMARKS_URI);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:88: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        myWriteResolverMethod(BOOKMARKS_URI);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "3 errors, 0 warnings\n" : ""
+
+                + "src/test/pkg/ActionTest.java:36: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivity(intent);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:42: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivity(intent);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:43: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivity(intent, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:44: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivityForResult(intent, 0);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:45: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivityFromChild(activity, intent, 0);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:46: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivityIfNeeded(intent, 0);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:47: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startActivityFromFragment(null, intent, 0);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:48: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        activity.startNextMatchingActivity(intent);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:54: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        context.sendBroadcast(intent);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:55: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        context.sendBroadcast(intent, \"\");\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:56: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        context.sendBroadcastAsUser(intent, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:57: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        context.sendStickyBroadcast(intent);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:62: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        resolver.query(BOOKMARKS_URI, null, null, null, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:65: Error: Missing permissions required to write ActionTest.BOOKMARKS_URI: com.android.browser.permission.WRITE_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        resolver.insert(BOOKMARKS_URI, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:66: Error: Missing permissions required to write ActionTest.BOOKMARKS_URI: com.android.browser.permission.WRITE_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        resolver.delete(BOOKMARKS_URI, null, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:67: Error: Missing permissions required to write ActionTest.BOOKMARKS_URI: com.android.browser.permission.WRITE_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        resolver.update(BOOKMARKS_URI, null, null, null);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:86: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+                + "        myStartActivity(\"\", null, new Intent(ACTION_CALL));\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:87: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        myReadResolverMethod(\"\", BOOKMARKS_URI);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "src/test/pkg/ActionTest.java:88: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+                + "        myWriteResolverMethod(BOOKMARKS_URI);\n"
+                + "        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+                + "19 errors, 0 warnings\n",
+
+                lintProject(
+                        getManifestWithPermissions(14, 23),
+                        java("src/test/pkg/ActionTest.java", ""
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.Manifest;\n"
+                                + "import android.app.Activity;\n"
+                                + "import android.content.ContentResolver;\n"
+                                + "import android.content.Context;\n"
+                                + "import android.content.Intent;\n"
+                                + "import android.database.Cursor;\n"
+                                + "import android.net.Uri;\n"
+                                + "import android.support.annotation.RequiresPermission;\n"
+                                + "\n"
+                                //+ "import static android.Manifest.permission.READ_HISTORY_BOOKMARKS;\n"
+                                //+ "import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS;\n"
+                                + "\n"
+                                + "@SuppressWarnings({\"deprecation\", \"unused\"})\n"
+                                + "public class ActionTest {\n"
+                                + "     public static final String READ_HISTORY_BOOKMARKS=\"com.android.browser.permission.READ_HISTORY_BOOKMARKS\";\n"
+                                + "     public static final String WRITE_HISTORY_BOOKMARKS=\"com.android.browser.permission.WRITE_HISTORY_BOOKMARKS\";\n"
+                                + "    @RequiresPermission(Manifest.permission.CALL_PHONE)\n"
+                                + "    public static final String ACTION_CALL = \"android.intent.action.CALL\";\n"
+                                + "\n"
+                                + "    @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))\n"
+                                + "    @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))\n"
+                                + "    public static final Uri BOOKMARKS_URI = Uri.parse(\"content://browser/bookmarks\");\n"
+                                + "\n"
+                                + "    public static final Uri COMBINED_URI = Uri.withAppendedPath(BOOKMARKS_URI, \"bookmarks\");\n"
+                                + "    \n"
+                                + "    public static void activities1(Activity activity) {\n"
+                                + "        Intent intent = new Intent(Intent.ACTION_CALL);\n"
+                                + "        intent.setData(Uri.parse(\"tel:1234567890\"));\n"
+                                + "        // This one will only be flagged if we have framework metadata on Intent.ACTION_CALL\n"
+                                + "        activity.startActivity(intent);\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public static void activities2(Activity activity) {\n"
+                                + "        Intent intent = new Intent(ACTION_CALL);\n"
+                                + "        intent.setData(Uri.parse(\"tel:1234567890\"));\n"
+                                + "        activity.startActivity(intent);\n"
+                                + "    }\n"
+                                + "    public static void activities3(Activity activity) {\n"
+                                + "        Intent intent;\n"
+                                + "        intent = new Intent(ACTION_CALL);\n"
+                                + "        intent.setData(Uri.parse(\"tel:1234567890\"));\n"
+                                + "        activity.startActivity(intent);\n"
+                                + "        activity.startActivity(intent, null);\n"
+                                + "        activity.startActivityForResult(intent, 0);\n"
+                                + "        activity.startActivityFromChild(activity, intent, 0);\n"
+                                + "        activity.startActivityIfNeeded(intent, 0);\n"
+                                + "        activity.startActivityFromFragment(null, intent, 0);\n"
+                                + "        activity.startNextMatchingActivity(intent);\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public static void broadcasts(Context context) {\n"
+                                + "        Intent intent;\n"
+                                + "        intent = new Intent(ACTION_CALL);\n"
+                                + "        context.sendBroadcast(intent);\n"
+                                + "        context.sendBroadcast(intent, \"\");\n"
+                                + "        context.sendBroadcastAsUser(intent, null);\n"
+                                + "        context.sendStickyBroadcast(intent);\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public static void contentResolvers(Context context, ContentResolver resolver) {\n"
+                                + "        // read\n"
+                                + "        resolver.query(BOOKMARKS_URI, null, null, null, null);\n"
+                                + "\n"
+                                + "        // write\n"
+                                + "        resolver.insert(BOOKMARKS_URI, null);\n"
+                                + "        resolver.delete(BOOKMARKS_URI, null, null);\n"
+                                + "        resolver.update(BOOKMARKS_URI, null, null, null);\n"
+                                + "\n"
+                                + "        // Framework (external) annotation\n"
+                                + "//REMOVED        resolver.query(android.provider.Browser.BOOKMARKS_URI, null, null, null, null);\n"
+                                + "\n"
+                                + "        // TODO: Look for more complex URI manipulations\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public static void myStartActivity(String s1, String s2, \n"
+                                + "                                       @RequiresPermission Intent intent) {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public static void myReadResolverMethod(String s1, @RequiresPermission.Read(@RequiresPermission) Uri uri) {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public static void myWriteResolverMethod(@RequiresPermission.Read(@RequiresPermission) Uri uri) {\n"
+                                + "    }\n"
+                                + "    \n"
+                                + "    public static void testCustomMethods() {\n"
+                                + "        myStartActivity(\"\", null, new Intent(ACTION_CALL));\n"
+                                + "        myReadResolverMethod(\"\", BOOKMARKS_URI);\n"
+                                + "        myWriteResolverMethod(BOOKMARKS_URI);\n"
+                                + "    }\n"
+                                + "}\n"),
+                        mRequirePermissionAnnotation
+                ));
+    }
+
+    public void testCombinedIntDefAndIntRange() throws Exception {
+        assertEquals(""
+                        + "src/test/pkg/X.java:27: Error: Must be one of: X.LENGTH_INDEFINITE, X.LENGTH_SHORT, X.LENGTH_LONG [WrongConstant]\n"
+                        + "        setDuration(UNRELATED); /// ERROR: Not right intdef, even if it's in the right number range\n"
+                        + "                    ~~~~~~~~~\n"
+                        + "src/test/pkg/X.java:28: Error: Must be one of: X.LENGTH_INDEFINITE, X.LENGTH_SHORT, X.LENGTH_LONG or value must be ≥ 10 (was -5) [WrongConstant]\n"
+                        + "        setDuration(-5); // ERROR (not right int def or value\n"
+                        + "                    ~~\n"
+                        + "src/test/pkg/X.java:29: Error: Must be one of: X.LENGTH_INDEFINITE, X.LENGTH_SHORT, X.LENGTH_LONG or value must be ≥ 10 (was 8) [WrongConstant]\n"
+                        + "        setDuration(8); // ERROR (not matching number range)\n"
+                        + "                    ~\n"
+                        + "3 errors, 0 warnings\n",
+                lintProject(
+                        getManifestWithPermissions(14, 23),
+                        java("src/test/pkg/X.java", ""
+                                + "\n"
+                                + "package test.pkg;\n"
+                                + "\n"
+                                + "import android.support.annotation.IntDef;\n"
+                                + "import android.support.annotation.IntRange;\n"
+                                + "\n"
+                                + "import java.lang.annotation.Retention;\n"
+                                + "import java.lang.annotation.RetentionPolicy;\n"
+                                + "\n"
+                                + "@SuppressWarnings({\"UnusedParameters\", \"unused\", \"SpellCheckingInspection\"})\n"
+                                + "public class X {\n"
+                                + "\n"
+                                + "    public static final int UNRELATED = 500;\n"
+                                + "\n"
+                                + "    @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})\n"
+                                + "    @IntRange(from = 10)\n"
+                                + "    @Retention(RetentionPolicy.SOURCE)\n"
+                                + "    public @interface Duration {}\n"
+                                + "\n"
+                                + "    public static final int LENGTH_INDEFINITE = -2;\n"
+                                + "    public static final int LENGTH_SHORT = -1;\n"
+                                + "    public static final int LENGTH_LONG = 0;\n"
+                                + "    public void setDuration(@Duration int duration) {\n"
+                                + "    }\n"
+                                + "\n"
+                                + "    public void test() {\n"
+                                + "        setDuration(UNRELATED); /// ERROR: Not right intdef, even if it's in the right number range\n"
+                                + "        setDuration(-5); // ERROR (not right int def or value\n"
+                                + "        setDuration(8); // ERROR (not matching number range)\n"
+                                + "        setDuration(8000); // OK (@IntRange applies)\n"
+                                + "        setDuration(LENGTH_INDEFINITE); // OK (@IntDef)\n"
+                                + "        setDuration(LENGTH_LONG); // OK (@IntDef)\n"
+                                + "        setDuration(LENGTH_SHORT); // OK (@IntDef)\n"
+                                + "    }\n"
+                                + "}\n"),
+                        copy("src/android/support/annotation/IntDef.java.txt", "src/android/support/annotation/IntDef.java"),
+                        copy("src/android/support/annotation/IntRange.java.txt", "src/android/support/annotation/IntRange.java")
+                ));
+    }
+
+
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
new file mode 100644
index 0000000..673685e
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
@@ -0,0 +1,328 @@
+/*
+ * 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.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.io.File;
+import java.util.Arrays;
+
+@SuppressWarnings("javadoc")
+public class UnusedResourceDetectorTest extends AbstractCheckTest {
+    private boolean mEnableIds = false;
+
+    @Override
+    protected Detector getDetector() {
+        return new UnusedResourceDetector();
+    }
+
+    @Override
+    protected boolean isEnabled(Issue issue) {
+        if (issue == UnusedResourceDetector.ISSUE_IDS) {
+            return mEnableIds;
+        } else {
+            return true;
+        }
+    }
+
+    public void testUnused() throws Exception {
+        mEnableIds = false;
+        assertEquals(
+           "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
+           "res/layout/main.xml: Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
+           "res/layout/other.xml: Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
+           "res/values/strings2.xml:3: Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
+           "    <string name=\"hello\">Hello</string>\n" +
+           "            ~~~~~~~~~~~~\n" +
+           "0 errors, 4 warnings\n" +
+           "",
+
+            lintProject(
+                "res/values/strings2.xml",
+                "res/layout/layout1.xml=>res/layout/main.xml",
+                "res/layout/layout1.xml=>res/layout/other.xml",
+
+                // Rename .txt files to .java
+                "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
+                "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
+                "AndroidManifest.xml",
+                "res/layout/accessibility.xml"));
+    }
+
+    public void testUnusedIds() throws Exception {
+        mEnableIds = true;
+
+        assertEquals(
+           "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
+           "Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
+           "Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
+           "Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
+           "res/layout/accessibility.xml:2: Warning: The resource R.id.newlinear appears to be unused [UnusedIds]\n" +
+           "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\">\n" +
+           "                                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+           "res/layout/accessibility.xml:3: Warning: The resource R.id.button1 appears to be unused [UnusedIds]\n" +
+           "    <Button android:text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
+           "                                  ~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+           "res/layout/accessibility.xml:4: Warning: The resource R.id.android_logo appears to be unused [UnusedIds]\n" +
+           "    <ImageView android:id=\"@+id/android_logo\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
+           "               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+           "res/layout/accessibility.xml:5: Warning: The resource R.id.android_logo2 appears to be unused [UnusedIds]\n" +
+           "    <ImageButton android:importantForAccessibility=\"yes\" android:id=\"@+id/android_logo2\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
+           "                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+           "Warning: The resource R.id.imageView1 appears to be unused [UnusedIds]\n" +
+           "Warning: The resource R.id.include1 appears to be unused [UnusedIds]\n" +
+           "Warning: The resource R.id.linearLayout2 appears to be unused [UnusedIds]\n" +
+           "0 errors, 11 warnings\n",
+
+            lintProject(
+                // Rename .txt files to .java
+                "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
+                "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
+                "AndroidManifest.xml",
+                "res/layout/accessibility.xml"));
+    }
+
+    public void testArrayReference() throws Exception {
+        assertEquals(
+           "res/values/arrayusage.xml:3: Warning: The resource R.array.my_array appears to be unused [UnusedResources]\n" +
+           "<string-array name=\"my_array\">\n" +
+           "              ~~~~~~~~~~~~~~~\n" +
+           "0 errors, 1 warnings\n" +
+           "",
+
+            lintProject(
+                "AndroidManifest.xml",
+                "res/values/arrayusage.xml"));
+    }
+
+    public void testAttrs() throws Exception {
+        assertEquals(
+           "res/layout/customattrlayout.xml: Warning: The resource R.layout.customattrlayout appears to be unused [UnusedResources]\n" +
+           "0 errors, 1 warnings\n" +
+           "",
+
+            lintProject(
+                "res/values/customattr.xml",
+                "res/layout/customattrlayout.xml",
+                "unusedR.java.txt=>gen/my/pkg/R.java",
+                "AndroidManifest.xml"));
+    }
+
+    public void testMultiProjectIgnoreLibraries() throws Exception {
+        assertEquals(
+           "No warnings.",
+
+            lintProject(
+                // Master project
+                "multiproject/main-manifest.xml=>AndroidManifest.xml",
+                "multiproject/main.properties=>project.properties",
+                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java",
+
+                // Library project
+                "multiproject/library-manifest.xml=>../LibraryProject/AndroidManifest.xml",
+                "multiproject/library.properties=>../LibraryProject/project.properties",
+                "multiproject/LibraryCode.java.txt=>../LibraryProject/src/foo/library/LibraryCode.java",
+                "multiproject/strings.xml=>../LibraryProject/res/values/strings.xml"
+            ));
+    }
+
+    public void testMultiProject() throws Exception {
+        File master = getProjectDir("MasterProject",
+                // Master project
+                "multiproject/main-manifest.xml=>AndroidManifest.xml",
+                "multiproject/main.properties=>project.properties",
+                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+        );
+        File library = getProjectDir("LibraryProject",
+                // Library project
+                "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                "multiproject/library.properties=>project.properties",
+                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+                "multiproject/strings.xml=>res/values/strings.xml"
+        );
+        assertEquals(
+           // string1 is defined and used in the library project
+           // string2 is defined in the library project and used in the master project
+           // string3 is defined in the library project and not used anywhere
+           "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
+           "    <string name=\"string3\">String 3</string>\n" +
+           "            ~~~~~~~~~~~~~~\n" +
+           "0 errors, 1 warnings\n",
+
+           checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
+    }
+
+    public void testFqcnReference() throws Exception {
+        assertEquals(
+           "No warnings.",
+
+            lintProject(
+                "res/layout/layout1.xml=>res/layout/main.xml",
+                "src/test/pkg/UnusedReference.java.txt=>src/test/pkg/UnusedReference.java",
+                "AndroidManifest.xml"));
+    }
+
+    public void testIgnoreXmlDrawable() throws Exception {
+        assertEquals(
+           "No warnings.",
+
+            lintProject(
+                    "res/drawable/ic_menu_help.xml",
+                    "gen/my/pkg/R2.java.txt=>gen/my/pkg/R.java"
+            ));
+    }
+
+    public void testPlurals() throws Exception {
+        assertEquals(
+           "res/values/plurals.xml:3: Warning: The resource R.plurals.my_plural appears to be unused [UnusedResources]\n" +
+           "    <plurals name=\"my_plural\">\n" +
+           "             ~~~~~~~~~~~~~~~~\n" +
+           "0 errors, 1 warnings\n" +
+           "",
+
+            lintProject(
+                "res/values/strings4.xml",
+                "res/values/plurals.xml",
+                "AndroidManifest.xml"));
+    }
+
+    public void testNoMerging() throws Exception {
+        // http://code.google.com/p/android/issues/detail?id=36952
+
+        File master = getProjectDir("MasterProject",
+                // Master project
+                "multiproject/main-manifest.xml=>AndroidManifest.xml",
+                "multiproject/main.properties=>project.properties",
+                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+        );
+        File library = getProjectDir("LibraryProject",
+                // Library project
+                "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                "multiproject/library.properties=>project.properties",
+                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+                "multiproject/strings.xml=>res/values/strings.xml"
+        );
+        assertEquals(
+           // The strings are all referenced in the library project's manifest file
+           // which in this project is merged in
+           "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
+           "    <string name=\"string3\">String 3</string>\n" +
+           "            ~~~~~~~~~~~~~~\n" +
+           "0 errors, 1 warnings\n",
+
+           checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
+    }
+
+    public void testLibraryMerging() throws Exception {
+        // http://code.google.com/p/android/issues/detail?id=36952
+        File master = getProjectDir("MasterProject",
+                // Master project
+                "multiproject/main-manifest.xml=>AndroidManifest.xml",
+                "multiproject/main-merge.properties=>project.properties",
+                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+        );
+        File library = getProjectDir("LibraryProject",
+                // Library project
+                "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                "multiproject/library.properties=>project.properties",
+                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+                "multiproject/strings.xml=>res/values/strings.xml"
+        );
+        assertEquals(
+           // The strings are all referenced in the library project's manifest file
+           // which in this project is merged in
+           "No warnings.",
+
+           checkLint(Arrays.asList(master, library)));
+    }
+
+    public void testCornerCase() throws Exception {
+        // See http://code.google.com/p/projectlombok/issues/detail?id=415
+        mEnableIds = true;
+        assertEquals(
+            "No warnings.",
+
+             lintProject(
+                 "src/test/pkg/Foo.java.txt=>src/test/pkg/Foo.java",
+                 "AndroidManifest.xml"));
+    }
+
+    public void testAnalytics() throws Exception {
+        // See http://code.google.com/p/android/issues/detail?id=42565
+        mEnableIds = false;
+        assertEquals(
+                "No warnings.",
+
+                lintProject(
+                        "res/values/analytics.xml"
+        ));
+    }
+
+    public void testIntegers() throws Exception {
+        // See https://code.google.com/p/android/issues/detail?id=53995
+        mEnableIds = true;
+        assertEquals(
+                "No warnings.",
+
+                lintProject(
+                        "res/values/integers.xml",
+                        "res/anim/slide_in_out.xml"
+                ));
+    }
+
+    public void testIntegerArrays() throws Exception {
+        // See http://code.google.com/p/android/issues/detail?id=59761
+        mEnableIds = false;
+        assertEquals(
+                "No warnings.",
+                lintProject("res/values/integer_arrays.xml=>res/values/integer_arrays.xml"));
+    }
+
+    public void testUnitTestReferences() throws Exception {
+        // Make sure that we pick up references in unit tests as well
+        // Regression test for
+        // https://code.google.com/p/android/issues/detail?id=79066
+        mEnableIds = false;
+        //noinspection ClassNameDiffersFromFileName
+        assertEquals("No warnings.",
+
+                lintProject(
+                        copy("res/values/strings2.xml"),
+                        copy("res/layout/layout1.xml", "res/layout/main.xml"),
+                        copy("res/layout/layout1.xml", "res/layout/other.xml"),
+
+                        copy("src/my/pkg/Test.java.txt", "src/my/pkg/Test.java"),
+                        copy("gen/my/pkg/R.java.txt", "gen/my/pkg/R.java"),
+                        copy("AndroidManifest.xml"),
+                        copy("res/layout/accessibility.xml"),
+
+                        // Add unit test source which references resources which would otherwise
+                        // be marked as unused
+                        java("test/my/pkg/MyTest.java", ""
+                                + "package my.pkg;\n"
+                                + "class MyTest {\n"
+                                + "    public void test() {\n"
+                                + "        System.out.println(R.layout.accessibility);\n"
+                                + "        System.out.println(R.layout.main);\n"
+                                + "        System.out.println(R.layout.other);\n"
+                                + "        System.out.println(R.string.hello);\n"
+                                + "    }\n"
+                                + "}\n")
+                        ));
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appindexing_manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appindexing_manifest.xml
new file mode 100644
index 0000000..746c7c6
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appindexing_manifest.xml
@@ -0,0 +1,28 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.helloworld">
+
+    <application
+            android:allowBackup="true"
+            android:icon="@mipmap/ic_launcher"
+            android:label="@string/app_name"
+            android:theme="@style/AppTheme" >
+        <activity
+                android:name=".FullscreenActivity"
+                android:configChanges="orientation|keyboardHidden|screenSize"
+                android:label="@string/title_activity_fullscreen"
+                android:theme="@style/FullscreenTheme" >
+            <intent-filter android:label="@string/title_activity_fullscreen">
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="http"
+                      android:host="example.com"
+                      android:pathPrefix="@string/path_prefix"
+                      android:port="@string/port"/>
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+            </intent-filter>
+        </activity>
+
+        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
+    </application>
+
+</manifest>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/debuggable.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/debuggable.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/debuggable.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/debuggable.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
new file mode 100644
index 0000000..520216c
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
@@ -0,0 +1,24 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 19
+    buildToolsVersion "19.0.0"
+
+    defaultConfig {
+        minSdkVersion 7
+        targetSdkVersion 19
+        versionCode 1
+        versionName "1.0"
+    }
+}
+
+dependencies {
+    compile 'com.android.support:support-v4:18.0.0'
+    compile 'com.android.support.test:espresso:0.2'
+    compile 'com.android.support:multidex:1.0.1'
+    compile 'com.android.support:multidex-instrumentation:1.0.1'
+
+    // Suppressed:
+    //noinspection GradleCompatible
+    compile 'com.android.support:support-v4:18.0.0'
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/local.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/local.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/local2.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local2.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/local2.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local2.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/mipmap.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mipmap.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/mipmap.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mipmap.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingmin.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingmin.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingmin.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingmin.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/mock_location.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mock_location.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/mock_location.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mock_location.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/no_version.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/no_version.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.project b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.project
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.project
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.project
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.cfg b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.cfg
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.cfg
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.cfg
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.pro b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.pro
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.pro
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.pro
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties1 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties1
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties1
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties1
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties19 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties19
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties19
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties19
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties2 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties2
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties2
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties2
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties3 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties3
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties3
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties3
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties4 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties4
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties4
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties4
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_strings.xml
new file mode 100644
index 0000000..677e98a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_strings.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2015 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>
+    <string name="path_prefix">/pathprefix</string>
+    <string name="port">8080</string>
+</resources>
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_wrong_strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_wrong_strings.xml
new file mode 100644
index 0000000..551c16c
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_wrong_strings.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2015 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>
+    <string name="path_prefix">pathprefix</string>
+    <string name="port">gizmos</string>
+</resources>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
new file mode 100644
index 0000000..133efa8
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
@@ -0,0 +1,19 @@
+package android.support.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+@Retention(CLASS)
+@Target({CONSTRUCTOR,METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
+public @interface IntRange {
+    long from() default Long.MIN_VALUE;
+    long to() default Long.MAX_VALUE;
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
new file mode 100644
index 0000000..8d73186
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
@@ -0,0 +1,125 @@
+import android.content.res.Resources;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.support.annotation.StyleRes;
+
+import java.util.Random;
+
+@SuppressWarnings("UnusedDeclaration")
+public class Flow {
+    public void testLiterals(Resources resources) {
+        resources.getDrawable(0); // OK
+        resources.getDrawable(-1); // OK
+        resources.getDrawable(10); // ERROR
+    }
+
+    public void testConstants(Resources resources) {
+        resources.getDrawable(R.drawable.my_drawable); // OK
+        resources.getDrawable(R.string.my_string); // ERROR
+    }
+
+    public void testLocalAnnotation() {
+        myMethod(R.string.my_string, null); // ERROR
+    }
+
+    private void myMethod(@DrawableRes int arg, Resources resources) {
+        resources.getDrawable(R.string.my_string); // ERROR
+    }
+
+    private void testAnyRes() {
+        myAnyResMethod(R.drawable.my_drawable); // OK
+        myAnyResMethod(R.string.my_string); // OK
+        myAnyResMethod(50); // ERROR
+    }
+
+    private void myAnyResMethod(@android.support.annotation.AnyRes int arg) {
+    }
+
+    public void testFields(String fileExt, Resources resources) {
+        int mimeIconId = MimeTypes.styleAndDrawable;
+        resources.getDrawable(mimeIconId); // OK
+
+        int s1 = MimeTypes.style;
+        resources.getDrawable(s1); // ERROR
+        int s2 = MimeTypes.styleAndDrawable;
+        resources.getDrawable(s2); // OK
+        int w3 = MimeTypes.drawable;
+        resources.getDrawable(w3); // OK
+
+        // Direct reference
+        resources.getDrawable(MimeTypes.style); // ERROR
+        resources.getDrawable(MimeTypes.styleAndDrawable); // OK
+        resources.getDrawable(MimeTypes.drawable); // OK
+    }
+
+    public void testCalls(String fileExt, Resources resources) {
+        int mimeIconId = MimeTypes.getIconForExt(fileExt);
+        resources.getDrawable(mimeIconId); // OK
+        resources.getDrawable(MimeTypes.getInferredString()); // OK (wrong but can't infer type)
+        resources.getDrawable(MimeTypes.getInferredDrawable()); // OK
+        resources.getDrawable(MimeTypes.getAnnotatedString()); // Error
+        resources.getDrawable(MimeTypes.getAnnotatedDrawable()); // OK
+        resources.getDrawable(MimeTypes.getUnknownType()); // OK (unknown/uncertain)
+    }
+
+    public void testFlow() {
+        int x = R.string.my_string;
+        int z = x;
+        myMethod(z, null); // ERROR
+
+        int w = MY_RESOURCE;
+        myMethod(w, null); // ERROR
+    }
+
+    private static final int MY_RESOURCE = R.string.my_string;
+
+    private static class MimeTypes {
+        @android.support.annotation.StyleRes
+        @android.support.annotation.DrawableRes
+        public static int styleAndDrawable;
+
+        @android.support.annotation.StyleRes
+        public static int style;
+
+        @android.support.annotation.DrawableRes
+        public static int drawable;
+
+        @android.support.annotation.DrawableRes
+        public static int getIconForExt(String ext) {
+            return R.drawable.my_drawable;
+        }
+
+        public static int getInferredString() {
+            // Implied string - can we handle this?
+            return R.string.my_string;
+        }
+
+        public static int getInferredDrawable() {
+            // Implied drawable - can we handle this?
+            return R.drawable.my_drawable;
+        }
+
+        @android.support.annotation.StringRes
+        public static int getAnnotatedString() {
+            return R.string.my_string;
+        }
+
+        @android.support.annotation.DrawableRes
+        public static int getAnnotatedDrawable() {
+            return R.drawable.my_drawable;
+        }
+
+        public static int getUnknownType() {
+            return new Random(1000).nextInt();
+        }
+    }
+
+    public static final class R {
+        public static final class drawable {
+            public static final int my_drawable =0x7f020057;
+        }
+        public static final class string {
+            public static final int my_string =0x7f0a000e;
+        }
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
new file mode 100644
index 0000000..40bf5df
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
@@ -0,0 +1,160 @@
+package test.pkg;
+
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntRange;
+import android.support.annotation.Size;
+
+@SuppressWarnings("UnusedDeclaration")
+public class RangeTest {
+    public void printExact(@Size(5) String arg) { System.out.println(arg); }
+    public void printMin(@Size(min=5) String arg) { }
+    public void printMax(@Size(max=8) String arg) { }
+    public void printRange(@Size(min=4,max=6) String arg) { }
+    public void printExact(@Size(5) int[] arg) { }
+    public void printMin(@Size(min=5) int[] arg) { }
+    public void printMax(@Size(max=8) int[] arg) { }
+    public void printRange(@Size(min=4,max=6) int[] arg) { }
+    public void printMultiple(@Size(multiple=3) int[] arg) { }
+    public void printMinMultiple(@Size(min=4,multiple=3) int[] arg) { }
+    public void printAtLeast(@IntRange(from=4) int arg) { }
+    public void printAtMost(@IntRange(to=7) int arg) { }
+    public void printBetween(@IntRange(from=4,to=7) int arg) { }
+    public void printAtLeastInclusive(@FloatRange(from=2.5) float arg) { }
+    public void printAtLeastExclusive(@FloatRange(from=2.5,fromInclusive=false) float arg) { }
+    public void printAtMostInclusive(@FloatRange(to=7) double arg) { }
+    public void printAtMostExclusive(@FloatRange(to=7,toInclusive=false) double arg) { }
+    public void printBetweenFromInclusiveToInclusive(@FloatRange(from=2.5,to=5.0) float arg) { }
+    public void printBetweenFromExclusiveToInclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false) float arg) { }
+    public void printBetweenFromInclusiveToExclusive(@FloatRange(from=2.5,to=5.0,toInclusive=false) float arg) { }
+    public void printBetweenFromExclusiveToExclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false,toInclusive=false) float arg) { }
+
+    public void testLength() {
+        printExact("1234"); // ERROR
+        printExact("12345"); // OK
+        printExact("123456"); // ERROR
+
+        printMin("1234"); // ERROR
+        printMin("12345"); // OK
+        printMin("123456"); // OK
+
+        printMax("123456"); // OK
+        printMax("1234567"); // OK
+        printMax("12345678"); // OK
+        printMax("123456789"); // ERROR
+
+        printRange("123"); // ERROR
+        printRange("1234"); // OK
+        printRange("12345"); // OK
+        printRange("123456"); // OK
+        printRange("1234567"); // ERROR
+    }
+
+    public void testSize() {
+        printExact(new int[]{1, 2, 3, 4}); // ERROR
+        printExact(new int[]{1, 2, 3, 4, 5}); // OK
+        printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR
+
+        printMin(new int[]{1, 2, 3, 4}); // ERROR
+        printMin(new int[]{1, 2, 3, 4, 5}); // OK
+        printMin(new int[]{1, 2, 3, 4, 5, 6}); // OK
+
+        printMax(new int[]{1, 2, 3, 4, 5, 6}); // OK
+        printMax(new int[]{1, 2, 3, 4, 5, 6, 7}); // OK
+        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
+        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
+        printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR
+
+        printRange(new int[] {1,2,3}); // ERROR
+        printRange(new int[] {1,2,3,4}); // OK
+        printRange(new int[] {1,2,3,4,5}); // OK
+        printRange(new int[] {1,2,3,4,5,6}); // OK
+        printRange(new int[] {1,2,3,4,5,6,7}); // ERROR
+
+        printMultiple(new int[] {1,2,3}); // OK
+        printMultiple(new int[] {1,2,3,4}); // ERROR
+        printMultiple(new int[] {1,2,3,4,5}); // ERROR
+        printMultiple(new int[] {1,2,3,4,5,6}); // OK
+        printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR
+
+        printMinMultiple(new int[] {1,2,3,4,5,6}); // OK
+        printMinMultiple(new int[]{1, 2, 3}); // ERROR
+    }
+
+    public void testIntRange() {
+        printAtLeast(3); // ERROR
+        printAtLeast(4); // OK
+        printAtLeast(5); // OK
+
+        printAtMost(5); // OK
+        printAtMost(6); // OK
+        printAtMost(7); // OK
+        printAtMost(8); // ERROR
+
+        printBetween(3); // ERROR
+        printBetween(4); // OK
+        printBetween(5); // OK
+        printBetween(6); // OK
+        printBetween(7); // OK
+        printBetween(8); // ERROR
+    }
+
+    public void testFloatRange() {
+        printAtLeastInclusive(2.49f); // ERROR
+        printAtLeastInclusive(2.5f); // OK
+        printAtLeastInclusive(2.6f); // OK
+
+        printAtLeastExclusive(2.49f); // ERROR
+        printAtLeastExclusive(2.5f); // ERROR
+        printAtLeastExclusive(2.501f); // OK
+
+        printAtMostInclusive(6.8f); // OK
+        printAtMostInclusive(6.9f); // OK
+        printAtMostInclusive(7.0f); // OK
+        printAtMostInclusive(7.1f); // ERROR
+
+        printAtMostExclusive(6.9f); // OK
+        printAtMostExclusive(6.99f); // OK
+        printAtMostExclusive(7.0f); // ERROR
+        printAtMostExclusive(7.1f); // ERROR
+
+        printBetweenFromInclusiveToInclusive(2.4f); // ERROR
+        printBetweenFromInclusiveToInclusive(2.5f); // OK
+        printBetweenFromInclusiveToInclusive(3f); // OK
+        printBetweenFromInclusiveToInclusive(5.0f); // OK
+        printBetweenFromInclusiveToInclusive(5.1f); // ERROR
+
+        printBetweenFromExclusiveToInclusive(2.4f); // ERROR
+        printBetweenFromExclusiveToInclusive(2.5f); // ERROR
+        printBetweenFromExclusiveToInclusive(5.0f); // OK
+        printBetweenFromExclusiveToInclusive(5.1f); // ERROR
+
+        printBetweenFromInclusiveToExclusive(2.4f); // ERROR
+        printBetweenFromInclusiveToExclusive(2.5f); // OK
+        printBetweenFromInclusiveToExclusive(3f); // OK
+        printBetweenFromInclusiveToExclusive(4.99f); // OK
+        printBetweenFromInclusiveToExclusive(5.0f); // ERROR
+
+        printBetweenFromExclusiveToExclusive(2.4f); // ERROR
+        printBetweenFromExclusiveToExclusive(2.5f); // ERROR
+        printBetweenFromExclusiveToExclusive(2.51f); // OK
+        printBetweenFromExclusiveToExclusive(4.99f); // OK
+        printBetweenFromExclusiveToExclusive(5.0f); // ERROR
+    }
+
+    public void testNegative() {
+        printBetween(-7); // ERROR
+        printAtLeastExclusive(-10.0f); // ERROR
+    }
+
+    public static final int MINIMUM = -1;
+    public static final int MAXIMUM = 42;
+    public void printIndirect(@IntRange(from = MINIMUM, to = MAXIMUM) int arg) { }
+    public static final int SIZE = 5;
+    public static void printIndirectSize(@Size(SIZE) String foo) { }
+
+    public void testIndirect() {
+        printIndirect(-2); // ERROR
+        printIndirect(43); // ERROR
+        printIndirectSize("1234567"); // ERROR
+    }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/LintClientTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintClientTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/LintClientTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintClientTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/ProjectTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/ProjectTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/IssueTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/IssueTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/IssueTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/IssueTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
new file mode 100644
index 0000000..6956445
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
@@ -0,0 +1,523 @@
+/*
+ * 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.tools.lint.detector.api;
+
+import static com.android.tools.lint.detector.api.LintUtils.computeResourceName;
+import static com.android.tools.lint.detector.api.LintUtils.convertVersion;
+import static com.android.tools.lint.detector.api.LintUtils.escapePropertyValue;
+import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
+import static com.android.tools.lint.detector.api.LintUtils.getFormattedParameters;
+import static com.android.tools.lint.detector.api.LintUtils.getLocaleAndRegion;
+import static com.android.tools.lint.detector.api.LintUtils.isImported;
+import static com.android.tools.lint.detector.api.LintUtils.splitPath;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ApiVersion;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.android.tools.lint.EcjParser;
+import com.android.tools.lint.LintCliClient;
+import com.android.tools.lint.checks.BuiltinIssueRegistry;
+import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.LintDriver;
+import com.google.common.collect.Iterables;
+
+import junit.framework.TestCase;
+
+import org.intellij.lang.annotations.Language;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+
+import lombok.ast.Node;
+
+@SuppressWarnings("javadoc")
+public class LintUtilsTest extends TestCase {
+    public void testPrintList() throws Exception {
+        assertEquals("foo, bar, baz",
+                LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 3));
+        assertEquals("foo, bar, baz",
+                LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 5));
+
+        assertEquals("foo, bar, baz... (3 more)",
+                LintUtils.formatList(
+                        Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3));
+        assertEquals("foo... (5 more)",
+                LintUtils.formatList(
+                        Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1));
+        assertEquals("foo, bar, baz",
+                LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 0));
+    }
+
+    public void testEndsWith() throws Exception {
+        assertTrue(LintUtils.endsWith("Foo", ""));
+        assertTrue(LintUtils.endsWith("Foo", "o"));
+        assertTrue(LintUtils.endsWith("Foo", "oo"));
+        assertTrue(LintUtils.endsWith("Foo", "Foo"));
+        assertTrue(LintUtils.endsWith("Foo", "FOO"));
+        assertTrue(LintUtils.endsWith("Foo", "fOO"));
+
+        assertFalse(LintUtils.endsWith("Foo", "f"));
+    }
+
+    public void testStartsWith() throws Exception {
+        assertTrue(LintUtils.startsWith("FooBar", "Bar", 3));
+        assertTrue(LintUtils.startsWith("FooBar", "BAR", 3));
+        assertTrue(LintUtils.startsWith("FooBar", "Foo", 0));
+        assertFalse(LintUtils.startsWith("FooBar", "Foo", 2));
+    }
+
+    public void testIsXmlFile() throws Exception {
+        assertTrue(LintUtils.isXmlFile(new File("foo.xml")));
+        assertTrue(LintUtils.isXmlFile(new File("foo.Xml")));
+        assertTrue(LintUtils.isXmlFile(new File("foo.XML")));
+
+        assertFalse(LintUtils.isXmlFile(new File("foo.png")));
+        assertFalse(LintUtils.isXmlFile(new File("xml")));
+        assertFalse(LintUtils.isXmlFile(new File("xml.png")));
+    }
+
+    public void testGetBasename() throws Exception {
+        assertEquals("foo", LintUtils.getBaseName("foo.png"));
+        assertEquals("foo", LintUtils.getBaseName("foo.9.png"));
+        assertEquals(".foo", LintUtils.getBaseName(".foo"));
+    }
+
+    public void testEditDistance() {
+        assertEquals(0, LintUtils.editDistance("kitten", "kitten"));
+
+        // editing kitten to sitting has edit distance 3:
+        //   replace k with s
+        //   replace e with i
+        //   append g
+        assertEquals(3, LintUtils.editDistance("kitten", "sitting"));
+
+        assertEquals(3, LintUtils.editDistance("saturday", "sunday"));
+        assertEquals(1, LintUtils.editDistance("button", "bitton"));
+        assertEquals(6, LintUtils.editDistance("radiobutton", "bitton"));
+    }
+
+    public void testSplitPath() throws Exception {
+        assertTrue(Arrays.equals(new String[] { "/foo", "/bar", "/baz" },
+                Iterables.toArray(splitPath("/foo:/bar:/baz"), String.class)));
+
+        assertTrue(Arrays.equals(new String[] { "/foo", "/bar" },
+                Iterables.toArray(splitPath("/foo;/bar"), String.class)));
+
+        assertTrue(Arrays.equals(new String[] { "/foo", "/bar:baz" },
+                Iterables.toArray(splitPath("/foo;/bar:baz"), String.class)));
+
+        assertTrue(Arrays.equals(new String[] { "\\foo\\bar", "\\bar\\foo" },
+                Iterables.toArray(splitPath("\\foo\\bar;\\bar\\foo"), String.class)));
+
+        assertTrue(Arrays.equals(new String[] { "${sdk.dir}\\foo\\bar", "\\bar\\foo" },
+                Iterables.toArray(splitPath("${sdk.dir}\\foo\\bar;\\bar\\foo"),
+                        String.class)));
+
+        assertTrue(Arrays.equals(new String[] { "${sdk.dir}/foo/bar", "/bar/foo" },
+                Iterables.toArray(splitPath("${sdk.dir}/foo/bar:/bar/foo"),
+                        String.class)));
+
+        assertTrue(Arrays.equals(new String[] { "C:\\foo", "/bar" },
+                Iterables.toArray(splitPath("C:\\foo:/bar"), String.class)));
+    }
+
+    public void testCommonParen1() {
+        assertEquals(new File("/a"), (LintUtils.getCommonParent(
+                new File("/a/b/c/d/e"), new File("/a/c"))));
+        assertEquals(new File("/a"), (LintUtils.getCommonParent(
+                new File("/a/c"), new File("/a/b/c/d/e"))));
+
+        assertEquals(new File("/"), LintUtils.getCommonParent(
+                new File("/foo/bar"), new File("/bar/baz")));
+        assertEquals(new File("/"), LintUtils.getCommonParent(
+                new File("/foo/bar"), new File("/")));
+        assertNull(LintUtils.getCommonParent(
+               new File("C:\\Program Files"), new File("F:\\")));
+        assertNull(LintUtils.getCommonParent(
+                new File("C:/Program Files"), new File("F:/")));
+
+        assertEquals(new File("/foo/bar/baz"), LintUtils.getCommonParent(
+                new File("/foo/bar/baz"), new File("/foo/bar/baz")));
+        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+                new File("/foo/bar/baz"), new File("/foo/bar")));
+        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+                new File("/foo/bar/baz"), new File("/foo/bar/foo")));
+        assertEquals(new File("/foo"), LintUtils.getCommonParent(
+                new File("/foo/bar"), new File("/foo/baz")));
+        assertEquals(new File("/foo"), LintUtils.getCommonParent(
+                new File("/foo/bar"), new File("/foo/baz")));
+        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+                new File("/foo/bar"), new File("/foo/bar/baz")));
+    }
+
+    public void testCommonParent2() {
+        assertEquals(new File("/"), LintUtils.getCommonParent(
+                Arrays.asList(new File("/foo/bar"), new File("/bar/baz"))));
+        assertEquals(new File("/"), LintUtils.getCommonParent(
+                Arrays.asList(new File("/foo/bar"), new File("/"))));
+        assertNull(LintUtils.getCommonParent(
+                Arrays.asList(new File("C:\\Program Files"), new File("F:\\"))));
+        assertNull(LintUtils.getCommonParent(
+                Arrays.asList(new File("C:/Program Files"), new File("F:/"))));
+
+        assertEquals(new File("/foo"), LintUtils.getCommonParent(
+                Arrays.asList(new File("/foo/bar"), new File("/foo/baz"))));
+        assertEquals(new File("/foo"), LintUtils.getCommonParent(
+                Arrays.asList(new File("/foo/bar"), new File("/foo/baz"),
+                        new File("/foo/baz/f"))));
+        assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+                Arrays.asList(new File("/foo/bar"), new File("/foo/bar/baz"),
+                        new File("/foo/bar/foo2/foo3"))));
+    }
+
+    public void testStripIdPrefix() throws Exception {
+        assertEquals("foo", LintUtils.stripIdPrefix("@+id/foo"));
+        assertEquals("foo", LintUtils.stripIdPrefix("@id/foo"));
+        assertEquals("foo", LintUtils.stripIdPrefix("foo"));
+    }
+
+    public void testIdReferencesMatch() throws Exception {
+        assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo"));
+        assertTrue(LintUtils.idReferencesMatch("@id/foo", "@id/foo"));
+        assertTrue(LintUtils.idReferencesMatch("@id/foo", "@+id/foo"));
+        assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@id/foo"));
+
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
+        assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/bar"));
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/bar"));
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
+
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo1"));
+        assertFalse(LintUtils.idReferencesMatch("@id/foo", "@id/foo1"));
+        assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/foo1"));
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/foo1"));
+
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@+id/foo"));
+        assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@id/foo"));
+        assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@+id/foo"));
+        assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@id/foo"));
+    }
+
+    private static void checkEncoding(String encoding, boolean writeBom, String lineEnding)
+            throws Exception {
+        @SuppressWarnings("StringBufferReplaceableByString")
+        StringBuilder sb = new StringBuilder();
+
+        // Norwegian extra vowel characters such as "latin small letter a with ring above"
+        String value = "\u00e6\u00d8\u00e5";
+        String expected = "First line." + lineEnding + "Second line." + lineEnding
+                + "Third line." + lineEnding + value + lineEnding;
+        sb.append(expected);
+        File file = File.createTempFile("getEncodingTest" + encoding + writeBom, ".txt");
+        file.deleteOnExit();
+        BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
+        OutputStreamWriter writer = new OutputStreamWriter(stream, encoding);
+
+        if (writeBom) {
+            String normalized = encoding.toLowerCase(Locale.US).replace("-", "_");
+            if (normalized.equals("utf_8")) {
+                stream.write(0xef);
+                stream.write(0xbb);
+                stream.write(0xbf);
+            } else if (normalized.equals("utf_16")) {
+                stream.write(0xfe);
+                stream.write(0xff);
+            } else if (normalized.equals("utf_16le")) {
+                stream.write(0xff);
+                stream.write(0xfe);
+            } else if (normalized.equals("utf_32")) {
+                stream.write(0x0);
+                stream.write(0x0);
+                stream.write(0xfe);
+                stream.write(0xff);
+            } else if (normalized.equals("utf_32le")) {
+                stream.write(0xff);
+                stream.write(0xfe);
+                stream.write(0x0);
+                stream.write(0x0);
+            } else {
+                fail("Can't write BOM for encoding " + encoding);
+            }
+        }
+        writer.write(sb.toString());
+        writer.close();
+
+        String s = LintUtils.getEncodedString(new LintCliClient(), file);
+        assertEquals(expected, s);
+    }
+
+    public void testGetEncodedString() throws Exception {
+        checkEncoding("utf-8", false /*bom*/, "\n");
+        checkEncoding("UTF-8", false /*bom*/, "\n");
+        checkEncoding("UTF_16", false /*bom*/, "\n");
+        checkEncoding("UTF-16", false /*bom*/, "\n");
+        checkEncoding("UTF_16LE", false /*bom*/, "\n");
+
+        // Try BOM's
+        checkEncoding("utf-8", true /*bom*/, "\n");
+        checkEncoding("UTF-8", true /*bom*/, "\n");
+        checkEncoding("UTF_16", true /*bom*/, "\n");
+        checkEncoding("UTF-16", true /*bom*/, "\n");
+        checkEncoding("UTF_16LE", true /*bom*/, "\n");
+        checkEncoding("UTF_32", true /*bom*/, "\n");
+        checkEncoding("UTF_32LE", true /*bom*/, "\n");
+
+        // Make sure this works for \r and \r\n as well
+        checkEncoding("UTF-16", false /*bom*/, "\r");
+        checkEncoding("UTF_16LE", false /*bom*/, "\r");
+        checkEncoding("UTF-16", false /*bom*/, "\r\n");
+        checkEncoding("UTF_16LE", false /*bom*/, "\r\n");
+        checkEncoding("UTF-16", true /*bom*/, "\r");
+        checkEncoding("UTF_16LE", true /*bom*/, "\r");
+        checkEncoding("UTF_32", true /*bom*/, "\r");
+        checkEncoding("UTF_32LE", true /*bom*/, "\r");
+        checkEncoding("UTF-16", true /*bom*/, "\r\n");
+        checkEncoding("UTF_16LE", true /*bom*/, "\r\n");
+        checkEncoding("UTF_32", true /*bom*/, "\r\n");
+        checkEncoding("UTF_32LE", true /*bom*/, "\r\n");
+    }
+
+    public void testGetLocaleAndRegion() throws Exception {
+        assertNull(getLocaleAndRegion(""));
+        assertNull(getLocaleAndRegion("values"));
+        assertNull(getLocaleAndRegion("values-xlarge-port"));
+        assertEquals("en", getLocaleAndRegion("values-en"));
+        assertEquals("pt-rPT", getLocaleAndRegion("values-pt-rPT-nokeys"));
+        assertEquals("b+pt+PT", getLocaleAndRegion("values-b+pt+PT-nokeys"));
+        assertEquals("zh-rCN", getLocaleAndRegion("values-zh-rCN-keyshidden"));
+        assertEquals("ms", getLocaleAndRegion("values-ms-keyshidden"));
+    }
+
+    public void testIsImported() throws Exception {
+        assertFalse(isImported(getCompilationUnit(
+                "package foo.bar;\n" +
+                "class Foo {\n" +
+                "}\n"),
+                "android.app.Activity"));
+
+        assertTrue(isImported(getCompilationUnit(
+                "package foo.bar;\n" +
+                "import foo.bar.*;\n" +
+                "import android.app.Activity;\n" +
+                "import foo.bar.Baz;\n" +
+                "class Foo {\n" +
+                "}\n"),
+                "android.app.Activity"));
+
+        assertTrue(isImported(getCompilationUnit(
+                "package foo.bar;\n" +
+                "import android.app.Activity;\n" +
+                "class Foo {\n" +
+                "}\n"),
+                "android.app.Activity"));
+
+        assertTrue(isImported(getCompilationUnit(
+                "package foo.bar;\n" +
+                "import android.app.*;\n" +
+                "class Foo {\n" +
+                "}\n"),
+                "android.app.Activity"));
+
+        assertFalse(isImported(getCompilationUnit(
+                "package foo.bar;\n" +
+                "import android.app.*;\n" +
+                "import foo.bar.Activity;\n" +
+                "class Foo {\n" +
+                "}\n"),
+                "android.app.Activity"));
+    }
+
+    public void testComputeResourceName() {
+        assertEquals("", computeResourceName("", ""));
+        assertEquals("foo", computeResourceName("", "foo"));
+        assertEquals("foo", computeResourceName("foo", ""));
+        assertEquals("prefix_name", computeResourceName("prefix_", "name"));
+        assertEquals("prefixName", computeResourceName("prefix", "name"));
+    }
+
+    public static Node getCompilationUnit(@Language("JAVA") String javaSource) {
+        return getCompilationUnit(javaSource, new File("test"));
+    }
+
+
+    public static Node getCompilationUnit(@Language("JAVA") String javaSource, File relativePath) {
+        JavaContext context = parse(javaSource, relativePath);
+        return context.getCompilationUnit();
+    }
+
+    public static JavaContext parse(@Language("JAVA") final String javaSource,
+            final File relativePath) {
+        File dir = new File("projectDir");
+        final File fullPath = new File(dir, relativePath.getPath());
+        LintCliClient client = new LintCliClient() {
+            @NonNull
+            @Override
+            public String readFile(@NonNull File file) {
+                if (file.getPath().equals(fullPath.getPath())) {
+                    return javaSource;
+                }
+                return super.readFile(file);
+            }
+
+            @Nullable
+            @Override
+            public IAndroidTarget getCompileTarget(@NonNull Project project) {
+                IAndroidTarget[] targets = getTargets();
+                for (int i = targets.length - 1; i >= 0; i--) {
+                    IAndroidTarget target = targets[i];
+                    if (target.isPlatform()) {
+                        return target;
+                    }
+                }
+
+                return super.getCompileTarget(project);
+            }
+        };
+        Project project = client.getProject(dir, dir);
+
+        LintDriver driver = new LintDriver(new BuiltinIssueRegistry(),
+                new LintCliClient());
+        driver.setScope(Scope.JAVA_FILE_SCOPE);
+        TestContext context = new TestContext(driver, client, project, javaSource, fullPath);
+        JavaParser parser = new EcjParser(client, project);
+        parser.prepareJavaParse(Collections.<JavaContext>singletonList(context));
+        Node compilationUnit = parser.parseJava(context);
+        assertNotNull(javaSource, compilationUnit);
+        context.setCompilationUnit(compilationUnit);
+        return context;
+    }
+
+    public void testConvertVersion() {
+        assertEquals(new AndroidVersion(5, null), convertVersion(new DefaultApiVersion(5, null),
+                null));
+        assertEquals(new AndroidVersion(19, null), convertVersion(new DefaultApiVersion(19, null),
+                null));
+        //noinspection SpellCheckingInspection
+        assertEquals(new AndroidVersion(18, "KITKAT"), // a preview platform API level is not final
+                convertVersion(new DefaultApiVersion(0, "KITKAT"),
+                null));
+    }
+
+    public void testIsModelOlderThan() throws Exception {
+        AndroidProject project = mock(AndroidProject.class);
+        when(project.getModelVersion()).thenReturn("0.10.4");
+
+        assertTrue(LintUtils.isModelOlderThan(project, 0, 10, 5));
+        assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 0));
+        assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 4));
+        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
+
+        project = mock(AndroidProject.class);
+        when(project.getModelVersion()).thenReturn("0.11.0");
+
+        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
+        assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
+        assertFalse(LintUtils.isModelOlderThan(project, 0, 10, 4));
+
+        project = mock(AndroidProject.class);
+        when(project.getModelVersion()).thenReturn("0.11.5");
+
+        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
+        assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
+
+        project = mock(AndroidProject.class);
+        when(project.getModelVersion()).thenReturn("1.0.0");
+
+        assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 1));
+        assertFalse(LintUtils.isModelOlderThan(project, 1, 0, 0));
+        assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
+    }
+
+    private static final class DefaultApiVersion implements ApiVersion {
+        private final int mApiLevel;
+        private final String mCodename;
+
+        public DefaultApiVersion(int apiLevel, @Nullable String codename) {
+            mApiLevel = apiLevel;
+            mCodename = codename;
+        }
+
+        @Override
+        public int getApiLevel() {
+            return mApiLevel;
+        }
+
+        @Nullable
+        @Override
+        public String getCodename() {
+            return mCodename;
+        }
+
+        @NonNull
+        @Override
+        public String getApiString() {
+            fail("Not needed in this test");
+            return "<invalid>";
+        }
+    }
+
+    public void testFindSubstring() {
+       assertEquals("foo", findSubstring("foo", null, null));
+       assertEquals("foo", findSubstring("foo  ", null, "  "));
+       assertEquals("foo", findSubstring("  foo", "  ", null));
+       assertEquals("foo", findSubstring("[foo]", "[", "]"));
+    }
+
+    public void testGetFormattedParameters() {
+        assertEquals(Arrays.asList("foo","bar"),
+                getFormattedParameters("Prefix %1$s Divider %2$s Suffix",
+                        "Prefix foo Divider bar Suffix"));
+    }
+
+    public void testEscapePropertyValue() throws Exception {
+        assertEquals("foo", escapePropertyValue("foo"));
+        assertEquals("\\  foo  ", escapePropertyValue("  foo  "));
+        assertEquals("c\\:/foo/bar", escapePropertyValue("c:/foo/bar"));
+        assertEquals("\\!\\#\\:\\\\a\\\\b\\\\c", escapePropertyValue("!#:\\a\\b\\c"));
+        assertEquals(
+                "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo\\#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo",
+                escapePropertyValue("foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo"));
+    }
+
+    private static class TestContext extends JavaContext {
+        private final String mJavaSource;
+        public TestContext(LintDriver driver, LintCliClient client, Project project,
+                String javaSource, File file) {
+            //noinspection ConstantConditions
+            super(driver, project,
+                    null, file, client.getJavaParser(null));
+
+            mJavaSource = javaSource;
+        }
+
+        @Override
+        @Nullable
+        public String getContents() {
+            return mJavaSource;
+        }
+    }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/LocationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
new file mode 100644
index 0000000..1984c0a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 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.tools.lint.detector.api;
+
+import static com.android.SdkConstants.AUTO_URI;
+import static com.android.tools.lint.detector.api.TextFormat.HTML;
+import static com.android.tools.lint.detector.api.TextFormat.RAW;
+import static com.android.tools.lint.detector.api.TextFormat.TEXT;
+
+import junit.framework.TestCase;
+
+public class TextFormatTest extends TestCase {
+    private static String convertMarkup(String raw, TextFormat to) {
+        return RAW.convertTo(raw, to);
+    }
+
+    public void testConvertMarkup() throws Exception {
+        assertEquals("", convertMarkup("", HTML));
+
+        // Normal escapes
+        assertEquals("foo bar", convertMarkup("foo bar", HTML));
+        assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
+        assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
+        assertEquals("&lt;&amp;>'\"", convertMarkup("<&>'\"", HTML));
+
+        // HTML Formatting
+        assertEquals("<code>@TargetApi(11)</code>, ", convertMarkup("`@TargetApi(11)`, ",
+                HTML));
+        assertEquals("with <code>getArguments()</code>.",
+                convertMarkup("with `getArguments()`.", HTML));
+        assertEquals("(<code>dip</code>)", convertMarkup("(`dip`)", HTML));
+        assertEquals(" <code>0dp</code> ", convertMarkup(" `0dp` ", HTML));
+        assertEquals(
+            "resources under <code>$ANDROID_SK/platforms/android-$VERSION/data/res/.</code>",
+                convertMarkup(
+                        "resources under `$ANDROID_SK/platforms/android-$VERSION/data/res/.`",
+                        HTML));
+        assertEquals("wrong format. Instead of <code>-keepclasseswithmembernames</code> use ",
+                convertMarkup("wrong format. Instead of `-keepclasseswithmembernames` use ",
+                        HTML));
+        assertEquals("<code>exported=false</code>)", convertMarkup("`exported=false`)",
+                HTML));
+        assertEquals("by setting <code>inputType=\"text\"</code>.",
+                convertMarkup("by setting `inputType=\"text\"`.", HTML));
+        assertEquals("* <code>View(Context context)</code><br/>\n",
+                convertMarkup("* `View(Context context)`\n", HTML));
+        assertEquals("The <code>@+id/</code> syntax", convertMarkup("The `@+id/` syntax",
+                HTML));
+        assertEquals("", convertMarkup("", HTML));
+        assertEquals("", convertMarkup("", HTML));
+        assertEquals("This is <b>bold</b>", convertMarkup("This is *bold*", HTML));
+        assertEquals("Visit <a href=\"http://google.com\">http://google.com</a>.",
+                convertMarkup("Visit http://google.com.", HTML));
+        assertEquals("This is <code>monospace</code>!", convertMarkup("This is `monospace`!",
+                HTML));
+        assertEquals(
+                "See <a href=\"http://developer.android.com/reference/android/view/" +
+                "WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON\">http://developer." +
+                "android.com/reference/android/view/WindowManager.LayoutParams.html#" +
+                "FLAG_KEEP_SCREEN_ON</a>.",
+            convertMarkup(
+              "See http://developer.android.com/reference/android/view/WindowManager.Layout" +
+              "Params.html#FLAG_KEEP_SCREEN_ON.", HTML));
+
+        // Text formatting
+        assertEquals("@TargetApi(11), ", convertMarkup("`@TargetApi(11)`, ", TEXT));
+        assertEquals("with getArguments().", convertMarkup("with `getArguments()`.", TEXT));
+        assertEquals("bold", convertMarkup("*bold*", TEXT));
+        assertEquals("Visit http://google.com.", convertMarkup("Visit http://google.com.",
+                TEXT));
+
+        // Corners (match at the beginning and end)
+        assertEquals("<b>bold</b>", convertMarkup("*bold*", HTML));
+        assertEquals("<code>monospace</code>!", convertMarkup("`monospace`!", HTML));
+
+        // Not formatting
+        assertEquals("a*b", convertMarkup("a*b", HTML));
+        assertEquals("a* b*", convertMarkup("a* b*", HTML));
+        assertEquals("*a *b", convertMarkup("*a *b", HTML));
+        assertEquals("Prefix is http:// ", convertMarkup("Prefix is http:// ", HTML));
+        assertEquals("", convertMarkup("", HTML));
+        assertEquals("", convertMarkup("", HTML));
+        assertEquals("", convertMarkup("", HTML));
+        assertEquals("", convertMarkup("", HTML));
+        assertEquals("This is * not * bold", convertMarkup("This is * not * bold", HTML));
+        assertEquals("* List item 1<br/>\n* List Item 2",
+                convertMarkup("* List item 1\n* List Item 2", HTML));
+        assertEquals("myhttp://foo.bar", convertMarkup("myhttp://foo.bar", HTML));
+    }
+
+    public void testConvertMarkup2() throws Exception {
+        // http at the end:
+        // Explanation from ManifestDetector#TARGET_NEWER
+        String explanation =
+            "When your application runs on a version of Android that is more recent than your " +
+            "targetSdkVersion specifies that it has been tested with, various compatibility " +
+            "modes kick in. This ensures that your application continues to work, but it may " +
+            "look out of place. For example, if the targetSdkVersion is less than 14, your " +
+            "app may get an option button in the UI.\n" +
+            "\n" +
+            "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
+            "test your app to make sure everything works correctly. You may want to consult " +
+            "the compatibility notes to see what changes apply to each version you are adding " +
+            "support for: " +
+            "http://developer.android.com/reference/android/os/Build.VERSION_CODES.html";
+
+        assertEquals(
+            "When your application runs on a version of Android that is more recent than your " +
+            "targetSdkVersion specifies that it has been tested with, various compatibility " +
+            "modes kick in. This ensures that your application continues to work, but it may " +
+            "look out of place. For example, if the targetSdkVersion is less than 14, your " +
+            "app may get an option button in the UI.<br/>\n" +
+            "<br/>\n" +
+            "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
+            "test your app to make sure everything works correctly. You may want to consult " +
+            "the compatibility notes to see what changes apply to each version you are adding " +
+            "support for: " +
+            "<a href=\"http://developer.android.com/reference/android/os/Build.VERSION_CODES." +
+            "html\">http://developer.android.com/reference/android/os/Build.VERSION_CODES.html" +
+            "</a>",
+            convertMarkup(explanation, HTML));
+    }
+
+    public void testConvertMarkup3() throws Exception {
+        // embedded http markup test
+        // Explanation from NamespaceDetector#CUSTOMVIEW
+        String explanation =
+            "When using a custom view with custom attributes in a library project, the layout " +
+            "must use the special namespace " + AUTO_URI + " instead of a URI which includes " +
+            "the library project's own package. This will be used to automatically adjust the " +
+            "namespace of the attributes when the library resources are merged into the " +
+            "application project.";
+        assertEquals(
+            "When using a custom view with custom attributes in a library project, the layout " +
+            "must use the special namespace " +
+            "<a href=\"http://schemas.android.com/apk/res-auto\">" +
+            "http://schemas.android.com/apk/res-auto</a> " +
+            "instead of a URI which includes the library project's own package. " +
+            "This will be used to automatically adjust the namespace of the attributes when " +
+            "the library resources are merged into the application project.",
+            convertMarkup(explanation, HTML));
+    }
+
+    public void testConvertMarkup4() throws Exception {
+        // monospace test
+        String explanation =
+            "The manifest should contain a `<uses-sdk>` element which defines the " +
+            "minimum minimum API Level required for the application to run, " +
+            "as well as the target version (the highest API level you have tested " +
+            "the version for.)";
+
+        assertEquals(
+            "The manifest should contain a <code>&lt;uses-sdk></code> element which defines the " +
+            "minimum minimum API Level required for the application to run, " +
+            "as well as the target version (the highest API level you have tested " +
+            "the version for.)",
+            convertMarkup(explanation, HTML));
+    }
+
+    public void testConvertMarkup5() throws Exception {
+        // monospace and bold test
+        // From ManifestDetector#MULTIPLE_USES_SDK
+        String explanation =
+            "The `<uses-sdk>` element should appear just once; the tools will *not* merge the " +
+            "contents of all the elements so if you split up the attributes across multiple " +
+            "elements, only one of them will take effect. To fix this, just merge all the " +
+            "attributes from the various elements into a single <uses-sdk> element.";
+
+        assertEquals(
+            "The <code>&lt;uses-sdk></code> element should appear just once; the tools " +
+            "will <b>not</b> merge the " +
+            "contents of all the elements so if you split up the attributes across multiple " +
+            "elements, only one of them will take effect. To fix this, just merge all the " +
+            "attributes from the various elements into a single &lt;uses-sdk> element.",
+            convertMarkup(explanation, HTML));
+        }
+
+    public void testConvertMarkup6() throws Exception {
+        // Embedded code next to attributes
+        // From AlwaysShowActionDetector#ISSUE
+        String explanation =
+            "Using `showAsAction=\"always\"` in menu XML, or `MenuItem.SHOW_AS_ACTION_ALWAYS` in "+
+            "Java code is usually a deviation from the user interface style guide." +
+            "Use `ifRoom` or the corresponding `MenuItem.SHOW_AS_ACTION_IF_ROOM` instead.\n" +
+            "\n" +
+            "If `always` is used sparingly there are usually no problems and behavior is " +
+            "roughly equivalent to `ifRoom` but with preference over other `ifRoom` " +
+            "items. Using it more than twice in the same menu is a bad idea.\n" +
+            "\n" +
+            "This check looks for menu XML files that contain more than two `always` " +
+            "actions, or some `always` actions and no `ifRoom` actions. In Java code, " +
+            "it looks for projects that contain references to `MenuItem.SHOW_AS_ACTION_ALWAYS` " +
+            "and no references to `MenuItem.SHOW_AS_ACTION_IF_ROOM`.";
+
+        assertEquals(
+            "Using <code>showAsAction=\"always\"</code> in menu XML, or " +
+            "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
+            "from the user interface style guide.Use <code>ifRoom</code> or the " +
+            "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
+            "<br/>\n" +
+            "If <code>always</code> is used sparingly there are usually no problems and " +
+            "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
+            "other <code>ifRoom</code> items. Using it more than twice in the same menu " +
+            "is a bad idea.<br/>\n" +
+            "<br/>\n" +
+            "This check looks for menu XML files that contain more than two <code>always</code> " +
+            "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
+            "In Java code, it looks for projects that contain references to " +
+            "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
+            "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
+            convertMarkup(explanation, HTML));
+    }
+
+    public void testConvertSelf() throws Exception {
+        // No changes
+        assertEquals("`foo`<b>", RAW.convertTo("`foo`<b>", RAW));
+        assertEquals("`foo`<b>", TEXT.convertTo("`foo`<b>", TEXT));
+        assertEquals("`foo`<b>", HTML.convertTo("`foo`<b>", HTML));
+    }
+
+    public void testConvertFromHtml() throws Exception {
+        assertEquals(""
+                        + "Line 1\n"
+                        + "Line 2 <div>\n",
+                HTML.convertTo("<html>Line 1<br>Line 2\n<!-- comment -->&lt;div&gt;</html>",
+                        TEXT));
+    }
+
+    public void testConvertFromHtml2() throws Exception {
+        assertEquals(""
+                        + "Using showAsAction=\"always\" in menu XML, or\n"
+                        + "MenuItem.SHOW_AS_ACTION_ALWAYS in Java code is usually a\n"
+                        + "deviation from the user interface style guide.Use ifRoom or\n"
+                        + "the corresponding MenuItem.SHOW_AS_ACTION_IF_ROOM instead.\n"
+                        + "If always is used sparingly there are usually no problems\n"
+                        + "and behavior is roughly equivalent to ifRoom but with\n"
+                        + "preference over other ifRoom items. Using it more than twice\n"
+                        + "in the same menu is a bad idea. This check looks for menu\n"
+                        + "XML files that contain more than two always actions, or some\n"
+                        + "always actions and no ifRoom actions. In Java code, it looks\n"
+                        + "for projects that contain references to\n"
+                        + "MenuItem.SHOW_AS_ACTION_ALWAYS and no references to\n"
+                        + "MenuItem.SHOW_AS_ACTION_IF_ROOM.\n",
+                HTML.convertTo(
+                        "Using <code>showAsAction=\"always\"</code> in       menu XML, or " +
+                        "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
+                        "from the user interface style guide.Use <code>ifRoom</code> or the " +
+                        "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
+                        "<br/>\n" +
+                        "If <code>always</code> is used sparingly there are usually no problems and " +
+                        "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
+                        "other <code>ifRoom</code> items.    Using it more than twice in the same menu " +
+                        "is a bad idea.<br/>\n" +
+                        "<br/>\n" +
+                        "This check looks for menu XML files that contain more than two <code>always</code> " +
+                        "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
+                        "In Java code, it looks for projects that contain references to " +
+                        "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
+                        "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
+                TEXT));
+    }
+
+    public void testNbsp() throws Exception {
+        assertEquals("&nbsp;&nbsp;text", RAW.convertTo("\u00a0\u00A0text", HTML));
+    }
+
+    public void test181820() throws Exception {
+        // Regression test for https://code.google.com/p/android/issues/detail?id=181820
+        // Make sure we handle formatting characters at the end
+        assertEquals("foo bar *", convertMarkup("foo bar *", HTML));
+    }
+}
\ No newline at end of file
diff --git a/misc/api-generator/.classpath b/misc/api-generator/.classpath
deleted file mode 100644
index 8c8b2ad..0000000
--- a/misc/api-generator/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/net/sf/kxml/kxml2/2.3.0/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/net/sf/kxml/kxml2/2.3.0/kxml2-2.3.0-sources.jar"/>
-	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm/5.0.3/asm-5.0.3.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm/5.0.3/asm-5.0.3-sources.jar"/>
-	<classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm-tree/5.0.3/asm-tree-5.0.3.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm-tree/5.0.3/asm-tree-5.0.3-sources.jar"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/common"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/misc/api-generator/.gitignore b/misc/api-generator/.gitignore
index c986b6b..89d1178 100644
--- a/misc/api-generator/.gitignore
+++ b/misc/api-generator/.gitignore
@@ -1,3 +1,8 @@
 /bin
 /build
-
+output.xml
+.gradle
+build
+.idea
+api-generator.iml
+run.sh
diff --git a/misc/api-generator/.project b/misc/api-generator/.project
deleted file mode 100644
index ef098b6..0000000
--- a/misc/api-generator/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>api-generator</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
diff --git a/misc/api-generator/.settings/org.eclipse.jdt.core.prefs b/misc/api-generator/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100755
index 7ce49c8..0000000
--- a/misc/api-generator/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,12 +0,0 @@
-#Wed Jun 04 15:07:10 PDT 2014
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/misc/api-generator/build.gradle b/misc/api-generator/build.gradle
index 9fe3ab2..754744b 100755
--- a/misc/api-generator/build.gradle
+++ b/misc/api-generator/build.gradle
@@ -1,15 +1,19 @@
+apply plugin: 'application'
 apply plugin: 'java'
 
+mainClassName = "com.android.apigenerator.Main"
+applicationDefaultJvmArgs = ["-ea", "-Xms1048m", "-Xmx2048m"]
+
+repositories {
+   jcenter()
+}
+
+sourceCompatibility = 1.6
 dependencies {
-    compile project(':base:common')
+    compile 'com.android.tools:common:24.2.3'
     compile 'net.sf.kxml:kxml2:2.3.0'
     compile 'org.ow2.asm:asm:4.0'
     compile 'org.ow2.asm:asm-tree:4.0'
 }
 
-group = 'com.android.tools'
-archivesBaseName = 'api-generator'
-version = rootProject.ext.baseVersion
-
-// configure the manifest of the sdkJar task
-jar.manifest.attributes("Main-Class": "com.android.apigenerator.Main")
+defaultTasks 'installApp'
diff --git a/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java b/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java
index 8cdfe80..c06e1fa 100644
--- a/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java
+++ b/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java
@@ -17,7 +17,6 @@
 package com.android.apigenerator;
 
 import com.android.utils.Pair;
-
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.tree.ClassNode;
@@ -93,40 +92,41 @@
                         ClassNode classNode = new ClassNode();
                         reader.accept(classNode, 0 /*flags*/);
 
-                        if (classNode != null) {
-                            ApiClass theClass = addClass(map, classNode.name, apiLevel);
+                        ApiClass theClass = addClass(map, classNode.name, apiLevel,
+                                (classNode.access & Opcodes.ACC_DEPRECATED) != 0);
 
-                            // super class
-                            if (classNode.superName != null) {
-                                theClass.addSuperClass(classNode.superName, apiLevel);
+                        // super class
+                        if (classNode.superName != null) {
+                            theClass.addSuperClass(classNode.superName, apiLevel);
+                        }
+
+                        // interfaces
+                        for (Object interfaceName : classNode.interfaces) {
+                            theClass.addInterface((String) interfaceName, apiLevel);
+                        }
+
+                        // fields
+                        for (Object field : classNode.fields) {
+                            FieldNode fieldNode = (FieldNode) field;
+                            if ((fieldNode.access & Opcodes.ACC_PRIVATE) != 0) {
+                                continue;
                             }
-
-                            // interfaces
-                            for (Object interfaceName : classNode.interfaces) {
-                                theClass.addInterface((String) interfaceName, apiLevel);
+                            if (!fieldNode.name.startsWith("this$") &&
+                                    !fieldNode.name.equals("$VALUES")) {
+                                boolean deprecated = (fieldNode.access & Opcodes.ACC_DEPRECATED) != 0;
+                                theClass.addField(fieldNode.name, apiLevel, deprecated);
                             }
+                        }
 
-                            // fields
-                            for (Object field : classNode.fields) {
-                                FieldNode fieldNode = (FieldNode) field;
-                                if ((fieldNode.access & Opcodes.ACC_PRIVATE) != 0) {
-                                    continue;
-                                }
-                                if (fieldNode.name.startsWith("this$") == false &&
-                                        fieldNode.name.equals("$VALUES") == false) {
-                                    theClass.addField(fieldNode.name, apiLevel);
-                                }
+                        // methods
+                        for (Object method : classNode.methods) {
+                            MethodNode methodNode = (MethodNode) method;
+                            if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
+                                continue;
                             }
-
-                            // methods
-                            for (Object method : classNode.methods) {
-                                MethodNode methodNode = (MethodNode) method;
-                                if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
-                                    continue;
-                                }
-                                if (methodNode.name.equals("<clinit>") == false) {
-                                    theClass.addMethod(methodNode.name + methodNode.desc, apiLevel);
-                                }
+                            if (!methodNode.name.equals("<clinit>")) {
+                                boolean deprecated = (methodNode.access & Opcodes.ACC_DEPRECATED) != 0;
+                                theClass.addMethod(methodNode.name + methodNode.desc, apiLevel, deprecated);
                             }
                         }
                     }
@@ -171,7 +171,7 @@
                 String methodName = method.getKey();
                 int apiLevel = method.getValue();
 
-                if (methodName.startsWith("<init>(") == false) {
+                if (!methodName.startsWith("<init>(")) {
 
                     for (Pair<String, Integer> parent : superClasses) {
                         // only check the parent if it was a parent class at the introduction
@@ -255,13 +255,17 @@
         return false;
     }
 
-    private ApiClass addClass(HashMap<String, ApiClass> classes, String name, int apiLevel) {
+    private ApiClass addClass(HashMap<String, ApiClass> classes, String name, int apiLevel, boolean deprecated) {
         ApiClass theClass = classes.get(name);
         if (theClass == null) {
             theClass = new ApiClass(name, apiLevel);
             classes.put(name, theClass);
         }
 
+        if (deprecated && apiLevel < theClass.deprecatedIn) {
+            theClass.deprecatedIn = apiLevel;
+        }
+
         return theClass;
     }
 }
diff --git a/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java b/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java
index 50ee8c8..ad00cf4 100644
--- a/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java
+++ b/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java
@@ -33,6 +33,7 @@
  * This is used to write the simplified XML file containing all the public API.
  *
  */
+@SuppressWarnings({"UnnecessaryUnboxing", "UnnecessaryBoxing"})
 public class ApiClass {
 
     private final String mName;
@@ -45,12 +46,15 @@
 
     private final Map<String, Integer> mFields = new HashMap<String, Integer>();
     private final Map<String, Integer> mMethods = new HashMap<String, Integer>();
+	private final Map<String, Integer> mDeprecated = new HashMap<String, Integer>();
 
     public ApiClass(String name, int since) {
         mName = name;
         mSince = since;
     }
 
+    public int deprecatedIn = Integer.MAX_VALUE;
+
     public String getName() {
         return mName;
     }
@@ -59,18 +63,31 @@
         return mSince;
     }
 
-    public void addField(String name, int since) {
+    public void addField(String name, int since, boolean deprecated) {
         Integer i = mFields.get(name);
         if (i == null || i.intValue() > since) {
             mFields.put(name, Integer.valueOf(since));
         }
+
+        if (deprecated) {
+            i = mDeprecated.get(name);
+            if (i == null || i.intValue() > since) {
+                mDeprecated.put(name, Integer.valueOf(since));
+            }
+        }
     }
 
-    public void addMethod(String name, int since) {
+    public void addMethod(String name, int since, boolean deprecated) {
         Integer i = mMethods.get(name);
         if (i == null || i.intValue() > since) {
             mMethods.put(name, Integer.valueOf(since));
         }
+        if (deprecated) {
+            i = mDeprecated.get(name);
+            if (i == null || i.intValue() > since) {
+                mDeprecated.put(name, Integer.valueOf(since));
+            }
+        }
     }
 
     public Map<String, Integer> getMethods() {
@@ -114,6 +131,10 @@
         stream.print(mName);
         stream.print("\" since=\"");
         stream.print(mSince);
+        if (deprecatedIn < Integer.MAX_VALUE) {
+            stream.print("\" deprecated=\"");
+            stream.print(deprecatedIn);
+        }
         stream.println("\">");
 
         print(mSuperClasses, "extends", stream);
@@ -134,11 +155,16 @@
         });
 
         for (Pair<String, Integer> pair : list) {
+            Integer deprecated = mDeprecated.get(pair.getFirst());
             if (mSince == pair.getSecond()) {
                 stream.print("\t\t<");
                 stream.print(name);
                 stream.print(" name=\"");
                 stream.print(encodeAttribute(pair.getFirst()));
+                if (deprecated != null) {
+                    stream.print("\" deprecated=\"");
+                    stream.print(deprecated);
+                }
                 stream.println("\" />");
             } else {
                 stream.print("\t\t<");
@@ -147,6 +173,10 @@
                 stream.print(encodeAttribute(pair.getFirst()));
                 stream.print("\" since=\"");
                 stream.print(pair.getSecond());
+                if (deprecated != null) {
+                    stream.print("\" deprecated=\"");
+                    stream.print(deprecated);
+                }
                 stream.println("\" />");
             }
         }
@@ -156,11 +186,16 @@
         TreeMap<String, Integer> map2 = new TreeMap<String, Integer>(map);
 
         for (Entry<String, Integer> entry : map2.entrySet()) {
+            Integer deprecated = mDeprecated.get(entry.getKey());
             if (mSince == entry.getValue()) {
                 stream.print("\t\t<");
                 stream.print(name);
                 stream.print(" name=\"");
                 stream.print(encodeAttribute(entry.getKey()));
+                if (deprecated != null) {
+                    stream.print("\" deprecated=\"");
+                    stream.print(deprecated);
+                }
                 stream.println("\" />");
             } else {
                 stream.print("\t\t<");
@@ -169,6 +204,10 @@
                 stream.print(encodeAttribute(entry.getKey()));
                 stream.print("\" since=\"");
                 stream.print(entry.getValue());
+                if (deprecated != null) {
+                    stream.print("\" deprecated=\"");
+                    stream.print(deprecated);
+                }
                 stream.println("\" />");
             }
         }
diff --git a/misc/api-generator/src/main/java/com/android/apigenerator/Main.java b/misc/api-generator/src/main/java/com/android/apigenerator/Main.java
index ef50c1d..2ca0c44 100644
--- a/misc/api-generator/src/main/java/com/android/apigenerator/Main.java
+++ b/misc/api-generator/src/main/java/com/android/apigenerator/Main.java
@@ -16,8 +16,6 @@
 
 package com.android.apigenerator;
 
-
-
 import java.io.File;
 import java.io.PrintStream;
 import java.util.ArrayList;
@@ -115,8 +113,8 @@
 
     /**
      * Creates the simplified diff-based API level.
-     * @param outFolder the out folder.
-     * @param classes
+     * @param outFile the output file
+     * @param classes the classes to write
      */
     private static boolean createApiFile(File outFile, Map<String, ApiClass> classes) {
 
@@ -124,7 +122,7 @@
         try {
             ps = new PrintStream(outFile, "UTF-8");
             ps.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
-            ps.println("<api version=\"1\">");
+            ps.println("<api version=\"2\">");
             TreeMap<String, ApiClass> map = new TreeMap<String, ApiClass>(classes);
             for (ApiClass theClass : map.values()) {
                 (theClass).print(ps);
diff --git a/ninepatch/build.gradle b/ninepatch/build.gradle
index fdc032d..7699e1b 100644
--- a/ninepatch/build.gradle
+++ b/ninepatch/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 group = 'com.android.tools'
diff --git a/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java b/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java
index 9dab870..6a684ea 100644
--- a/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java
+++ b/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java
@@ -119,7 +119,7 @@
                 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
 
         try {
-            if (mPatches.size() == 0) {
+            if (mPatches.isEmpty()) {
                 g.drawImage(image, x, y, scaledWidth, scaledHeight, null);
                 return;
             }
@@ -272,7 +272,7 @@
         data.mRemainderVertical = scaledHeight - remainderVertical;
 
         data.mHorizontalPatchesSum = 0;
-        if (mHorizontalPatches.size() > 0) {
+        if (!mHorizontalPatches.isEmpty()) {
             int start = -1;
             for (Rectangle rect : mHorizontalPatches) {
                 if (rect.x > start) {
@@ -291,7 +291,7 @@
         }
 
         data.mVerticalPatchesSum = 0;
-        if (mVerticalPatches.size() > 0) {
+        if (!mVerticalPatches.isEmpty()) {
             int start = -1;
             for (Rectangle rect : mVerticalPatches) {
                 if (rect.y > start) {
@@ -341,14 +341,14 @@
         mFixed = getRectangles(left.mFirst, top.mFirst);
         mPatches = getRectangles(left.mSecond, top.mSecond);
 
-        if (mFixed.size() > 0) {
+        if (!mFixed.isEmpty()) {
             mHorizontalPatches = getRectangles(left.mFirst, top.mSecond);
             mVerticalPatches = getRectangles(left.mSecond, top.mFirst);
         } else {
-            if (top.mFirst.size() > 0) {
+            if (!top.mFirst.isEmpty()) {
                 mHorizontalPatches = new ArrayList<Rectangle>(0);
                 mVerticalPatches = getVerticalRectangles(height, top.mFirst);
-            } else if (left.mFirst.size() > 0) {
+            } else if (!left.mFirst.isEmpty()) {
                 mHorizontalPatches = getHorizontalRectangles(width, left.mFirst);
                 mVerticalPatches = new ArrayList<Rectangle>(0);
             } else {
@@ -393,7 +393,7 @@
     }
 
     private Pair<Integer> getPadding(List<Pair<Integer>> pairs) {
-        if (pairs.size() == 0) {
+        if (pairs.isEmpty()) {
             return new Pair<Integer>(0, 0);
         } else if (pairs.size() == 1) {
             if (pairs.get(0).mFirst == 0) {
@@ -471,7 +471,7 @@
             fixed.add(new Pair<Integer>(lastIndex, pixels.length));
         }
 
-        if (patches.size() == 0) {
+        if (patches.isEmpty()) {
             patches.add(new Pair<Integer>(1, pixels.length));
             startWithPatch[0] = true;
             fixed.clear();
diff --git a/perflib/build.gradle b/perflib/build.gradle
index 5d58f9e..a923c0d 100644
--- a/perflib/build.gradle
+++ b/perflib/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 
 group = 'com.android.tools.perflib'
 archivesBaseName = 'perflib'
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java b/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java
index 2af5890..23774dc 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java
@@ -17,6 +17,10 @@
 package com.android.tools.perflib.heap;
 
 import com.android.annotations.NonNull;
+import com.android.tools.perflib.heap.io.HprofBuffer;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
 
 public class ArrayInstance extends Instance {
 
@@ -45,10 +49,30 @@
         return values;
     }
 
+    @NonNull
+    private byte[] asRawByteArray(int start, int elementCount) {
+        getBuffer().setPosition(mValuesOffset);
+        assert mType != Type.OBJECT;
+        assert start + elementCount <= mLength;
+        byte[] bytes = new byte[elementCount * mType.getSize()];
+        getBuffer().readSubSequence(bytes, start * mType.getSize(), elementCount * mType.getSize());
+        return bytes;
+    }
+
+    @NonNull
+    public char[] asCharArray(int offset, int length) {
+        assert mType == Type.CHAR;
+        // TODO: Make this copy less by supporting offset in asRawByteArray.
+        CharBuffer charBuffer = ByteBuffer.wrap(asRawByteArray(offset, length)).order(HprofBuffer.HPROF_BYTE_ORDER).asCharBuffer();
+        char[] result = new char[length];
+        charBuffer.get(result);
+        return result;
+    }
+
     @Override
     public final int getSize() {
         // TODO: Take the rest of the fields into account: length, type, etc (~16 bytes).
-        return mLength * mType.getSize();
+        return mLength * mHeap.mSnapshot.getTypeSize(mType);
     }
 
     @Override
@@ -57,9 +81,13 @@
         if (mType == Type.OBJECT) {
             for (Object value : getValues()) {
                 if (value instanceof Instance) {
-                    visitor.visitLater((Instance) value);
+                    if (!mReferencesAdded) {
+                        ((Instance)value).addReference(null, this);
+                    }
+                    visitor.visitLater(this, (Instance)value);
                 }
             }
+            mReferencesAdded = true;
         }
     }
 
@@ -73,7 +101,15 @@
         }
     }
 
+    public Type getArrayType() {
+        return mType;
+    }
+
     public final String toString() {
-        return String.format("%s[%d]@0x%08x", mType.name(), mLength, mId);
+        String className = getClassObj().getClassName();
+        if (className.endsWith("[]")) {
+            className = className.substring(0, className.length() - 2);
+        }
+        return String.format("%s[%d]@%d (0x%x)", className, mLength, getUniqueId(), getUniqueId());
     }
 }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java b/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java
index f7746d1..562ab48 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java
@@ -17,10 +17,11 @@
 package com.android.tools.perflib.heap;
 
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
 public class ClassInstance extends Instance {
 
@@ -32,19 +33,26 @@
     }
 
     @VisibleForTesting
-    Object getField(Type type, String name) {
-        return getValues().get(new Field(type, name));
+    @NonNull
+    List<FieldValue> getFields(String name) {
+        ArrayList<FieldValue> result = new ArrayList<FieldValue>();
+        for (FieldValue value : getValues()) {
+            if (value.getField().getName().equals(name)) {
+                result.add(value);
+            }
+        }
+        return result;
     }
 
     @NonNull
-    public Map<Field, Object> getValues() {
-        Map<Field, Object> result = new HashMap<Field, Object>();
+    public List<FieldValue> getValues() {
+        ArrayList<FieldValue> result = new ArrayList<FieldValue>();
 
         ClassObj clazz = getClassObj();
         getBuffer().setPosition(mValuesOffset);
         while (clazz != null) {
             for (Field field : clazz.getFields()) {
-                result.put(field, readValue(field.getType()));
+                result.add(new FieldValue(field, readValue(field.getType())));
             }
             clazz = clazz.getSuperClassObj();
         }
@@ -54,14 +62,41 @@
     @Override
     public final void accept(@NonNull Visitor visitor) {
         visitor.visitClassInstance(this);
-        for (Object value : getValues().values()) {
-            if (value instanceof Instance) {
-                visitor.visitLater((Instance) value);
+        for (FieldValue field : getValues()) {
+            if (field.getValue() instanceof Instance) {
+                if (!mReferencesAdded) {
+                    ((Instance) field.getValue()).addReference(field.getField(), this);
+                }
+                visitor.visitLater(this, (Instance) field.getValue());
             }
         }
+        mReferencesAdded = true;
+    }
+
+    @Override
+    public boolean getIsSoftReference() {
+        return getClassObj().getIsSoftReference();
     }
 
     public final String toString() {
-        return String.format("%s@0x%08x", getClassObj().getClassName(), mId);
+        return String.format("%s@%d (0x%x)", getClassObj().getClassName(), getUniqueId(), getUniqueId());
+    }
+
+    public static class FieldValue {
+        private Field mField;
+        private Object mValue;
+
+        public FieldValue(@NonNull Field field, @Nullable Object value) {
+            this.mField = field;
+            this.mValue = value;
+        }
+
+        public Field getField() {
+            return mField;
+        }
+
+        public Object getValue() {
+            return mValue;
+        }
     }
 }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java b/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
index 1da9af0..121c27c 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
@@ -19,15 +19,16 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
+import gnu.trove.TIntObjectHashMap;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 public class ClassObj extends Instance implements Comparable<ClassObj> {
+    public static class HeapData {
+        public int mShallowSize = 0;
+
+        public List<Instance> mInstances = new ArrayList<Instance>();
+    }
 
     @NonNull
     final String mClassName;
@@ -44,8 +45,10 @@
 
     private int mInstanceSize;
 
+    private boolean mIsSoftReference = false;
+
     @NonNull
-    ArrayList<Instance> mInstances = new ArrayList<Instance>();
+    TIntObjectHashMap<HeapData> mHeapData = new TIntObjectHashMap<HeapData>();
 
     @NonNull
     Set<ClassObj> mSubclasses = new HashSet<ClassObj>();
@@ -77,8 +80,18 @@
         return mClassName.replace('/', '.');
     }
 
-    public final void addInstance(@NonNull Instance instance) {
-        mInstances.add(instance);
+    public final void addInstance(int heapId, @NonNull Instance instance) {
+        if (instance instanceof ClassInstance) {
+            instance.setSize(mInstanceSize);
+        }
+
+        HeapData heapData = mHeapData.get(heapId);
+        if (heapData == null) {
+          heapData = new HeapData();
+          mHeapData.put(heapId, heapData);
+        }
+        heapData.mInstances.add(instance);
+        heapData.mShallowSize += instance.getSize();
     }
 
     public final void setSuperClassId(long superClass) {
@@ -119,6 +132,20 @@
         return mInstanceSize;
     }
 
+    public int getShallowSize(int heapId) {
+        HeapData heapData = mHeapData.get(heapId);
+        return heapData == null ? 0 : mHeapData.get(heapId).mShallowSize;
+    }
+
+    public void setIsSoftReference() {
+        mIsSoftReference = true;
+    }
+
+    @Override
+    public boolean getIsSoftReference() {
+        return mIsSoftReference;
+    }
+
     @NonNull
     public Map<Field, Object> getStaticFieldValues() {
         Map<Field, Object> result = new HashMap<Field, Object>();
@@ -164,16 +191,31 @@
     @Override
     public final void accept(@NonNull Visitor visitor) {
         visitor.visitClassObj(this);
-        for (Object value : getStaticFieldValues().values()) {
+        for (Map.Entry<Field, Object> entry : getStaticFieldValues().entrySet()) {
+            Object value = entry.getValue();
             if (value instanceof Instance) {
-                visitor.visitLater((Instance) value);
+                if (!mReferencesAdded) {
+                    ((Instance)value).addReference(entry.getKey(), this);
+                }
+                visitor.visitLater(this, (Instance)value);
             }
         }
+        mReferencesAdded = true;
     }
 
     @Override
     public final int compareTo(@NonNull ClassObj o) {
-        return mClassName.compareTo(o.mClassName);
+        if (getId() == o.getId()) {
+            return 0;
+        }
+
+        int nameCompareResult = mClassName.compareTo(o.mClassName);
+        if (nameCompareResult != 0) {
+            return nameCompareResult;
+        }
+        else {
+            return getId() - o.getId() > 0 ? 1 : -1;
+        }
     }
 
     public final boolean equals(Object o) {
@@ -200,11 +242,65 @@
 
     @Nullable
     public Instance getClassLoader() {
-        return mHeap.mSnapshot.findReference(mClassLoaderId);
+        return mHeap.mSnapshot.findInstance(mClassLoaderId);
+    }
+
+    public List<Instance> getInstancesList() {
+        int count = getInstanceCount();
+        ArrayList<Instance> resultList = new ArrayList<Instance>(count);
+        for (int heapId : mHeapData.keys()) {
+            resultList.addAll(getHeapInstances(heapId));
+        }
+        return resultList;
     }
 
     @NonNull
-    public Collection<Instance> getInstances() {
-        return mInstances;
+    public List<Instance> getHeapInstances(int heapId) {
+        HeapData result = mHeapData.get(heapId);
+        return result == null ? new ArrayList<Instance>(0) : result.mInstances;
+    }
+
+    public int getHeapInstancesCount(int heapId) {
+      HeapData result = mHeapData.get(heapId);
+      return result == null ? 0 : result.mInstances.size();
+    }
+
+    public int getInstanceCount() {
+        int count = 0;
+        for (Object heapStat : mHeapData.getValues()) {
+            count += ((HeapData)heapStat).mInstances.size();
+        }
+        return count;
+    }
+
+    public int getShallowSize() {
+        int size = 0;
+        for (Object heapStat : mHeapData.getValues()) {
+            size += ((HeapData)heapStat).mShallowSize;
+        }
+        return size;
+    }
+
+    @NonNull
+    public static String getReferenceClassName() {
+        return "java.lang.ref.Reference";
+    }
+
+    @NonNull
+    public List<ClassObj> getDescendantClasses() {
+        List<ClassObj> descendants = new ArrayList<ClassObj>();
+
+        Stack<ClassObj> searchStack = new Stack<ClassObj>();
+        searchStack.push(this);
+
+        while (!searchStack.isEmpty()) {
+            ClassObj classObj = searchStack.pop();
+            descendants.add(classObj);
+            for (ClassObj subClass : classObj.getSubclasses()) {
+                searchStack.push(subClass);
+            }
+        }
+
+        return descendants;
     }
 }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java b/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
index 4c4d601..3d617e6 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
@@ -20,8 +20,8 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 
+import com.google.common.collect.*;
 import gnu.trove.TIntObjectHashMap;
 import gnu.trove.TLongObjectHashMap;
 import gnu.trove.TObjectProcedure;
@@ -53,8 +53,7 @@
     @NonNull
     TLongObjectHashMap<ClassObj> mClassesById = new TLongObjectHashMap<ClassObj>();
 
-    @NonNull
-    HashMap<String, ClassObj> mClassesByName = new HashMap<String, ClassObj>();
+    @NonNull Multimap<String, ClassObj> mClassesByName = ArrayListMultimap.create();
 
     //  List of instances of above class definitions
     private final TLongObjectHashMap<Instance> mInstances = new TLongObjectHashMap<Instance>();
@@ -134,13 +133,21 @@
     }
 
     public final ClassObj getClass(String name) {
+        Collection<ClassObj> classes = mClassesByName.get(name);
+        if (classes.size() == 1) {
+            return classes.iterator().next();
+        }
+        return null;
+    }
+
+    public final Collection<ClassObj> getClasses(String name) {
         return mClassesByName.get(name);
     }
 
     public final void dumpInstanceCounts() {
         for (Object value : mClassesById.getValues()) {
             ClassObj theClass = (ClassObj) value;
-            int count = theClass.mInstances.size();
+            int count = theClass.getInstanceCount();
 
             if (count > 0) {
                 System.out.println(theClass + ": " + count);
@@ -166,7 +173,7 @@
 
             int size = 0;
 
-            for (Instance instance : theClass.mInstances) {
+            for (Instance instance : theClass.getHeapInstances(getId())) {
                 size += instance.getCompositeSize();
             }
 
@@ -194,4 +201,8 @@
         });
         return result;
     }
+
+    public int getInstancesCount() {
+        return mInstances.size();
+    }
 }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java b/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
index d420fd9..80271ab 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
@@ -150,7 +150,7 @@
                 readNullTerminatedString();  // Version, ignored for now.
 
                 mIdSize = mInput.readInt();
-                Type.setIdSize(mIdSize);
+                mSnapshot.setIdSize(mIdSize);
 
                 mInput.readLong();  // Timestamp, ignored for now.
 
@@ -196,6 +196,7 @@
                 //  this is fine
             }
             mSnapshot.resolveClasses();
+            mSnapshot.resolveReferences();
             // TODO: enable this after the dominators computation is also optimized.
             // mSnapshot.computeRetainedSizes();
         } catch (Exception e) {
@@ -519,9 +520,9 @@
             Type type = Type.getType(mInput.readByte());
 
             staticFields[i] = new Field(type, name);
-            skipFully(type.getSize());
+            skipFully(mSnapshot.getTypeSize(type));
 
-            bytesRead += mIdSize + 1 + type.getSize();
+            bytesRead += mIdSize + 1 + mSnapshot.getTypeSize(type);
         }
 
         theClass.setStaticFields(staticFields);
@@ -587,7 +588,7 @@
         StackTrace stack = mSnapshot.getStackTrace(stackId);
         int numElements = mInput.readInt();
         Type type = Type.getType(readUnsignedByte());
-        int size = type.getSize();
+        int size = mSnapshot.getTypeSize(type);
         ArrayInstance array = new ArrayInstance(id, stack, type, numElements, mInput.position());
         mSnapshot.addInstance(id, array);
 
@@ -611,7 +612,7 @@
 
     private int skipValue() throws IOException {
         Type type = Type.getType(readUnsignedByte());
-        int size = type.getSize();
+        int size = mSnapshot.getTypeSize(type);
 
         skipFully(size);
 
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java b/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java
index e68ab8b..4aab914 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java
@@ -46,6 +46,12 @@
     //  Another identifier for this Instance, that we computed during the analysis phase.
     int mTopologicalOrder;
 
+    int mDistanceToGcRoot = Integer.MAX_VALUE;
+
+    boolean mReferencesAdded = false;
+
+    Instance mNextInstanceToGcRoot = null;
+
     //  The immediate dominator of this instance, or null if not reachable from any GC roots.
     @Nullable
     private Instance mImmediateDominator;
@@ -57,7 +63,11 @@
     private long[] mRetainedSizes;
 
     //  List of all objects that hold a live reference to this object
-    private final ArrayList<Instance> mReferences = new ArrayList<Instance>();
+    private final ArrayList<Instance> mHardReferences = new ArrayList<Instance>();
+
+    //  List of all objects that hold a soft/weak/phantom reference to this object.
+    //  Don't create an actual list until we need to.
+    private ArrayList<Instance> mSoftReferences = null;
 
     Instance(long id, @NonNull StackTrace stackTrace) {
         mId = id;
@@ -68,6 +78,10 @@
         return mId;
     }
 
+    public long getUniqueId() {
+        return getId() & mHeap.mSnapshot.getIdSizeMask();
+    }
+
     public abstract void accept(Visitor visitor);
 
     public void setClassId(long classId) {
@@ -118,6 +132,23 @@
         mImmediateDominator = dominator;
     }
 
+    public int getDistanceToGcRoot() {
+        return mDistanceToGcRoot;
+    }
+
+    public Instance getNextInstanceToGcRoot() {
+        return mNextInstanceToGcRoot;
+    }
+
+    public void setDistanceToGcRoot(int newDistance) {
+        assert(newDistance < mDistanceToGcRoot);
+        mDistanceToGcRoot = newDistance;
+    }
+
+    public void setNextInstanceToGcRoot(Instance instance) {
+        mNextInstanceToGcRoot = instance;
+    }
+
     public void resetRetainedSize() {
         List<Heap> allHeaps = mHeap.mSnapshot.mHeaps;
         if (mRetainedSizes == null) {
@@ -138,7 +169,7 @@
 
     public long getTotalRetainedSize() {
         if (mRetainedSizes == null) {
-          return 0;
+            return 0;
         }
 
         long totalSize = 0;
@@ -148,14 +179,42 @@
         return totalSize;
     }
 
-    //  Add to the list of objects that have a hard reference to this Instance
-    public void addReference(Instance reference) {
-        mReferences.add(reference);
+    /**
+     * Add to the list of objects that references this Instance.
+     *
+     * @param field the named variable in #reference pointing to this instance. If the name of the field is "referent", and #reference is a
+     *              soft reference type, then reference is counted as a soft reference instead of the usual hard reference.
+     * @param reference another instance that references this instance
+     */
+    public void addReference(@Nullable Field field, @NonNull Instance reference) {
+        if (reference.getIsSoftReference() && field != null && field.getName().equals("referent")) {
+            if (mSoftReferences == null) {
+                mSoftReferences = new ArrayList<Instance>();
+            }
+            mSoftReferences.add(reference);
+        }
+        else {
+            mHardReferences.add(reference);
+        }
     }
 
     @NonNull
-    public ArrayList<Instance> getReferences() {
-        return mReferences;
+    public ArrayList<Instance> getHardReferences() {
+        return mHardReferences;
+    }
+
+    @Nullable
+    public ArrayList<Instance> getSoftReferences() {
+        return mSoftReferences;
+    }
+
+    /**
+     * There is an underlying assumption that a class that is a soft reference will only have one referent.
+     *
+     * @return true if the instance is a soft reference type, or false otherwise
+     */
+    public boolean getIsSoftReference() {
+        return false;
     }
 
     @Nullable
@@ -163,11 +222,7 @@
         switch (type) {
             case OBJECT:
                 long id = readId();
-                Instance result = mHeap.mSnapshot.findReference(id);
-                if (result != null) {
-                    result.addReference(this);
-                }
-                return result;
+                return mHeap.mSnapshot.findInstance(id);
             case BOOLEAN:
                 return getBuffer().readByte() != 0;
             case CHAR:
@@ -190,7 +245,7 @@
 
     protected long readId() {
         // As long as we don't interpret IDs, reading signed values here is fine.
-        switch (Type.OBJECT.getSize()) {
+        switch (mHeap.mSnapshot.getTypeSize(Type.OBJECT)) {
             case 1:
                 return getBuffer().readByte();
             case 2:
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java b/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
index 2269ab2..c481321 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
@@ -57,8 +57,8 @@
     }
 
     @Override
-    public void visitLater(@NonNull Instance instance) {
-        mStack.push(instance);
+    public void visitLater(Instance parent, @NonNull Instance child) {
+        mStack.push(child);
     }
 
     public void doVisit(Iterable<? extends Instance> startNodes) {
@@ -67,7 +67,7 @@
                 // RootObj nodes don't have their own id, they should be visited right away.
                 node.accept(this);
             } else {
-                visitLater(node);
+                visitLater(null, node);
             }
         }
         while (!mStack.isEmpty()) {
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java b/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java
index 4b6b427..3868308 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java
@@ -19,14 +19,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
+import java.util.*;
 
 public class Queries {
     /*
@@ -166,9 +159,8 @@
             throw new IllegalArgumentException("Class not found: " + baseClassName);
         }
 
-        Instance[] instances = new Instance[theClass.mInstances.size()];
-
-        return theClass.mInstances.toArray(instances);
+        List<Instance> instances = theClass.getInstancesList();
+        return instances.toArray(new Instance[instances.size()]);
     }
 
     /*
@@ -191,7 +183,7 @@
         ArrayList<Instance> instanceList = new ArrayList<Instance>();
 
         for (ClassObj someClass : classList) {
-            instanceList.addAll(someClass.mInstances);
+            instanceList.addAll(someClass.getInstancesList());
         }
 
         Instance[] result = new Instance[instanceList.size()];
@@ -220,7 +212,7 @@
     public static Instance findObject(@NonNull Snapshot snapshot, String id) {
         long id2 = Long.parseLong(id, 16);
 
-        return snapshot.findReference(id2);
+        return snapshot.findInstance(id2);
     }
 
     @NonNull
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java b/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java
index a37eed3..3a6fb86 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java
@@ -48,7 +48,7 @@
         if (mType == RootType.SYSTEM_CLASS) {
             theClass = snapshot.findClass(mId);
         } else {
-            theClass = snapshot.findReference(mId).getClassObj();
+            theClass = snapshot.findInstance(mId).getClassObj();
         }
 
         if (theClass == null) {
@@ -63,7 +63,7 @@
         visitor.visitRootObj(this);
         Instance instance = getReferredInstance();
         if (instance != null) {
-            visitor.visitLater(instance);
+            visitor.visitLater(null, instance);
         }
     }
 
@@ -76,7 +76,7 @@
         if (mType == RootType.SYSTEM_CLASS) {
             return mHeap.mSnapshot.findClass(mId);
         } else {
-            return mHeap.mSnapshot.findReference(mId);
+            return mHeap.mSnapshot.findInstance(mId);
         }
     }
 
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java b/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
index 04e0cff..0b074bf 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
@@ -19,13 +19,13 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.tools.perflib.heap.analysis.Dominators;
+import com.android.tools.perflib.heap.analysis.ShortestDistanceVisitor;
 import com.android.tools.perflib.heap.analysis.TopologicalSort;
 import com.android.tools.perflib.heap.io.HprofBuffer;
 import com.google.common.collect.ImmutableList;
+import gnu.trove.THashSet;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 
 /*
  * A snapshot of all of the heaps, and related meta-data, for the runtime at a given instant.
@@ -56,6 +56,13 @@
 
     private Dominators mDominators;
 
+    //  The set of all classes that are (sub)class(es) of java.lang.ref.Reference.
+    private THashSet<ClassObj> mReferenceClasses = new THashSet<ClassObj>();
+
+    private int[] mTypeSizes;
+
+    private long mIdSizeMask = 0x00000000ffffffffl;
+
     public Snapshot(@NonNull HprofBuffer buffer) {
         mBuffer = buffer;
         setToDefaultHeap();
@@ -134,8 +141,7 @@
         return mCurrentHeap.getStackTrace(traceSerialNumber);
     }
 
-    public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
-            int depth) {
+    public final StackTrace getStackTraceAtDepth(int traceSerialNumber, int depth) {
         return mCurrentHeap.getStackTraceAtDepth(traceSerialNumber, depth);
     }
 
@@ -152,6 +158,30 @@
         return mCurrentHeap.getThread(serialNumber);
     }
 
+    public final void setIdSize(int size) {
+        int maxId = -1;
+        for (int i = 0; i < Type.values().length; ++i) {
+            maxId = Math.max(Type.values()[i].getTypeId(), maxId);
+        }
+        assert (maxId > 0) && (maxId <= Type.LONG.getTypeId()); // Update this if hprof format ever changes its supported types.
+        mTypeSizes = new int[maxId + 1];
+        Arrays.fill(mTypeSizes, -1);
+
+        for (int i = 0; i < Type.values().length; ++i) {
+            mTypeSizes[Type.values()[i].getTypeId()] = Type.values()[i].getSize();
+        }
+        mTypeSizes[Type.OBJECT.getTypeId()] = size;
+        mIdSizeMask = 0xffffffffffffffffl >>> ((8 - size) * 8);
+    }
+
+    public final int getTypeSize(Type type) {
+        return mTypeSizes[type.getTypeId()];
+    }
+
+    public final long getIdSizeMask() {
+        return mIdSizeMask;
+    }
+
     public final void addInstance(long id, @NonNull Instance instance) {
         mCurrentHeap.addInstance(id, instance);
         instance.setHeap(mCurrentHeap);
@@ -163,7 +193,7 @@
     }
 
     @Nullable
-    public final Instance findReference(long id) {
+    public final Instance findInstance(long id) {
         //noinspection ForLoopReplaceableByForEach
         for (int i = 0; i < mHeaps.size(); i++) {
             Instance instance = mHeaps.get(i).getInstance(id);
@@ -191,6 +221,12 @@
         return null;
     }
 
+    /**
+     * Finds the first ClassObj with a class name that matches <code>name</code>.
+     *
+     * @param name of the class to find
+     * @return the found <code>ClassObj</code>, or null if not found
+     */
     @Nullable
     public final ClassObj findClass(String name) {
         //noinspection ForLoopReplaceableByForEach
@@ -205,6 +241,24 @@
         return null;
     }
 
+    /**
+     * Finds all <code>ClassObj</code>s with class name that match the given <code>name</code>.
+     *
+     * @param name of the class to find
+     * @return a collection of the found <code>ClassObj</code>s, or empty collection if not found
+     */
+    @NonNull
+    public final Collection<ClassObj> findClasses(String name) {
+        ArrayList<ClassObj> classObjs = new ArrayList<ClassObj>();
+
+        //noinspection ForLoopReplaceableByForEach
+        for (int i = 0; i < mHeaps.size(); i++) {
+            classObjs.addAll(mHeaps.get(i).getClasses(name));
+        }
+
+        return classObjs;
+    }
+
     public void resolveClasses() {
         ClassObj clazz = findClass(JAVA_LANG_CLASS);
         int javaLangClassSize = clazz != null ? clazz.getInstanceSize() : 0;
@@ -220,29 +274,46 @@
                 int classSize = javaLangClassSize;
 
                 for (Field f : classObj.mStaticFields) {
-                    classSize += f.getType().getSize();
+                    classSize += getTypeSize(f.getType());
                 }
                 classObj.setSize(classSize);
             }
             for (Instance instance : heap.getInstances()) {
                 ClassObj classObj = instance.getClassObj();
                 if (classObj != null) {
-                    classObj.addInstance(instance);
-                    // Now is a good time to set the size of this instance
-                    if (instance instanceof ClassInstance) {
-                        instance.setSize(classObj.getInstanceSize());
-                    }
+                    classObj.addInstance(heap.getId(), instance);
                 }
             }
         }
     }
 
+    public void resolveReferences() {
+        List<ClassObj> referenceDescendants = findAllDescendantClasses(ClassObj.getReferenceClassName());
+        for (ClassObj classObj : referenceDescendants) {
+            classObj.setIsSoftReference();
+            mReferenceClasses.add(classObj);
+        }
+    }
+
+    @NonNull
+    public List<ClassObj> findAllDescendantClasses(@NonNull String className) {
+        Collection<ClassObj> ancestorClasses = findClasses(className);
+        List<ClassObj> descendants = new ArrayList<ClassObj>();
+        for (ClassObj ancestor : ancestorClasses) {
+            descendants.addAll(ancestor.getDescendantClasses());
+        }
+        return descendants;
+    }
+
     // TODO: Break dominator computation into fixed chunks, because it can be unbounded/expensive.
     public void computeDominators() {
         if (mDominators == null) {
             mTopSort = TopologicalSort.compute(getGCRoots());
             mDominators = new Dominators(this, mTopSort);
             mDominators.computeRetainedSizes();
+
+            ShortestDistanceVisitor shortestDistanceVisitor = new ShortestDistanceVisitor();
+            shortestDistanceVisitor.doVisit(getGCRoots());
         }
     }
 
@@ -257,6 +328,10 @@
         return result;
     }
 
+    public ImmutableList<Instance> getTopologicalOrdering() {
+        return mTopSort;
+    }
+
     public final void dumpInstanceCounts() {
         for (Heap heap : mHeaps) {
             System.out.println(
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Type.java b/perflib/src/main/java/com/android/tools/perflib/heap/Type.java
index 05ac6d7..d988869 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Type.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Type.java
@@ -21,7 +21,7 @@
 import java.util.Map;
 
 public enum Type {
-    OBJECT(2, 0),
+    OBJECT(2, 0), // Pointer sizes are dependent on the hprof file, so set it to 0 for now.
     BOOLEAN(4, 1),
     CHAR(5, 2),
     FLOAT(6, 4),
@@ -31,8 +31,6 @@
     INT(10, 4),
     LONG(11, 8);
 
-    private static int sIdSize = 4;
-
     private static Map<Integer, Type> sTypeMap = Maps.newHashMap();
 
     private int mId;
@@ -50,16 +48,16 @@
         mSize = size;
     }
 
-    public static final void setIdSize(int size) {
-        sIdSize = size;
-    }
-
     public static Type getType(int id) {
         return sTypeMap.get(id);
     }
 
     public int getSize() {
-        return this == OBJECT ? sIdSize : mSize;
+        return mSize;
+    }
+
+    public int getTypeId() {
+        return mId;
     }
 
     public static String getClassNameOfPrimitiveArray(Type type) {
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Value.java b/perflib/src/main/java/com/android/tools/perflib/heap/Value.java
index 98980ff..4e74ba6 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Value.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Value.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 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.
@@ -37,7 +37,7 @@
         mValue = value;
 
         if (value instanceof Instance) {
-            ((Instance) value).addReference(instance);
+            ((Instance) value).addReference(null, instance);
         }
     }
 }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java b/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java
index d6af10b..53b2e55 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java
@@ -31,5 +31,5 @@
     /**
      * Marks an instance to be visited later, depending on the visitor's traversal strategy.
      */
-    void visitLater(@NonNull Instance instance);
+    void visitLater(Instance parent, @NonNull Instance child);
 }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java
index cba7bb5..f7b04e8 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java
@@ -17,10 +17,7 @@
 package com.android.tools.perflib.heap.analysis;
 
 import com.android.annotations.NonNull;
-import com.android.tools.perflib.heap.Heap;
-import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.RootObj;
-import com.android.tools.perflib.heap.Snapshot;
+import com.android.tools.perflib.heap.*;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -73,8 +70,8 @@
                 if (node.getImmediateDominator() != Snapshot.SENTINEL_ROOT) {
                     Instance dominator = null;
 
-                    for (int j = 0; j < node.getReferences().size(); j++) {
-                        Instance predecessor = node.getReferences().get(j);
+                    for (int j = 0; j < node.getHardReferences().size(); j++) {
+                        Instance predecessor = node.getHardReferences().get(j);
                         if (predecessor.getImmediateDominator() == null) {
                             // If we don't have a dominator/approximation for predecessor, skip it
                             continue;
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/ShortestDistanceVisitor.java b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/ShortestDistanceVisitor.java
new file mode 100644
index 0000000..5cb1a42
--- /dev/null
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/ShortestDistanceVisitor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 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.tools.perflib.heap.analysis;
+
+import com.android.annotations.NonNull;
+import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.NonRecursiveVisitor;
+
+import java.util.Comparator;
+import java.util.PriorityQueue;
+
+public class ShortestDistanceVisitor extends NonRecursiveVisitor {
+    private PriorityQueue<Instance> mPriorityQueue = new PriorityQueue<Instance>(1024, new Comparator<Instance>() {
+        @Override
+        public int compare(Instance o1, Instance o2) {
+            return o1.getDistanceToGcRoot() - o2.getDistanceToGcRoot();
+        }
+    });
+    private Instance mPreviousInstance = null;
+    private int mVisitDistance = 0;
+
+    @Override
+    public void visitLater(Instance parent, @NonNull Instance child) {
+        if (mVisitDistance < child.getDistanceToGcRoot() &&
+                (parent == null ||
+                     child.getSoftReferences() == null ||
+                     !child.getSoftReferences().contains(parent) ||
+                     child.getIsSoftReference())) {
+            child.setDistanceToGcRoot(mVisitDistance);
+            child.setNextInstanceToGcRoot(mPreviousInstance);
+            mPriorityQueue.add(child);
+        }
+    }
+
+    @Override
+    public void doVisit(Iterable<? extends Instance> startNodes) {
+        // root nodes are instances that share the same id as the node they point to.
+        // This means that we cannot mark them as visited here or they would be marking
+        // the actual root instance
+        // TODO RootObj should not be Instance objects
+        for (Instance node : startNodes) {
+            node.accept(this);
+        }
+
+        while (!mPriorityQueue.isEmpty()) {
+            Instance node = mPriorityQueue.poll();
+            mVisitDistance = node.getDistanceToGcRoot() + 1;
+            mPreviousInstance = node;
+            node.accept(this);
+        }
+    }
+}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
index 3ce8aa4..3bcc704 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
@@ -66,7 +66,19 @@
         private final List<Instance> mPostorder = Lists.newArrayList();
 
         @Override
+        public void visitLater(Instance parent, @NonNull Instance child) {
+            if (!mSeen.contains(child.getId())) {
+                mStack.push(child);
+            }
+        }
+
+        @Override
         public void doVisit(Iterable<? extends Instance> startNodes) {
+            // root nodes are instances that share the same id as the node they point to.
+            // This means that we cannot mark them as visited here or they would be marking
+            // the actual root instance
+            // TODO RootObj should not be Instance objects
+
             for (Instance node : startNodes) {
                 node.accept(this);
             }
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java b/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java
index 09cb59a..15d7d6d 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java
@@ -15,12 +15,17 @@
  */
 package com.android.tools.perflib.heap.io;
 
+import java.nio.ByteOrder;
+
 public interface HprofBuffer {
+    ByteOrder HPROF_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
 
     byte readByte();
 
     void read(byte[] b);
 
+    void readSubSequence(byte[] b, int sourceStart, int sourceEnd);
+
     char readChar();
 
     short readShort();
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java b/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java
index b282663..7607f0f 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java
@@ -23,7 +23,6 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
 import java.nio.channels.FileChannel;
 
 import sun.nio.ch.DirectBuffer;
@@ -62,7 +61,7 @@
                 long size = Math.min(mLength - offset, mBufferSize + mPadding);
                 mByteBuffers[i] = inputStream.getChannel()
                         .map(FileChannel.MapMode.READ_ONLY, offset, size);
-                mByteBuffers[i].order(ByteOrder.BIG_ENDIAN);
+                mByteBuffers[i].order(HPROF_BYTE_ORDER);
                 offset += mBufferSize;
             }
             mCurrentPosition = 0;
@@ -121,6 +120,35 @@
     }
 
     @Override
+    public void readSubSequence(@NonNull byte[] b, int sourceStart, int length) {
+        assert length < mLength;
+
+        mCurrentPosition += sourceStart;
+
+        int index = getIndex();
+        mByteBuffers[index].position(getOffset());
+        if (b.length <= mByteBuffers[index].remaining()) {
+            mByteBuffers[index].get(b, 0, b.length);
+        } else {
+            int split = mBufferSize - mByteBuffers[index].position();
+            mByteBuffers[index].get(b, 0, split);
+
+            int start = split;
+            int remainingMaxLength = Math.min(length - start, b.length - start);
+            int remainingShardCount = (remainingMaxLength + mBufferSize - 1) / mBufferSize;
+            for (int i = 0; i < remainingShardCount; ++i) {
+                int maxToRead = Math.min(remainingMaxLength, mBufferSize);
+                mByteBuffers[index + 1 + i].position(0);
+                mByteBuffers[index + 1 + i].get(b, start, maxToRead);
+                start += maxToRead;
+                remainingMaxLength -= maxToRead;
+            }
+        }
+
+        mCurrentPosition += Math.min(b.length, length);
+    }
+
+    @Override
     public char readChar() {
         char result = mByteBuffers[getIndex()].getChar(getOffset());
         mCurrentPosition += 2;
diff --git a/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java b/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java
index ae14475..e12b5df 100644
--- a/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java
+++ b/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java
@@ -24,7 +24,6 @@
 import com.google.common.collect.Maps;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
diff --git a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java
index 6209153..17ce3ec 100644
--- a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java
+++ b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java
@@ -21,7 +21,6 @@
 import com.android.tools.perflib.vmtrace.ClockType;
 import com.android.tools.perflib.vmtrace.MethodInfo;
 import com.android.tools.perflib.vmtrace.ThreadInfo;
-import com.android.tools.perflib.vmtrace.TimeSelector;
 import com.android.tools.perflib.vmtrace.VmTraceData;
 import com.android.utils.HtmlBuilder;
 
diff --git a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java
index 90ed94e..b315459 100644
--- a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java
+++ b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java
@@ -19,7 +19,6 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.VisibleForTesting;
 
-import java.awt.*;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java
index 48563b3..9374e33 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java
@@ -43,4 +43,28 @@
     assertNotNull(dialer);
     assertEquals(5, dialer.getAllFieldsCount());
   }
+
+  public void testComparison() {
+    ClassObj a = new ClassObj(1, null, "TestClassA", 0);
+    ClassObj b = new ClassObj(1, null, "TestClassB", 0);
+    ClassObj c = new ClassObj(2, null, "TestClassC", 0);
+    ClassObj aAlt = new ClassObj(3, null, "TestClassA", 0);
+
+    assertEquals(0, a.compareTo(a));
+    assertEquals(0, a.compareTo(b)); // This is a weird test case, since IDs are supposed to be unique.
+    assertTrue(c.compareTo(a) > 0);
+    assertTrue(aAlt.compareTo(a) > 0);
+  }
+
+  public void testSubClassNameClash() {
+    ClassObj superClass = new ClassObj(1, null, "TestClassA", 0);
+    ClassObj subClass1 = new ClassObj(2, null, "SubClass", 0);
+    ClassObj subClass2 = new ClassObj(3, null, "SubClass", 0);
+    superClass.addSubclass(subClass1);
+    superClass.addSubclass(subClass2);
+
+    assertEquals(2, superClass.getSubclasses().size());
+    assertTrue(superClass.getSubclasses().contains(subClass1));
+    assertTrue(superClass.getSubclasses().contains(subClass2));
+  }
 }
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java
index c089dda..9b674b2 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java
@@ -22,6 +22,7 @@
 
 import java.io.File;
 import java.util.Collection;
+import java.util.List;
 
 public class HprofParserTest extends TestCase {
 
@@ -70,23 +71,25 @@
 
     public void testPrimitiveArrays() {
         ClassObj byteArray = mSnapshot.findClass("byte[]");
-        assertEquals(1406, byteArray.getInstances().size());
+        assertEquals(1406, byteArray.getInstancesList().size());
+        assertEquals(0, byteArray.getInstanceSize());
+        assertEquals(681489, byteArray.getShallowSize());
 
-        ArrayInstance byteArrayInstance = (ArrayInstance) mSnapshot.findReference(0xB0D60401);
+        ArrayInstance byteArrayInstance = (ArrayInstance) mSnapshot.findInstance(0xB0D60401);
         assertEquals(byteArray, byteArrayInstance.getClassObj());
         assertEquals(43224, byteArrayInstance.getSize());
         assertEquals(43224, byteArrayInstance.getCompositeSize());
 
         ClassObj intArrayArray = mSnapshot.findClass("int[][]");
-        assertEquals(37, intArrayArray.getInstances().size());
+        assertEquals(37, intArrayArray.getInstancesList().size());
 
-        ArrayInstance intArrayInstance = (ArrayInstance) mSnapshot.findReference(0xB0F69F58);
+        ArrayInstance intArrayInstance = (ArrayInstance) mSnapshot.findInstance(0xB0F69F58);
         assertEquals(intArrayArray, intArrayInstance.getClassObj());
         assertEquals(40, intArrayInstance.getSize());
         assertEquals(52, intArrayInstance.getCompositeSize());
 
         ClassObj stringArray = mSnapshot.findClass("java.lang.String[]");
-        assertEquals(1396, stringArray.getInstances().size());
+        assertEquals(1396, stringArray.getInstancesList().size());
     }
 
     /**
@@ -103,7 +106,7 @@
         Object[] values = array.getValues();
         assertEquals(6, values.length);
 
-        Collection<Instance> instances = clazz.getInstances();
+        Collection<Instance> instances = clazz.getInstancesList();
         for (Object value : values) {
             assertTrue(value instanceof Instance);
             assertTrue(instances.contains(value));
@@ -114,11 +117,18 @@
         ClassInstance instance = (ClassInstance) enumValue;
         assertSame(clazz, instance.getClassObj());
 
-        Object name = instance.getField(Type.OBJECT, "name");
+        List<ClassInstance.FieldValue> fields = instance.getFields("name");
+        assertEquals(1, fields.size());
+        assertEquals(Type.OBJECT, fields.get(0).getField().getType());
+        Object name = fields.get(0).getValue();
+
         assertTrue(name instanceof ClassInstance);
         ClassInstance string = (ClassInstance) name;
         assertEquals("java.lang.String", string.getClassObj().getClassName());
-        Object value = string.getField(Type.OBJECT, "value");
+        fields = string.getFields("value");
+        assertEquals(1, fields.size());
+        assertEquals(Type.OBJECT, fields.get(0).getField().getType());
+        Object value = fields.get(0).getValue();
         assertTrue(value instanceof ArrayInstance);
         Object[] data = ((ArrayInstance) value).getValues();
         assertEquals(3, data.length);
@@ -126,7 +136,45 @@
         assertEquals('E', data[1]);
         assertEquals('W', data[2]);
 
-        Object ordinal = instance.getField(Type.INT, "ordinal");
-        assertEquals(0, ordinal);
+        fields = instance.getFields("ordinal");
+        assertEquals(1, fields.size());
+        assertEquals(Type.INT, fields.get(0).getField().getType());
+        assertEquals(0, fields.get(0).getValue());
+    }
+
+    /**
+     * Tests getValues to make sure it's not adding duplicate entries to the back references.
+     */
+    public void testDuplicateEntries() {
+        mSnapshot = new SnapshotBuilder(2).addReferences(1, 2).addRoot(1).build();
+        mSnapshot.computeDominators();
+
+        assertEquals(2, mSnapshot.getReachableInstances().size());
+        ClassInstance parent = (ClassInstance)mSnapshot.findInstance(1);
+        List<ClassInstance.FieldValue> firstGet = parent.getValues();
+        List<ClassInstance.FieldValue> secondGet = parent.getValues();
+        assertEquals(1, firstGet.size());
+        assertEquals(firstGet.size(), secondGet.size());
+        Instance child = mSnapshot.findInstance(2);
+        assertEquals(1, child.getHardReferences().size());
+    }
+
+    public void testResolveReferences() {
+        mSnapshot = new SnapshotBuilder(1).addRoot(1).build();
+        ClassObj subSoftReferenceClass = new ClassObj(98, null, "SubSoftReference", 0);
+        subSoftReferenceClass.setSuperClassId(SnapshotBuilder.SOFT_REFERENCE_ID);
+        ClassObj subSubSoftReferenceClass = new ClassObj(97, null, "SubSubSoftReference", 0);
+        subSubSoftReferenceClass.setSuperClassId(98);
+
+        mSnapshot.findClass(SnapshotBuilder.SOFT_REFERENCE_ID).addSubclass(subSoftReferenceClass);
+        subSoftReferenceClass.addSubclass(subSubSoftReferenceClass);
+
+        mSnapshot.addClass(98, subSoftReferenceClass);
+        mSnapshot.addClass(97, subSubSoftReferenceClass);
+
+        mSnapshot.resolveReferences();
+
+        assertTrue(subSoftReferenceClass.getIsSoftReference());
+        assertTrue(subSubSoftReferenceClass.getIsSoftReference());
     }
 }
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/SnapshotBuilder.java b/perflib/src/test/java/com/android/tools/perflib/heap/SnapshotBuilder.java
new file mode 100644
index 0000000..35f06e9
--- /dev/null
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/SnapshotBuilder.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 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.tools.perflib.heap;
+
+import com.android.tools.perflib.heap.io.InMemoryBuffer;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Utility for creating Snapshot objects to be used in tests.
+ *
+ * As the main concern here is graph connectivity, we only initialize the app heap, creating
+ * ClassInstance objects with id in [1..numNodes], each instance pointing to a unique ClassObj.
+ * The class ids range in [101..100+numNodes] and their size is set to match the id of their object
+ * instance. The default heap holds the roots.
+ */
+public class SnapshotBuilder {
+    public static final int SOFT_REFERENCE_ID = 99;
+
+    public static final int SOFT_AND_HARD_REFERENCE_ID = 98;
+
+    private final Snapshot mSnapshot;
+
+    private final ClassInstance[] mNodes;
+
+    private final int[] mOffsets;
+
+    private final ByteBuffer mDirectBuffer;
+
+    private final int mMaxTotalNodes;
+
+    private short mNextAvailableSoftReferenceNodeId;
+
+    private short mNextAvailableSoftAndHardReferenceNodeId;
+
+    public SnapshotBuilder(int numNodes) {
+        this(numNodes, 0, 0);
+    }
+
+    public SnapshotBuilder(int numNodes, int numSoftNodes, int numSoftAndHardNodes) {
+        mMaxTotalNodes = numNodes + numSoftNodes + numSoftAndHardNodes;
+        InMemoryBuffer buffer = new InMemoryBuffer(2 * mMaxTotalNodes * mMaxTotalNodes);
+        mDirectBuffer = buffer.getDirectBuffer();
+        mOffsets = new int[mMaxTotalNodes + 1];
+
+        mSnapshot = new Snapshot(buffer);
+        mSnapshot.setHeapTo(13, "testHeap");
+        mSnapshot.setIdSize(2);
+
+        ClassObj softClazz = new ClassObj(SOFT_REFERENCE_ID, null, ClassObj.getReferenceClassName(), 0);
+        softClazz.setClassLoaderId(0);
+        softClazz.setFields(new Field[]{new Field(Type.OBJECT, "referent")});
+        softClazz.setIsSoftReference();
+        mSnapshot.addClass(SOFT_REFERENCE_ID, softClazz);
+
+        ClassObj softAndHardClazz = new ClassObj(SOFT_AND_HARD_REFERENCE_ID, null, "SoftAndHardReference", 0);
+        softAndHardClazz.setSuperClassId(SOFT_REFERENCE_ID);
+        softAndHardClazz.setClassLoaderId(0);
+        softAndHardClazz.setFields(new Field[]{new Field(Type.OBJECT, "referent"), new Field(Type.OBJECT, "hardReference")});
+        softAndHardClazz.setIsSoftReference();
+        mSnapshot.addClass(SOFT_AND_HARD_REFERENCE_ID, softAndHardClazz);
+
+        mNodes = new ClassInstance[mMaxTotalNodes + 1];
+        for (int i = 1; i <= numNodes; i++) {
+            // Use same name classes on different loaders to extend test coverage
+            ClassObj clazz = new ClassObj(100 + i, null, "Class" + (i / 2), 0);
+            clazz.setClassLoaderId(i % 2);
+            clazz.setFields(new Field[0]);
+            mSnapshot.addClass(100 + i, clazz);
+
+            mOffsets[i] = 2 * (i - 1) * mMaxTotalNodes;
+            mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
+            mNodes[i].setClassId(100 + i);
+            mNodes[i].setSize(i);
+            mSnapshot.addInstance(i, mNodes[i]);
+        }
+
+        mNextAvailableSoftReferenceNodeId = (short)(numNodes + 1);
+        for (int i = mNextAvailableSoftReferenceNodeId; i <= mMaxTotalNodes; ++i) {
+            mOffsets[i] = 2 * (i - 1) * mMaxTotalNodes;
+            mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
+            mNodes[i].setClassId(SOFT_REFERENCE_ID);
+            mNodes[i].setSize(i);
+            mSnapshot.addInstance(i, mNodes[i]);
+        }
+
+        mNextAvailableSoftAndHardReferenceNodeId = (short)(numNodes + numSoftNodes + 1);
+        for (int i = mNextAvailableSoftAndHardReferenceNodeId; i <= mMaxTotalNodes; ++i) {
+            mOffsets[i] = 2 * (i - 1) * mMaxTotalNodes;
+            mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
+            mNodes[i].setClassId(SOFT_AND_HARD_REFERENCE_ID);
+            mNodes[i].setSize(i);
+            mSnapshot.addInstance(i, mNodes[i]);
+        }
+    }
+
+    public SnapshotBuilder addReferences(int nodeFrom, int... nodesTo) {
+        assertEquals(mNodes[nodeFrom].getClassObj().getFields().length, 0);
+
+        Field[] fields = new Field[nodesTo.length];
+        for (int i = 0; i < nodesTo.length; i++) {
+            insertHardReferenceIntoBuffer(nodeFrom, nodesTo[i], i);
+            // Fields should support duplicated field names due to inheritance of private fields
+            fields[i] = new Field(Type.OBJECT, "duplicated_name");
+        }
+
+        mNodes[nodeFrom].getClassObj().setFields(fields);
+        return this;
+    }
+
+    /**
+     * Inserts a soft reference instance between <code>nodeFrom</code> to <code>nodeTo</code>.
+     *
+     * @param nodeFrom the parent node
+     * @param nodeTo the child node
+     * @return this
+     */
+    public SnapshotBuilder insertSoftReference(int nodeFrom, int nodeToSoftReference) {
+        Field[] nodeFromFields = mNodes[nodeFrom].getClassObj().getFields();
+        Field[] newFields = Arrays.copyOf(nodeFromFields, nodeFromFields.length + 1);
+
+        short softReferenceId = mNextAvailableSoftReferenceNodeId++;
+        assert softReferenceId <= mMaxTotalNodes;
+        insertSoftReferenceIntoBuffer(nodeFrom, softReferenceId, nodeFromFields.length);
+
+        setupSoftReference(softReferenceId, nodeToSoftReference);
+        newFields[nodeFromFields.length] = new Field(Type.OBJECT, "soft" + nodeToSoftReference);
+        mNodes[nodeFrom].getClassObj().setFields(newFields);
+        return this;
+    }
+
+    public SnapshotBuilder insertSoftAndHardReference(int nodeFrom, int nodeToSoftReference, int nodeToHardReference) {
+        Field[] nodeFromFields = mNodes[nodeFrom].getClassObj().getFields();
+        Field[] newFields = Arrays.copyOf(nodeFromFields, nodeFromFields.length + 1);
+
+        short softReferenceId = mNextAvailableSoftAndHardReferenceNodeId++;
+        assert softReferenceId <= mMaxTotalNodes;
+        insertSoftReferenceIntoBuffer(nodeFrom, softReferenceId, nodeFromFields.length);
+
+        setupSoftAndHardReference(softReferenceId, nodeToSoftReference, nodeToHardReference);
+        newFields[nodeFromFields.length] = new Field(Type.OBJECT, "soft" + nodeToSoftReference + "hard" + nodeToHardReference);
+        mNodes[nodeFrom].getClassObj().setFields(newFields);
+        return this;
+    }
+
+    public SnapshotBuilder addRoot(int node) {
+        RootObj root = new RootObj(RootType.JAVA_LOCAL, node);
+        mSnapshot.setToDefaultHeap();
+        mSnapshot.addRoot(root);
+        return this;
+    }
+
+    public Snapshot build() {
+        return mSnapshot;
+    }
+
+    private void insertHardReferenceIntoBuffer(int nodeFrom, int nodeTo, int fieldIndex) {
+        mDirectBuffer.putShort(mOffsets[nodeFrom] + fieldIndex * 2, (short)nodeTo);
+    }
+
+    private void insertSoftReferenceIntoBuffer(int nodeFrom, int softReferenceId, int fieldIndex) {
+        mDirectBuffer.putShort(mOffsets[nodeFrom] + fieldIndex * 2, (short)softReferenceId);
+    }
+
+    private void setupSoftReference(int softReferenceId, int referent) {
+        mDirectBuffer.putShort(mOffsets[softReferenceId], (short)referent);
+    }
+
+    private void setupSoftAndHardReference(int softReferenceId, int referent, int hardReference) {
+        mDirectBuffer.putShort(mOffsets[softReferenceId], (short)referent);
+        mDirectBuffer.putShort(mOffsets[softReferenceId] + 2, (short)hardReference);
+    }
+}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java
index 9e691ca..e398a2c 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java
@@ -16,15 +16,14 @@
 
 package com.android.tools.perflib.heap.analysis;
 
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.HprofParser;
-import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.Snapshot;
+import com.android.tools.perflib.heap.*;
 import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer;
 
 import junit.framework.TestCase;
 
 import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
 
 public class DominatorsTest extends TestCase {
 
@@ -37,7 +36,7 @@
                 .addReferences(3, 4, 5)
                 .addReferences(4, 6)
                 .addRoot(1)
-                .getSnapshot();
+                .build();
 
         mSnapshot.computeDominators();
 
@@ -47,6 +46,12 @@
         assertDominates(1, 4);
         assertDominates(1, 6);
         assertDominates(3, 5);
+
+        assertParentPathToGc(2, 1);
+        assertParentPathToGc(3, 1);
+        assertParentPathToGc(4, 2, 3);
+        assertParentPathToGc(5, 3);
+        assertParentPathToGc(6, 2);
     }
 
     public void testCyclicGraph() {
@@ -56,7 +61,7 @@
                 .addReferences(3, 4)
                 .addReferences(4, 2)
                 .addRoot(1)
-                .getSnapshot();
+                .build();
 
         mSnapshot.computeDominators();
 
@@ -64,6 +69,10 @@
         assertDominates(1, 2);
         assertDominates(1, 3);
         assertDominates(1, 4);
+
+        assertParentPathToGc(2, 1);
+        assertParentPathToGc(3, 1);
+        assertParentPathToGc(4, 1);
     }
 
     public void testMultipleRoots() {
@@ -75,7 +84,7 @@
                 .addReferences(5, 6)
                 .addRoot(1)
                 .addRoot(2)
-                .getSnapshot();
+                .build();
 
         mSnapshot.computeDominators();
 
@@ -83,9 +92,13 @@
         assertDominates(1, 3);
         assertDominates(2, 4);
         // Node 5 is reachable via both roots, neither of which can be the sole dominator.
-        assertEquals(mSnapshot.SENTINEL_ROOT,
-                mSnapshot.findReference(5).getImmediateDominator());
+        assertEquals(mSnapshot.SENTINEL_ROOT, mSnapshot.findInstance(5).getImmediateDominator());
         assertDominates(5, 6);
+
+        assertParentPathToGc(3, 1);
+        assertParentPathToGc(4, 2);
+        assertParentPathToGc(5, 3, 4);
+        assertParentPathToGc(6, 5);
     }
 
     public void testDoublyLinkedList() {
@@ -101,14 +114,126 @@
                 .addReferences(8, 7, 9)
                 .addReferences(9, 2, 8)
                 .addRoot(1)
-                .getSnapshot();
+                .build();
 
         mSnapshot.computeDominators();
 
-        assertEquals(45, mSnapshot.findReference(1).getRetainedSize(1));
-        for (int i = 2; i <= 9; i++) {
-            assertEquals(i, mSnapshot.findReference(i).getRetainedSize(1));
+        assertEquals(45, mSnapshot.findInstance(1).getRetainedSize(1));
+        assertEquals(44, mSnapshot.findInstance(2).getRetainedSize(1));
+        for (int i = 3; i <= 9; i++) {
+            assertEquals(i, mSnapshot.findInstance(i).getRetainedSize(1));
         }
+
+        assertParentPathToGc(2, 1);
+        assertParentPathToGc(3, 2);
+        assertParentPathToGc(9, 2);
+        assertParentPathToGc(4, 3);
+        assertParentPathToGc(8, 9);
+        assertParentPathToGc(5, 4);
+        assertParentPathToGc(7, 8);
+        assertParentPathToGc(6, 5, 7);
+    }
+
+    public void testSameClassDifferentLoader() {
+        mSnapshot = new SnapshotBuilder(4)
+                .addReferences(1, 3, 2)
+                .addReferences(3, 2)
+                .addRoot(1)
+                .build();
+
+        assertNotNull(mSnapshot.getHeap(13).getClass(102));
+        assertNotNull(mSnapshot.getHeap(13).getClass(103));
+
+        mSnapshot.computeDominators();
+
+        assertEquals(0, mSnapshot.getHeap(13).getClass(102).getRetainedSize(1));
+        assertEquals(0, mSnapshot.getHeap(13).getClass(103).getRetainedSize(1));
+    }
+
+    public void testTopSort() {
+        mSnapshot = new SnapshotBuilder(4)
+                .addReferences(1, 3, 2)
+                .addReferences(3, 2)
+                .addRoot(1)
+                .build();
+
+        mSnapshot.computeDominators();
+
+        assertEquals(6, mSnapshot.findInstance(1).getRetainedSize(1));
+        assertEquals(2, mSnapshot.findInstance(2).getRetainedSize(1));
+        assertEquals(3, mSnapshot.findInstance(3).getRetainedSize(1));
+    }
+
+    public void testMultiplePaths() {
+        mSnapshot = new SnapshotBuilder(8)
+                .addReferences(1, 7, 8)
+                .addReferences(7, 2, 3)
+                .addReferences(8, 2)
+                .addReferences(2, 4)
+                .addReferences(3, 5)
+                .addReferences(5, 4)
+                .addReferences(4, 6)
+                .addRoot(1)
+                .build();
+
+        mSnapshot.computeDominators();
+
+        assertEquals(mSnapshot.findInstance(1), mSnapshot.findInstance(4).getImmediateDominator());
+        assertEquals(mSnapshot.findInstance(4), mSnapshot.findInstance(6).getImmediateDominator());
+        assertEquals(36, mSnapshot.findInstance(1).getRetainedSize(1));
+        assertEquals(2, mSnapshot.findInstance(2).getRetainedSize(1));
+        assertEquals(8, mSnapshot.findInstance(3).getRetainedSize(1));
+    }
+
+    public void testReachableInstances() {
+        mSnapshot = new SnapshotBuilder(11, 2, 1)
+                .addReferences(1, 2, 3)
+                .insertSoftReference(1, 11)
+                .addReferences(2, 4)
+                .addReferences(3, 5, 6)
+                .insertSoftReference(4, 9)
+                .addReferences(5, 7)
+                .addReferences(6, 7)
+                .addReferences(7, 8, 10)
+                .insertSoftAndHardReference(8, 10, 9)
+                .addRoot(1)
+                .build();
+
+        mSnapshot.computeDominators();
+        for (Heap heap : mSnapshot.getHeaps()) {
+            ClassObj softClass = heap.getClass(SnapshotBuilder.SOFT_REFERENCE_ID);
+            if (softClass != null) {
+                assertTrue(softClass.getIsSoftReference());
+            }
+
+            ClassObj softAndHardClass = heap.getClass(SnapshotBuilder.SOFT_AND_HARD_REFERENCE_ID);
+            if (softAndHardClass != null) {
+                assertTrue(softAndHardClass.getIsSoftReference());
+            }
+        }
+
+        Instance instance9 = mSnapshot.findInstance(9);
+        assertNotNull(instance9);
+        assertNotNull(instance9.getSoftReferences());
+        assertEquals(1, instance9.getHardReferences().size());
+        assertEquals(1, instance9.getSoftReferences().size());
+        assertEquals(6, instance9.getDistanceToGcRoot());
+
+        Instance instance10 = mSnapshot.findInstance(10);
+        assertNotNull(instance10);
+        assertNotNull(instance10.getSoftReferences());
+        assertEquals(1, instance10.getHardReferences().size());
+        assertEquals(1, instance10.getSoftReferences().size());
+        assertEquals(4, instance10.getDistanceToGcRoot());
+
+        Instance instance11 = mSnapshot.findInstance(11);
+        assertNotNull(instance11);
+        assertNotNull(instance11.getSoftReferences());
+        assertEquals(0, instance11.getHardReferences().size());
+        assertEquals(1, instance11.getSoftReferences().size());
+        assertEquals(Integer.MAX_VALUE, instance11.getDistanceToGcRoot());
+
+        assertEquals(13, mSnapshot.getReachableInstances().size());
     }
 
     public void testSampleHprof() throws Exception {
@@ -116,11 +241,20 @@
         mSnapshot = (new HprofParser(new MemoryMappedFileBuffer(file))).parse();
         mSnapshot.computeDominators();
 
-        // TODO: investigate the unreachable objects: there are 43687 objects in total.
-        assertEquals(42911, mSnapshot.getReachableInstances().size());
+        Set<Instance> topologicalSet = new HashSet<Instance>(mSnapshot.getTopologicalOrdering());
+        assertEquals(topologicalSet.size(), mSnapshot.getTopologicalOrdering().size());
+
+        long totalInstanceCount = 0;
+        for (Heap heap : mSnapshot.getHeaps()) {
+            totalInstanceCount += heap.getInstances().size();
+            totalInstanceCount += heap.getClasses().size();
+        }
+        assertEquals(43687, totalInstanceCount);
+
+        assertEquals(42839, mSnapshot.getReachableInstances().size());
 
         // An object reachable via two GC roots, a JNI global and a Thread.
-        Instance instance = mSnapshot.findReference(0xB0EDFFA0);
+        Instance instance = mSnapshot.findInstance(0xB0EDFFA0);
         assertEquals(Snapshot.SENTINEL_ROOT, instance.getImmediateDominator());
 
         int appIndex = mSnapshot.getHeapIndex(mSnapshot.getHeap("app"));
@@ -133,7 +267,7 @@
 
         // One of the bigger objects in the app heap
         ClassObj activityThread = mSnapshot.findClass("android.app.ActivityThread");
-        assertEquals(813, activityThread.getRetainedSize(zygoteIndex));
+        assertEquals(853, activityThread.getRetainedSize(zygoteIndex));
         assertEquals(576, activityThread.getRetainedSize(appIndex));
     }
 
@@ -141,7 +275,20 @@
      * Asserts that nodeA dominates nodeB in mHeap.
      */
     private void assertDominates(int nodeA, int nodeB) {
-        assertEquals(mSnapshot.findReference(nodeA),
-                mSnapshot.findReference(nodeB).getImmediateDominator());
+        assertEquals(mSnapshot.findInstance(nodeA),
+                mSnapshot.findInstance(nodeB).getImmediateDominator());
+    }
+
+    /**
+     * Asserts that one of the parents is the direct parent to node in the shortest path to the GC root.
+     */
+    private void assertParentPathToGc(int node, int... parents) {
+        for (int parent : parents) {
+            Instance parentInstance = mSnapshot.findInstance(node).getNextInstanceToGcRoot();
+            if (parentInstance != null && parentInstance.getId() == parent) {
+                return;
+            }
+        }
+        fail();
     }
 }
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/SnapshotBuilder.java b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/SnapshotBuilder.java
deleted file mode 100644
index 1358675..0000000
--- a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/SnapshotBuilder.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2014 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.tools.perflib.heap.analysis;
-
-import com.android.tools.perflib.heap.ClassInstance;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Field;
-import com.android.tools.perflib.heap.RootObj;
-import com.android.tools.perflib.heap.RootType;
-import com.android.tools.perflib.heap.Snapshot;
-import com.android.tools.perflib.heap.Type;
-import com.android.tools.perflib.heap.io.InMemoryBuffer;
-
-import java.nio.ByteBuffer;
-
-/**
- * Utility for creating Snapshot objects to be used in tests.
- *
- * As the main concern here is graph connectivity, we only initialize the app heap, creating
- * ClassInstance objects with id in [1..numNodes], each instance pointing to a unique ClassObj.
- * The class ids range in [101..100+numNodes] and their size is set to match the id of their object
- * instance. The default heap holds the roots.
- */
-public class SnapshotBuilder {
-
-    private final Snapshot mSnapshot;
-
-    private final ClassInstance[] mNodes;
-
-    private final int[] mOffsets;
-
-    private final ByteBuffer mDirectBuffer;
-
-    public SnapshotBuilder(int numNodes) {
-        Type.setIdSize(2); // A good opportunity to test the handling of short IDs.
-        InMemoryBuffer buffer = new InMemoryBuffer(2 * numNodes * numNodes);
-        mDirectBuffer = buffer.getDirectBuffer();
-        mOffsets = new int[numNodes + 1];
-
-        mSnapshot = new Snapshot(buffer);
-        mSnapshot.setHeapTo(13, "testHeap");
-
-        mNodes = new ClassInstance[numNodes + 1];
-        for (int i = 1; i <= numNodes; i++) {
-            ClassObj clazz = new ClassObj(100 + i, null, "Class" + i, 0);
-            clazz.setFields(new Field[0]);
-            mSnapshot.addClass(100 + i, clazz);
-
-            mOffsets[i] = 2 * (i - 1) * numNodes;
-            mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
-            mNodes[i].setClassId(100 + i);
-            mNodes[i].setSize(i);
-            mSnapshot.addInstance(i, mNodes[i]);
-        }
-    }
-
-    public SnapshotBuilder addReferences(int nodeFrom, int... nodesTo) {
-        Field[] fields = new Field[nodesTo.length];
-        for (int i = 0; i < nodesTo.length; i++) {
-            mDirectBuffer.putShort(mOffsets[nodeFrom] + i * 2, (short) nodesTo[i]);
-            fields[i] = new Field(Type.OBJECT, "f" + nodesTo[i]);
-        }
-
-        mNodes[nodeFrom].getClassObj().setFields(fields);
-        return this;
-    }
-
-    public SnapshotBuilder addRoot(int node) {
-        RootObj root = new RootObj(RootType.JAVA_LOCAL, node);
-        mSnapshot.setToDefaultHeap();
-        mSnapshot.addRoot(root);
-        return this;
-    }
-
-    public Snapshot getSnapshot() {
-        return mSnapshot;
-    }
-}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java
index 74de899..9b26671 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java
@@ -17,15 +17,7 @@
 package com.android.tools.perflib.heap.analysis;
 
 import com.android.annotations.NonNull;
-import com.android.tools.perflib.heap.ArrayInstance;
-import com.android.tools.perflib.heap.ClassInstance;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Field;
-import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.RootObj;
-import com.android.tools.perflib.heap.RootType;
-import com.android.tools.perflib.heap.Snapshot;
-import com.android.tools.perflib.heap.Type;
+import com.android.tools.perflib.heap.*;
 import com.android.tools.perflib.heap.io.InMemoryBuffer;
 import com.google.common.collect.Maps;
 
@@ -54,7 +46,7 @@
     }
 
     public void testSimpleStaticFieldsGraph() {
-        Type.setIdSize(4);
+        mSnapshot.setIdSize(4);
         final ClassInstance object1 = new ClassInstance(1, null, 0);
         object1.setClassId(42);
         object1.setSize(20);
@@ -87,7 +79,7 @@
     }
 
     public void testSimpleArray() {
-        Type.setIdSize(4);
+        mSnapshot.setIdSize(4);
         final ClassInstance object = new ClassInstance(1, null, 0);
         object.setClassId(42);
         object.setSize(20);
@@ -116,12 +108,12 @@
                 .addReferences(2, 4)
                 .addReferences(3, 4)
                 .addRoot(1)
-                .getSnapshot();
+                .build();
 
-        assertEquals(10, snapshot.findReference(1).getCompositeSize());
-        assertEquals(6, snapshot.findReference(2).getCompositeSize());
-        assertEquals(7, snapshot.findReference(3).getCompositeSize());
-        assertEquals(4, snapshot.findReference(4).getCompositeSize());
+        assertEquals(10, snapshot.findInstance(1).getCompositeSize());
+        assertEquals(6, snapshot.findInstance(2).getCompositeSize());
+        assertEquals(7, snapshot.findInstance(3).getCompositeSize());
+        assertEquals(4, snapshot.findInstance(4).getCompositeSize());
     }
 
     public void testBasicCycle() {
@@ -130,12 +122,12 @@
                 .addReferences(2, 3)
                 .addReferences(3, 1)
                 .addRoot(1)
-                .getSnapshot();
+                .build();
 
         // The composite size is a sum over all nodes participating in the cycle.
-        assertEquals(6, snapshot.findReference(1).getCompositeSize());
-        assertEquals(6, snapshot.findReference(2).getCompositeSize());
-        assertEquals(6, snapshot.findReference(3).getCompositeSize());
+        assertEquals(6, snapshot.findInstance(1).getCompositeSize());
+        assertEquals(6, snapshot.findInstance(2).getCompositeSize());
+        assertEquals(6, snapshot.findInstance(3).getCompositeSize());
     }
 
     public void testTopSortSimpleGraph() {
@@ -145,26 +137,26 @@
                 .addReferences(3, 4, 5)
                 .addReferences(4, 6)
                 .addRoot(1)
-                .getSnapshot();
+                .build();
 
         List<Instance> topSort = TopologicalSort.compute(snapshot.getGCRoots());
         assertEquals(6, topSort.size());
         // Make sure finishing times are computed correctly. A visitor simply collecting nodes as
         // they are expanded will not yield the correct order. The correct invariant for a DAG is:
         // for each directed edge (u,v), topsort(u) < topsort(v).
-        assertTrue(snapshot.findReference(1).getTopologicalOrder() <
-                snapshot.findReference(2).getTopologicalOrder());
-        assertTrue(snapshot.findReference(1).getTopologicalOrder() <
-                snapshot.findReference(3).getTopologicalOrder());
-        assertTrue(snapshot.findReference(2).getTopologicalOrder() <
-                snapshot.findReference(4).getTopologicalOrder());
-        assertTrue(snapshot.findReference(2).getTopologicalOrder() <
-                snapshot.findReference(6).getTopologicalOrder());
-        assertTrue(snapshot.findReference(3).getTopologicalOrder() <
-                snapshot.findReference(4).getTopologicalOrder());
-        assertTrue(snapshot.findReference(3).getTopologicalOrder() <
-                snapshot.findReference(5).getTopologicalOrder());
-        assertTrue(snapshot.findReference(4).getTopologicalOrder() <
-                snapshot.findReference(6).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(1).getTopologicalOrder() <
+                snapshot.findInstance(2).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(1).getTopologicalOrder() <
+                snapshot.findInstance(3).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(2).getTopologicalOrder() <
+                snapshot.findInstance(4).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(2).getTopologicalOrder() <
+                snapshot.findInstance(6).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(3).getTopologicalOrder() <
+                snapshot.findInstance(4).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(3).getTopologicalOrder() <
+                snapshot.findInstance(5).getTopologicalOrder());
+        assertTrue(snapshot.findInstance(4).getTopologicalOrder() <
+                snapshot.findInstance(6).getTopologicalOrder());
     }
 }
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java
index 95bc9b9..ddf4210 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java
@@ -20,9 +20,12 @@
 import com.android.tools.perflib.heap.Snapshot;
 
 import junit.framework.TestCase;
+import sun.misc.IOUtils;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.RandomAccessFile;
+import java.util.Arrays;
 
 public class HprofBufferTest extends TestCase {
 
@@ -73,6 +76,52 @@
         assertFalse(g.exists());
     }
 
+    public void testSubsequenceReads() throws Exception {
+        byte[] fileContents = null;
+        FileInputStream fileInputStream = new FileInputStream(file);
+        try {
+            fileContents = IOUtils.readFully(fileInputStream, -1, true);
+        }
+        finally {
+            fileInputStream.close();
+        }
+
+        MemoryMappedFileBuffer mappedBuffer = new MemoryMappedFileBuffer(file, 8259, 8);
+
+        byte[] buffer = new byte[8190];
+        mappedBuffer.readSubSequence(buffer, 0, 8190);
+        assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 0, 8190)));
+        assertEquals(mappedBuffer.position(), 8190);
+
+        buffer = new byte[8190];
+        mappedBuffer.setPosition(0);
+        mappedBuffer.readSubSequence(buffer, 2000, 8190);
+        assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 2000, 2000 + 8190)));
+        assertEquals(mappedBuffer.position(), 2000 + 8190);
+
+        buffer = new byte[100000];
+        mappedBuffer.setPosition(0);
+        mappedBuffer.readSubSequence(buffer, 19242, 100000);
+        assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 19242, 19242 + 100000)));
+        assertEquals(mappedBuffer.position(), 19242 + 100000);
+
+        buffer = new byte[8259];
+        mappedBuffer.setPosition(0);
+        mappedBuffer.readSubSequence(buffer, 0, 8259);
+        assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 0, 8259)));
+        assertEquals(mappedBuffer.position(), 8259);
+
+        buffer = new byte[8259];
+        mappedBuffer.setPosition(0);
+        mappedBuffer.readSubSequence(buffer, 8259, 8259);
+        assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 8259, 8259 + 8259)));
+        assertEquals(mappedBuffer.position(), 8259 + 8259);
+
+        mappedBuffer.readSubSequence(buffer, 8259, 8259);
+        assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 8259 * 3, 8259 * 4)));
+        assertEquals(mappedBuffer.position(), 8259 * 4);
+    }
+
     private void assertSnapshotCorrect(Snapshot snapshot) {
         assertEquals(11182, snapshot.getGCRoots().size());
         assertEquals(38, snapshot.getHeap(65).getClasses().size());
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java b/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java
index 3533250..055eb26 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java
@@ -41,6 +41,11 @@
     }
 
     @Override
+    public void readSubSequence(byte[] b, int sourceStart, int sourceEnd) {
+        ((ByteBuffer)mBuffer.slice().position(sourceStart)).get(b);
+    }
+
+    @Override
     public char readChar() {
         return mBuffer.getChar();
     }
diff --git a/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java b/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java
index ea4a460..2358a14 100644
--- a/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java
+++ b/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java
@@ -16,8 +16,8 @@
 
 package com.android.tools.perflib.vmtrace.viz;
 
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
 import com.android.tools.perflib.vmtrace.ClockType;
 import com.android.tools.perflib.vmtrace.SearchResult;
diff --git a/rule-api/rule-api.iml b/rule-api/rule-api.iml
index f108533..c616ef9 100644
--- a/rule-api/rule-api.iml
+++ b/rule-api/rule-api.iml
@@ -3,7 +3,7 @@
   <component name="NewModuleRootManager" inherit-compiler-output="true">
     <exclude-output />
     <content url="file://$MODULE_DIR$">
-      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
       <excludeFolder url="file://$MODULE_DIR$/.settings" />
     </content>
     <orderEntry type="sourceFolder" forTests="false" />
@@ -11,5 +11,4 @@
     <orderEntry type="module" module-name="common" exported="" />
     <orderEntry type="module" module-name="layoutlib-api-base" exported="" />
   </component>
-</module>
-
+</module>
\ No newline at end of file
diff --git a/sdk-common/build.gradle b/sdk-common/build.gradle
index f2ac5ed..cc946c4 100644
--- a/sdk-common/build.gradle
+++ b/sdk-common/build.gradle
@@ -1,24 +1,17 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
+apply plugin: 'application'
 
 group = 'com.android.tools'
 archivesBaseName = 'sdk-common'
 version = rootProject.ext.baseVersion
+mainClassName = 'com.android.ide.common.vectordrawable.VdPreview'
 
 dependencies {
     compile project(':base:sdklib')
     compile project(':base:builder-test-api')
     compile project(':base:builder-model')
-    compile 'com.google.code.gson:gson:2.2.4'
 
     testCompile 'junit:junit:4.12'
     testCompile project(':base:sdklib').sourceSets.test.output
diff --git a/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java b/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java
index bf37d7d..0bcecd2 100644
--- a/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java
+++ b/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java
@@ -127,6 +127,17 @@
                 continue;
             }
             assert iso3.length() == 3 : iso3;
+
+            // 3 languages in that spec have deprecated codes which will not work right;
+            // see LocaleFolderDetector#DEPRECATED_CODE for details
+            if (iso2.equals("he")) {
+                iso2 = "iw";
+            } else if (iso2.equals("id")) {
+                iso2 = "in";
+            } else if (iso2.equals("yi")) {
+                iso2 = "ji";
+            }
+
             if (!iso2.isEmpty() && mLanguage2to3.containsKey(iso2)) {
                 // We already know about this one
                 matched++;
@@ -297,6 +308,11 @@
         mLanguage2to3.put("in", "ind"); // proper 3-letter code, but NOT mapping back: should be id
         mLanguage2to3.put("ji", "yid"); // proper 3-letter code, but NOT mapping back: should be yi
         mLanguage2to3.put("iw", "heb"); // proper 3-letter code, but NOT mapping back: should be he
+        // Make sure the forward map (from 3 to 2) is also using the primary (new) code
+        mLanguage3to2.put("ind", "in");
+        mLanguage3to2.put("heb", "iw");
+        mLanguage3to2.put("yid", "ji");
+
 
         mLanguage2Codes = sorted(mLanguage2to3.keySet());
         mLanguage3Codes = sorted(mLanguageName.keySet());
@@ -1430,7 +1446,7 @@
     }
 
     private void generateZones(int level, Collection<TimeZone> zones) {
-        assert zones.size() > 0;
+        assert !zones.isEmpty();
 
         // See if they all map to the same region
         boolean regionsDiffer = false;
diff --git a/sdk-common/sdk-common-base.iml b/sdk-common/sdk-common-base.iml
index 3296017..1c7c2dc 100644
--- a/sdk-common/sdk-common-base.iml
+++ b/sdk-common/sdk-common-base.iml
@@ -17,10 +17,9 @@
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
     <orderEntry type="module" module-name="testutils" scope="TEST" />
     <orderEntry type="module" module-name="builder-model" exported="" />
-    <orderEntry type="library" name="mockito" level="project" />
-    <orderEntry type="library" name="gson" level="project" />
-    <orderEntry type="module" module-name="builder-test-api" />
+    <orderEntry type="library" scope="TEST" name="mockito" level="project" />
+    <orderEntry type="module" module-name="builder-test-api" exported="" />
     <orderEntry type="module" module-name="sdklib-base" exported="" />
     <orderEntry type="library" scope="TEST" name="easymock-tools" level="project" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/FilePosition.java b/sdk-common/src/main/java/com/android/ide/common/blame/FilePosition.java
deleted file mode 100644
index 76c3d94..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/FilePosition.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame;
-
-import com.android.annotations.NonNull;
-import com.google.common.base.Objects;
-
-import java.io.File;
-
-/**
- * An immutable position in a text file with a reference to that file.
- */
-public class FilePosition extends SourcePosition {
-
-    private final File sourceFile;
-
-    public FilePosition(@NonNull File sourceFile, @NonNull SourcePosition position) {
-        super(position);
-        this.sourceFile = sourceFile;
-    }
-
-    public static FilePosition wholeFile(@NonNull File sourceFile) {
-        return new FilePosition(sourceFile, SourcePosition.UNKNOWN);
-    }
-
-    public File getSourceFile() {
-        return sourceFile;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append(sourceFile.getAbsoluteFile());
-        if (getStartLine() != -1) {
-            stringBuilder.append(':');
-            stringBuilder.append(super.toString());
-        }
-        return stringBuilder.toString();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof FilePosition)) {
-            return false;
-        }
-        FilePosition other = (FilePosition) obj;
-
-        return other.getSourceFile().getAbsolutePath().equals(getSourceFile().getAbsolutePath()) &&
-                super.equals((SourcePosition) other);
-
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(super.hashCode(), sourceFile.getAbsolutePath());
-    }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/MessageJsonSerializer.java b/sdk-common/src/main/java/com/android/ide/common/blame/MessageJsonSerializer.java
new file mode 100644
index 0000000..2bb4652
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/MessageJsonSerializer.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.EnumHashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Class to handle json serialization and deserialization of messages.
+ *
+ * Reads json objects of the form:
+ *
+ * <pre>
+ * {
+ *     "kind":"ERROR",
+ *     "text":"errorText",
+ *     "original":"unparsed error text: Error in some intermediate file",
+ *     "sources": [{
+ *         "file":"/path/to/source.java",
+ *         "position":{
+ *             "startLine":1,
+ *             "startColumn":2,
+ *             "startOffset":3,
+ *             "endLine":4,
+ *             "endColumn":5,
+ *             "endOffset":6
+ *         }
+ *     }]
+ * }</pre>
+ *
+ * All fields, other than text, may be omitted. They have the following defaults:
+ *
+ * <table>
+ *     <tr><th>Property</th>        <th>Default</th>             <th>Notes</th></tr>
+ *     <tr><td>kind (ERROR, WARNING,
+ *             INFO, UNKNOWN)</td>  <td>UNKNOWN</td>             <td></td></tr>
+ *     <tr><td>text</td>            <td><i>Empty String</i></td> <td>Should not be omitted.</td></tr>
+ *     <tr><td>file (Absolute)</td> <td>{} <i>[unknown]</i></td> <td>See {@link SourceFileJsonTypeAdapter}</td></tr>
+ *     <tr><td>position</td>        <td>UNKNOWN</td>             <td></td></tr>
+ *     <tr><td>startLine,
+ *             startColumn,
+ *             startOffset</td>     <td>-1 <i>[unknown]</i></td> <td rowspan="2">0-based</td></tr>
+ *     <tr><td>endLine,
+ *             endColumn,
+ *             endOffset</td>       <td>startLine, startColumn,
+ *                                      startOffset</td></tr>
+ * </table>
+ *
+ * <h4>Notes</h4>
+ * <ul>
+ *     <li>Offset need not be included, if needed by the consumer of the message it can
+ *         be derived from the file, line and column.</li>
+ *     <li>If line is included and column is not the message will be considered to apply
+ *         to the whole line.</li>
+ *     <li>A message can have multiple sources.</li>
+ * </ul>
+ * It also can read legacy serialized objects of the form:
+ *
+ * <pre>{
+ *     "kind":"ERROR",
+ *     "text":"errorText",
+ *     "sourcePath": "/path/to/source.java",
+ *     "position":{
+ *         "startLine":1,
+ *         "startColumn":2,
+ *         "startOffset":3,
+ *         "endLine":4,
+ *         "endColumn":5,
+ *         "endOffset":6
+ *     }
+ * }</pre>
+ *
+ * These serializers are implemented using the lower-level TypeAdapter gson API which gives much
+ * more control and allow changes to be made without breaking backward compatibility.
+ */
+public class MessageJsonSerializer extends TypeAdapter<Message> {
+
+    private static final String KIND = "kind";
+
+    private static final String TEXT = "text";
+
+    private static final String SOURCE_FILE_POSITIONS = "sources";
+
+    private static final String RAW_MESSAGE = "original";
+
+    private static final String LEGACY_SOURCE_PATH = "sourcePath";
+
+    private static final String LEGACY_POSITION = "position";
+
+    private static final BiMap<Message.Kind, String> KIND_STRING_ENUM_MAP;
+
+    static {
+        EnumHashBiMap<Message.Kind, String> map = EnumHashBiMap.create(Message.Kind.class);
+        map.put(Message.Kind.ERROR, "error");
+        map.put(Message.Kind.WARNING, "warning");
+        map.put(Message.Kind.INFO, "info");
+        map.put(Message.Kind.STATISTICS, "statistics");
+        map.put(Message.Kind.UNKNOWN, "unknown");
+        map.put(Message.Kind.SIMPLE, "simple");
+        KIND_STRING_ENUM_MAP = Maps.unmodifiableBiMap(map);
+    }
+
+    private final SourceFilePositionJsonSerializer mSourceFilePositionTypeAdapter;
+    private final SourcePositionJsonTypeAdapter mSourcePositionTypeAdapter;
+
+    public MessageJsonSerializer() {
+        mSourceFilePositionTypeAdapter = new SourceFilePositionJsonSerializer();
+        mSourcePositionTypeAdapter = mSourceFilePositionTypeAdapter.getSourcePositionTypeAdapter();
+    }
+
+    @Override
+    public void write(JsonWriter out, Message message) throws IOException {
+        out.beginObject()
+                .name(KIND).value(KIND_STRING_ENUM_MAP.get(message.getKind()))
+                .name(TEXT).value(message.getText())
+                .name(SOURCE_FILE_POSITIONS).beginArray();
+        for (SourceFilePosition position : message.getSourceFilePositions()) {
+            mSourceFilePositionTypeAdapter.write(out, position);
+        }
+        out.endArray();
+        if (!message.getRawMessage().equals(message.getText())) {
+            out.name(RAW_MESSAGE).value(message.getRawMessage());
+        }
+        out.endObject();
+    }
+
+    @Override
+    public Message read(JsonReader in) throws IOException {
+        in.beginObject();
+        Message.Kind kind = Message.Kind.UNKNOWN;
+        String text = "";
+        String rawMessage = null;
+        ImmutableList.Builder<SourceFilePosition> positions =
+                new ImmutableList.Builder<SourceFilePosition>();
+        SourceFile legacyFile = SourceFile.UNKNOWN;
+        SourcePosition legacyPosition = SourcePosition.UNKNOWN;
+        while (in.hasNext()) {
+            String name = in.nextName();
+            if (name.equals(KIND)) {
+                //noinspection StringToUpperCaseOrToLowerCaseWithoutLocale
+                Message.Kind theKind = KIND_STRING_ENUM_MAP.inverse()
+                        .get(in.nextString().toLowerCase());
+                kind = (theKind != null) ? theKind : Message.Kind.UNKNOWN;
+            } else if (name.equals(TEXT)) {
+                text = in.nextString();
+            } else if (name.equals(RAW_MESSAGE)) {
+                rawMessage = in.nextString();
+            } else if (name.equals(SOURCE_FILE_POSITIONS)) {
+                switch (in.peek()) {
+                    case BEGIN_ARRAY:
+                        in.beginArray();
+                        while(in.hasNext()) {
+                            positions.add(mSourceFilePositionTypeAdapter.read(in));
+                        }
+                        in.endArray();
+                        break;
+                    case BEGIN_OBJECT:
+                        positions.add(mSourceFilePositionTypeAdapter.read(in));
+                        break;
+                    default:
+                        in.skipValue();
+                        break;
+                }
+            } else if (name.equals(LEGACY_SOURCE_PATH)) {
+                legacyFile = new SourceFile(new File(in.nextString()));
+            } else if (name.equals(LEGACY_POSITION)) {
+                legacyPosition = mSourcePositionTypeAdapter.read(in);
+            } else {
+                in.skipValue();
+            }
+        }
+        in.endObject();
+
+        if (legacyFile != SourceFile.UNKNOWN || legacyPosition != SourcePosition.UNKNOWN) {
+            positions.add(new SourceFilePosition(legacyFile, legacyPosition));
+        }
+        if (rawMessage == null) {
+            rawMessage = text;
+        }
+        ImmutableList<SourceFilePosition> sourceFilePositions = positions.build();
+        if (!sourceFilePositions.isEmpty()) {
+            return new Message(kind, text, rawMessage, sourceFilePositions);
+        } else {
+            return new Message(kind, text, rawMessage, ImmutableList.of(SourceFilePosition.UNKNOWN));
+        }
+    }
+
+    public static void registerTypeAdapters(GsonBuilder builder) {
+        builder.registerTypeAdapter(SourceFile.class, new SourceFileJsonTypeAdapter());
+        builder.registerTypeAdapter(SourcePosition.class, new SourcePositionJsonTypeAdapter());
+        builder.registerTypeAdapter(SourceFilePosition.class,
+                new SourceFilePositionJsonSerializer());
+        builder.registerTypeAdapter(Message.class, new MessageJsonSerializer());
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/MessageReceiver.java b/sdk-common/src/main/java/com/android/ide/common/blame/MessageReceiver.java
new file mode 100644
index 0000000..1b6ab98
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/MessageReceiver.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.android.annotations.NonNull;
+
+public interface MessageReceiver {
+    void receiveMessage(@NonNull Message m);
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/ParsingProcessOutputHandler.java b/sdk-common/src/main/java/com/android/ide/common/blame/ParsingProcessOutputHandler.java
new file mode 100644
index 0000000..c7fd993
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/ParsingProcessOutputHandler.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.blame.parser.ToolOutputParser;
+import com.android.ide.common.process.BaseProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.utils.ILogger;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * A ProcessOutputHandler that runs ToolOutputParsers over the process output and emits Messages.
+ */
+public class ParsingProcessOutputHandler extends BaseProcessOutputHandler {
+
+    @NonNull
+    private final ToolOutputParser mErrorToolOutputParser;
+
+    @NonNull
+    private final ToolOutputParser mStdoutToolOutputParser;
+
+    @NonNull
+    private final List<MessageReceiver> mMessageReceivers;
+
+    /**
+     * Create a ParsingProcessOutputHandler.
+     *
+     * @param errorToolOutputParser the {@link ToolOutputParser} to use for process output sent
+     *                              to stderr,
+     * @param stdoutToolOutputParser the ToolOutputParser to use for process output to sent to
+     *                               stdout.
+     * @param messageReceivers the message receivers to notify for each message,
+     */
+    public ParsingProcessOutputHandler(
+            @NonNull ToolOutputParser errorToolOutputParser,
+            @NonNull ToolOutputParser stdoutToolOutputParser,
+            @NonNull MessageReceiver... messageReceivers) {
+        mErrorToolOutputParser = errorToolOutputParser;
+        mStdoutToolOutputParser = stdoutToolOutputParser;
+        mMessageReceivers = ImmutableList.copyOf(messageReceivers);
+    }
+
+    /**
+     * Create a ParsingProcessOutputHandler.
+     *
+     * @param toolOutputParser the {@link ToolOutputParser} to use for process output sent to
+     *                         stderr and stdout,
+     * @param messageReceivers the message receivers to notify for each message,
+     */
+    public ParsingProcessOutputHandler(
+            @NonNull ToolOutputParser toolOutputParser,
+            @NonNull MessageReceiver... messageReceivers) {
+        this(toolOutputParser, toolOutputParser, messageReceivers);
+    }
+
+    @Override
+    public void handleOutput(@NonNull ProcessOutput processOutput) throws ProcessException {
+        if (!(processOutput instanceof BaseProcessOutput)) {
+            throw new IllegalArgumentException("processOutput was not created by this handler.");
+        }
+        BaseProcessOutput impl = (BaseProcessOutput) processOutput;
+        String stdout = impl.getStandardOutputAsString();
+        if (!stdout.isEmpty()) {
+            outputMessages(mStdoutToolOutputParser.parseToolOutput(stdout));
+        }
+        String stderr = impl.getErrorOutputAsString();
+        if (!stderr.isEmpty()) {
+            outputMessages(mErrorToolOutputParser.parseToolOutput(stderr));
+        }
+    }
+
+    private void outputMessages(List<Message> messages) {
+        for (Message message: messages) {
+            for (MessageReceiver messageReceiver: mMessageReceivers) {
+                messageReceiver.receiveMessage(message);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/SourceFileJsonTypeAdapter.java b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFileJsonTypeAdapter.java
new file mode 100644
index 0000000..3794c57
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFileJsonTypeAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.google.common.base.Strings;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * JsonSerializer and Deserializer for {@link SourceFile}.
+ *
+ * The JsonDeserialiser accepts either a string of the file path or a json object of the form
+ * <pre>{
+ *     "path":"/path/to/file.java",
+ *     "description": "short human-readable description"
+ * }</pre> where both
+ * properties are optionally present, so unknown is represented by the empty object.
+ */
+public class SourceFileJsonTypeAdapter extends TypeAdapter<SourceFile> {
+
+    private static final String PATH = "path";
+
+    private static final String DESCRIPTION = "description";
+
+    @Override
+    public void write(JsonWriter out, SourceFile src) throws IOException {
+        File file = src.getSourceFile();
+        String description = src.getDescription();
+
+        if (description == null && file != null) {
+            out.value(file.getAbsolutePath());
+            return;
+        }
+
+        out.beginObject();
+        if (description != null) {
+            out.name(DESCRIPTION).value(description);
+        }
+        if (file != null) {
+            out.name(PATH).value(file.getAbsolutePath());
+        }
+        out.endObject();
+    }
+
+    @Override
+    public SourceFile read(JsonReader in) throws IOException {
+        switch (in.peek()) {
+            case BEGIN_OBJECT:
+                in.beginObject();
+                String filePath = null;
+                String description = null;
+                while (in.hasNext()) {
+                    String name = in.nextName();
+                    if (name.equals(PATH)) {
+                        filePath = in.nextString();
+                    } else if (DESCRIPTION.equals(name)) {
+                        description = in.nextString();
+                    } else {
+                        in.skipValue();
+                    }
+                }
+                in.endObject();
+                if (!Strings.isNullOrEmpty(filePath)) {
+                    File file = new File(filePath);
+                    if (!Strings.isNullOrEmpty(description)) {
+                        return new SourceFile(file, description);
+                    } else {
+                        return new SourceFile(file);
+                    }
+                } else {
+                    if (!Strings.isNullOrEmpty(description)) {
+                        return new SourceFile(description);
+                    } else {
+                        return SourceFile.UNKNOWN;
+                    }
+                }
+            case STRING:
+                String fileName = in.nextString();
+                if (Strings.isNullOrEmpty(fileName)) {
+                    return SourceFile.UNKNOWN;
+                }
+                return new SourceFile(new File(fileName));
+            default:
+                return SourceFile.UNKNOWN;
+        }
+
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/SourceFilePositionJsonSerializer.java b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFilePositionJsonSerializer.java
new file mode 100644
index 0000000..58e55f1
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFilePositionJsonSerializer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+public class SourceFilePositionJsonSerializer extends TypeAdapter<SourceFilePosition> {
+
+    private static final String POSITION = "position";
+
+    private static final String FILE = "file";
+
+    private final SourceFileJsonTypeAdapter mSourceFileJsonTypeAdapter;
+    private final SourcePositionJsonTypeAdapter mSourcePositionJsonTypeAdapter;
+
+    public SourceFilePositionJsonSerializer() {
+        mSourcePositionJsonTypeAdapter = new SourcePositionJsonTypeAdapter();
+        mSourceFileJsonTypeAdapter = new SourceFileJsonTypeAdapter();
+    }
+
+    @Override
+    public SourceFilePosition read(JsonReader in) throws IOException {
+        in.beginObject();
+        SourceFile file = SourceFile.UNKNOWN;
+        SourcePosition position = SourcePosition.UNKNOWN;
+        while(in.hasNext()) {
+            String name = in.nextName();
+            if (name.equals(FILE)) {
+                file = mSourceFileJsonTypeAdapter.read(in);
+            } else if (name.equals(POSITION)) {
+                position = mSourcePositionJsonTypeAdapter.read(in);
+            } else {
+                in.skipValue();
+            }
+        }
+        in.endObject();
+        return new SourceFilePosition(file, position);
+    }
+
+    @Override
+    public void write(JsonWriter out, SourceFilePosition src) throws IOException {
+        out.beginObject();
+        SourceFile sourceFile = src.getFile();
+        if (!sourceFile.equals(SourceFile.UNKNOWN)) {
+            out.name(FILE);
+            mSourceFileJsonTypeAdapter.write(out, sourceFile);
+        }
+        SourcePosition position = src.getPosition();
+        if (!position.equals(SourcePosition.UNKNOWN)) {
+            out.name(POSITION);
+            mSourcePositionJsonTypeAdapter.write(out, position);
+        }
+        out.endObject();
+    }
+
+    /* package */ SourcePositionJsonTypeAdapter getSourcePositionTypeAdapter() {
+        return mSourcePositionJsonTypeAdapter;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameAwareLoggedProcessOutputHandler.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameAwareLoggedProcessOutputHandler.java
deleted file mode 100644
index ebad245..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameAwareLoggedProcessOutputHandler.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.process.LoggedProcessOutputHandler;
-import com.android.utils.ILogger;
-
-public class BlameAwareLoggedProcessOutputHandler extends LoggedProcessOutputHandler {
-    public BlameAwareLoggedProcessOutputHandler(@NonNull ILogger logger, @NonNull GradleMessageRewriter.ErrorFormatMode errorFormatMode) {
-        super(new BlameRewritingLogger(logger, errorFormatMode));
-    }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameRewritingLogger.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameRewritingLogger.java
deleted file mode 100644
index caba3ce..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameRewritingLogger.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.blame.parser.ToolOutputParser;
-import com.android.ide.common.blame.parser.aapt.AaptOutputParser;
-import com.android.utils.ILogger;
-
-public class BlameRewritingLogger implements ILogger {
-
-    private final ILogger mLogger;
-    private final GradleMessageRewriter mGradleMessageRewriter;
-
-    public BlameRewritingLogger(@NonNull ILogger logger, @NonNull GradleMessageRewriter.ErrorFormatMode errorFormatMode) {
-        this.mLogger = logger;
-        ToolOutputParser parser = new ToolOutputParser(new AaptOutputParser(), logger);
-        mGradleMessageRewriter = new GradleMessageRewriter(parser, errorFormatMode);
-    }
-
-    @Override
-    public void error(@Nullable Throwable t, @Nullable String msgFormat, Object... args) {
-        mLogger.error(t, mGradleMessageRewriter.rewriteMessages(msgFormat), args);
-    }
-
-    @Override
-    public void warning(@NonNull String msgFormat, Object... args) {
-        mLogger.warning(mGradleMessageRewriter.rewriteMessages(msgFormat), args);
-    }
-
-    @Override
-    public void info(@NonNull String msgFormat, Object... args) {
-        mLogger.info(msgFormat, args);
-    }
-
-    @Override
-    public void verbose(@NonNull String msgFormat, Object... args) {
-        mLogger.verbose(msgFormat, args);
-    }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessage.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessage.java
deleted file mode 100644
index e0d03d3..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessage.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.blame.SourcePosition;
-import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Message produced by tools invoked when building an Android project.
- */
-public class GradleMessage {
-
-    @SerializedName("kind")
-    @NonNull
-    private final Kind mKind;
-
-    @SerializedName("text")
-    @NonNull
-    private final String mText;
-
-    @SerializedName("sourcePath")
-    @Nullable
-    private final String mSourcePath;
-
-    @SerializedName("position")
-    @NonNull
-    private final SourcePosition mPosition;
-
-    @SerializedName("original")
-    @NonNull
-    private final String mOriginal;
-
-    public GradleMessage(@NonNull Kind kind, @NonNull String text) {
-        this(kind, text, null /* sourcePath */, SourcePosition.UNKNOWN, text);
-    }
-
-
-    public GradleMessage(@NonNull Kind kind,
-                         @NonNull String text,
-                         @Nullable String sourcePath,
-                         @NonNull SourcePosition position,
-                         @NonNull String original) {
-        mKind = kind;
-        mText = text;
-        mSourcePath = sourcePath;
-        mPosition = position;
-        mOriginal = original;
-    }
-
-    public GradleMessage(@NonNull Kind kind, @NonNull String text, @Nullable String sourcePath, int line, int column) {
-        this(kind, text, sourcePath, new SourcePosition(line, column, -1), text);
-    }
-
-    @NonNull
-    public Kind getKind() {
-        return mKind;
-    }
-
-    @NonNull
-    public String getText() {
-        return mText;
-    }
-
-    @Nullable
-    public String getSourcePath() {
-        return mSourcePath;
-    }
-
-    public int getLineNumber() {
-        return mPosition.getStartLine();
-    }
-
-    public int getColumn() {
-        return mPosition.getStartColumn();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        GradleMessage that = (GradleMessage) o;
-
-        return Objects.equal(mPosition, that.mPosition) &&
-                mKind == that.mKind &&
-                Objects.equal(mSourcePath, that.mSourcePath) &&
-                Objects.equal(mText, that.mText);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(mPosition, mKind, mSourcePath, mText);
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[" +
-                "kind=" + mKind +
-                ", text=\"" + mText + '\"' +
-                ", sourcePath=" + mSourcePath +
-                ", position=" + mPosition.toString() +
-                ']';
-    }
-
-    public SourcePosition getPosition() {
-        return mPosition;
-    }
-
-    public String getOriginal() {
-        return mOriginal;
-    }
-
-    public enum Kind {
-        ERROR, WARNING, INFO, STATISTICS, SIMPLE;
-
-        @Nullable
-        public static Kind findIgnoringCase(@NonNull String s) {
-            for (Kind kind : values()) {
-                if (kind.toString().equalsIgnoreCase(s)) {
-                    return kind;
-                }
-            }
-            return null;
-        }
-    }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessageRewriter.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessageRewriter.java
deleted file mode 100644
index 38a7f18..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessageRewriter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.SourcePositionJsonTypeAdapter;
-import com.android.ide.common.blame.parser.ToolOutputParser;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-import java.util.List;
-
-public class GradleMessageRewriter {
-
-    public static final String STDOUT_ERROR_TAG = "AGPBI: ";
-
-    public enum ErrorFormatMode {
-        MACHINE_PARSABLE, HUMAN_READABLE
-    }
-
-    private final ToolOutputParser mParser;
-    private final Gson mGson;
-    private final ErrorFormatMode mErrorFormatMode;
-
-    public GradleMessageRewriter(ToolOutputParser parser, ErrorFormatMode errorFormatMode) {
-        mParser = parser;
-        mErrorFormatMode = errorFormatMode;
-        mGson = createGson();
-    }
-
-    public String rewriteMessages(@NonNull String originalMessage) {
-        List<GradleMessage> messages = mParser.parseToolOutput(originalMessage);
-
-        if (messages.isEmpty()) {
-            return originalMessage;
-        }
-
-        StringBuilder errorStringBuilder = new StringBuilder();
-        for (GradleMessage message: messages) {
-            if (mErrorFormatMode == ErrorFormatMode.HUMAN_READABLE) {
-                if (message.getPosition() != null && message.getPosition().getStartLine() != -1) {
-                    errorStringBuilder.append(" Position ");
-                    errorStringBuilder.append(message.getPosition().toString());
-                    errorStringBuilder.append(" : ");
-
-                }
-                errorStringBuilder.append(message.getText())
-                        .append("\n");
-
-            } else {
-                errorStringBuilder.append(STDOUT_ERROR_TAG)
-                    .append(mGson.toJson(message)).append("\n");
-            }
-        }
-        return errorStringBuilder.toString();
-    }
-
-    private static Gson createGson() {
-        GsonBuilder gsonBuilder = new GsonBuilder();
-        gsonBuilder.registerTypeAdapter(SourcePosition.class,
-                new SourcePositionJsonTypeAdapter());
-        return gsonBuilder.create();
-    }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java
index 977eae8..391757d 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java
@@ -16,10 +16,8 @@
 package com.android.ide.common.blame.parser;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.SourcePositionJsonTypeAdapter;
-import com.android.ide.common.blame.output.GradleMessage;
-import com.android.ide.common.blame.output.GradleMessageRewriter;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.MessageJsonSerializer;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.utils.ILogger;
 import com.google.gson.Gson;
@@ -31,21 +29,25 @@
 import java.util.regex.Pattern;
 
 /**
- * Reconstruct GradleMessages that were parsed by the gradle plugin.
+ * Reconstruct Messages that were parsed by the gradle plugin.
  */
 public class JsonEncodedGradleMessageParser implements PatternAwareOutputParser {
 
+    // Android gradle parsed build issue. A prefix that is very unlikely to occur elsewhere to
+    // signify the rest of the line should be parsed as a json encoded {@link Message},
+    public static final String STDOUT_ERROR_TAG = "AGPBI: ";
+
     /**
      * The errors are of the form:
      * <pre>AGPBI: {"kind":"ERROR","text":"Nothing"...}</pre>
      */
     private static final Pattern MSG_PATTERN = Pattern.compile("^" + Pattern.quote(
-            GradleMessageRewriter.STDOUT_ERROR_TAG) + "(.*)$");
+            STDOUT_ERROR_TAG) + "(.*)$");
 
     @Override
     public boolean parse(@NonNull String line,
             @NonNull OutputLineReader reader,
-            @NonNull List<GradleMessage> messages,
+            @NonNull List<Message> messages,
             @NonNull ILogger logger) throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -57,11 +59,10 @@
         }
 
         GsonBuilder gsonBuilder = new GsonBuilder();
-        gsonBuilder.registerTypeAdapter(SourcePosition.class,
-                new SourcePositionJsonTypeAdapter());
+        MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
         Gson gson = gsonBuilder.create();
         try {
-            GradleMessage msg = gson.fromJson(json, GradleMessage.class);
+            Message msg = gson.fromJson(json, Message.class);
             messages.add(msg);
             return true;
         } catch (JsonParseException e) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/LegacyNdkOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/LegacyNdkOutputParser.java
new file mode 100644
index 0000000..36f802a
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/LegacyNdkOutputParser.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame.parser;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.android.ide.common.blame.parser.util.OutputLineReader;
+import com.android.utils.ILogger;
+import com.android.utils.SdkUtils;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Parses output from the legacy NDK support.
+ */
+public class LegacyNdkOutputParser implements PatternAwareOutputParser {
+
+    private static final String FROM = "from";
+    private static final String UNKNOWN_MSG_PREFIX1 = "In file included " + FROM;
+    private static final String UNKNOWN_MSG_PREFIX2 = "                 " + FROM;
+
+    private static final char COLON = ':';
+
+    @Override
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
+            @NonNull List<Message> messages, @NonNull ILogger logger)
+            throws ParsingFailedException {
+        // Parses unknown message
+        if (line.startsWith(UNKNOWN_MSG_PREFIX1) || line.startsWith(UNKNOWN_MSG_PREFIX2)) {
+            int fromIndex = line.indexOf(FROM);
+            String unknownMsgCause = line.substring(0, fromIndex).trim();
+            unknownMsgCause = "(Unknown) " + unknownMsgCause;
+            String coordinates = line.substring(fromIndex + FROM.length()).trim();
+            if (!coordinates.isEmpty()) {
+                int colonIndex1 = line.indexOf(COLON);
+                if (colonIndex1 == 1) { // drive letter (Windows)
+                    coordinates = coordinates.substring(colonIndex1 + 1);
+                }
+                if (coordinates.endsWith(",") || coordinates.endsWith(":")) {
+                    coordinates = coordinates.substring(0, coordinates.length() - 1);
+                }
+
+                List<String> segments = Splitter.on(COLON).splitToList(coordinates);
+                if (segments.size() == 3) {
+                    String pathname = segments.get(0);
+                    File file = new File(pathname);
+                    int lineNumber = 0;
+                    try {
+                        lineNumber = Integer.parseInt(segments.get(1));
+                    }
+                    catch (NumberFormatException ignore) {
+                    }
+                    int column = 0;
+                    try {
+                        column = Integer.parseInt(segments.get(2));
+                    }
+                    catch (NumberFormatException ignore) {
+                    }
+                    SourceFilePosition position = new SourceFilePosition(file,
+                            new SourcePosition(lineNumber - 1, column - 1, -1));
+                    Message message = new Message(Message.Kind.INFO, unknownMsgCause.trim(), position);
+                    if (!messages.contains(message)) {
+                        // There may be a few duplicate "unknown" messages
+                        addMessage(message, messages);
+                    }
+                }
+            }
+            return true;
+        }
+
+        // Parses unresolved include.
+        int colonIndex1 = line.indexOf(COLON);
+        if (colonIndex1 == 1) { // drive letter (Windows)
+            colonIndex1 = line.indexOf(COLON, colonIndex1 + 1);
+        }
+        if (colonIndex1 >= 0) { // looks like found something like a file path.
+            String part1 = line.substring(0, colonIndex1).trim();
+
+            int colonIndex2 = line.indexOf(COLON, colonIndex1 + 1);
+            if (colonIndex2 >= 0) {
+                File file = new File(part1);
+                if (!file.isFile()) {
+                    // the part one is not a file path.
+                    return false;
+                }
+                try {
+                    int lineNumber = Integer.parseInt(
+                            line.substring(colonIndex1 + 1, colonIndex2).trim()); // 1-based.
+
+                    int colonIndex3 = line.indexOf(COLON, colonIndex2 + 1);
+                    if (colonIndex1 >= 0) {
+                        int column = Integer.parseInt(
+                                line.substring(colonIndex2 + 1, colonIndex3).trim());
+
+                        int colonIndex4 = line.indexOf(COLON, colonIndex3 + 1);
+                        if (colonIndex4 >= 0) {
+                            Message.Kind kind = Message.Kind.INFO;
+
+                            String severity =
+                                    line.substring(colonIndex3 + 1,
+                                            colonIndex4).toLowerCase(Locale.getDefault()).trim();
+                            if (severity.endsWith("error")) {
+                                kind = Message.Kind.ERROR;
+                            } else if (severity.endsWith("warning")) {
+                                kind = Message.Kind.WARNING;
+                            }
+                            String text = line.substring(colonIndex4 + 1).trim();
+                            List<String> messageList = Lists.newArrayList();
+                            messageList.add(text);
+                            String prevLine = null;
+                            do {
+                                String nextLine = reader.readLine();
+                                if (nextLine == null) {
+                                    return false;
+                                }
+                                if (nextLine.trim().equals("^")) {
+                                    String messageEnd = reader.readLine();
+
+                                    while (isMessageEnd(messageEnd)) {
+                                        messageList.add(messageEnd.trim());
+                                        messageEnd = reader.readLine();
+                                    }
+
+                                    if (messageEnd != null) {
+                                        reader.pushBack(messageEnd);
+                                    }
+                                    break;
+                                }
+                                if (prevLine != null) {
+                                    messageList.add(prevLine);
+                                }
+                                prevLine = nextLine;
+                            } while (true);
+
+                            if (column >= 0) {
+                                messageList = convertMessages(messageList);
+                                StringBuilder buf = new StringBuilder();
+                                for (String m : messageList) {
+                                    if (buf.length() > 0) {
+                                        buf.append(SdkUtils.getLineSeparator());
+                                    }
+                                    buf.append(m);
+                                }
+                                Message msg = new Message(kind, buf.toString(),
+                                        new SourceFilePosition(file,
+                                                new SourcePosition(lineNumber - 1, column - 1, -1)));
+                                if (!messages.contains(msg)) {
+                                    addMessage(msg, messages);
+                                }
+                                return true;
+                            }
+                        }
+
+                    }
+                } catch (NumberFormatException ignored) {
+                }
+            }
+        }
+        return false;
+    }
+
+    private static void addMessage(@NonNull Message message, @NonNull List<Message> messages) {
+        boolean duplicatesPrevious = false;
+        int messageCount = messages.size();
+        if (messageCount > 0) {
+            Message lastMessage = messages.get(messageCount - 1);
+            duplicatesPrevious = lastMessage.equals(message);
+        }
+        if (!duplicatesPrevious) {
+            messages.add(message);
+        }
+    }
+
+    private static boolean isMessageEnd(@Nullable String line) {
+        return line != null && !line.isEmpty() && Character.isWhitespace(line.charAt(0));
+    }
+
+    @NonNull
+    private static List<String> convertMessages(@NonNull List<String> messages) {
+        if (messages.size() <= 1) {
+            return messages;
+        }
+        final String line0 = messages.get(0);
+        final String line1 = messages.get(1);
+        final int colonIndex = line1.indexOf(':');
+        if (colonIndex > 0) {
+            String part1 = line1.substring(0, colonIndex).trim();
+            // jikes
+            if ("symbol".equals(part1)) {
+                String symbol = line1.substring(colonIndex + 1).trim();
+                messages.remove(1);
+                if (messages.size() >= 2) {
+                    messages.remove(1);
+                }
+                messages.set(0, line0 + " " + symbol);
+            }
+        }
+        return messages;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java
index b6f0a9f..70b6c57 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.utils.ILogger;
 
@@ -32,13 +32,13 @@
      *
      * @param line     the line to parse.
      * @param reader   passed in case this parser needs to parse more lines in order to create a
-     *                 {@code GradleMessage}.
+     *                 {@code Message}.
      * @param messages stores the messages created during parsing, if any.
      * @return {@code true} if this parser was able to parser the given line, {@code false}
      * otherwise.
      * @throws ParsingFailedException if something goes wrong (e.g. malformed output.)
      */
     boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
-            @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+            @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException;
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java
index 69a2c5e..4cc42d2 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java
@@ -17,10 +17,10 @@
 package com.android.ide.common.blame.parser;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.utils.ILogger;
-import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
@@ -50,14 +50,14 @@
         mLogger = logger;
     }
 
-    public List<GradleMessage> parseToolOutput(@NonNull String output) {
+    public List<Message> parseToolOutput(@NonNull String output) {
         OutputLineReader outputReader = new OutputLineReader(output);
 
         if (outputReader.getLineCount() == 0) {
             return Collections.emptyList();
         }
 
-        List<GradleMessage> messages = Lists.newArrayList();
+        List<Message> messages = Lists.newArrayList();
         String line;
         while ((line = outputReader.readLine()) != null) {
             if (line.isEmpty()) {
@@ -78,7 +78,7 @@
             if (handled) {
                 int messageCount = messages.size();
                 if (messageCount > 0) {
-                    GradleMessage last = messages.get(messageCount - 1);
+                    Message last = messages.get(messageCount - 1);
                     if (last.getText().contains("Build cancelled")) {
                         // Build was cancelled, just quit. Extra messages are just confusing noise.
                         break;
@@ -86,11 +86,11 @@
                 }
             }
             else {
-                // If none of the standard parsers recognize the input, include it as info such
+                // If none of the standard parsers recogni ze the input, include it as info such
                 // that users don't miss potentially vital output such as gradle plugin exceptions.
                 // If there is predictable useless input we don't want to appear here, add a custom
                 // parser to digest it.
-                messages.add(new GradleMessage(GradleMessage.Kind.SIMPLE, line));
+                messages.add(new Message(Message.Kind.SIMPLE, line, SourceFilePosition.UNKNOWN));
             }
         }
         return messages;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java
index 0e5bf94..dae8779 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.ide.common.blame.parser.PatternAwareOutputParser;
@@ -47,7 +47,7 @@
     };
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger) {
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger) {
         for (AbstractAaptOutputParser parser : PARSERS) {
             try {
                 if (parser.parse(line, reader, messages, logger)) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java
index 60d801b..e30eae1 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java
@@ -24,13 +24,13 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
-import com.android.ide.common.blame.FilePosition;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
 import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.output.GradleMessage;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.ide.common.blame.parser.PatternAwareOutputParser;
-import com.android.ide.common.res2.MergedResourceWriter;
 import com.android.resources.ResourceFolderType;
 import com.android.utils.ILogger;
 import com.android.utils.SdkUtils;
@@ -190,8 +190,8 @@
         int endResultOffset = resultOffset + (second != null ? second.length() : first.length());
         int endLineNumber = document.lineNumber(endResultOffset);
         int endLineOffset = document.lineOffset((endLineNumber));
-        return new SourcePosition(startLineNumber, resultOffset - startLineOffset + 1, resultOffset,
-                endLineNumber, endResultOffset - endLineOffset + 1, endResultOffset);
+        return new SourcePosition(startLineNumber, resultOffset - startLineOffset, resultOffset,
+                                  endLineNumber, endResultOffset - endLineOffset, endResultOffset);
     }
 
     @Nullable
@@ -231,8 +231,8 @@
                 break;
             }
         }
-        return new SourcePosition(locationLine, resultOffset - lineOffset + 1, resultOffset,
-                locationLine, endResultOffset - lineOffset + 1, endResultOffset);
+        return new SourcePosition(locationLine, resultOffset - lineOffset, resultOffset,
+                locationLine, endResultOffset - lineOffset, endResultOffset);
     }
 
     @Nullable
@@ -279,11 +279,11 @@
      * "string/group2_string" it will locate an element {@code <string name="group2_string">} or
      * {@code <item type="string" name="group2_string"}
      */
-    public static int findResourceLine(@NonNull File file, @NonNull String key, @NonNull ILogger logger) {
+    public static SourcePosition findResourceLine(@NonNull File file, @NonNull String key, @NonNull ILogger logger) {
         int slash = key.indexOf('/');
         if (slash == -1) {
             assert false : slash; // invalid key format
-            return -1;
+            return SourcePosition.UNKNOWN;
         }
 
         final String type = key.substring(0, slash);
@@ -296,26 +296,26 @@
      * Locates a resource value declaration in a given file and returns the corresponding line
      * number, or -1 if not found.
      */
-    public static int findValueDeclaration(@NonNull File file, @NonNull final String type,
+    public static SourcePosition findValueDeclaration(@NonNull File file, @NonNull final String type,
             @NonNull final String name, @NonNull ILogger logger) {
         if (!file.exists()) {
-            return -1;
+            return SourcePosition.UNKNOWN;
         }
 
         final ReadOnlyDocument document = getDocument(file, logger);
         if (document == null) {
-            return -1;
+            return SourcePosition.UNKNOWN;
         }
 
         // First just do something simple: scan for the string. If it only occurs once, it's easy!
         int index = document.findText(name, 0);
         if (index == -1) {
-            return -1;
+            return SourcePosition.UNKNOWN;
         }
 
         // See if there are any more occurrences; if not, we're done
         if (document.findText(name, index + name.length()) == -1) {
-            return document.lineNumber(index);
+            return document.sourcePosition(index);
         }
 
         // Try looking for name="$name"
@@ -323,21 +323,21 @@
         if (nameIndex != -1) {
             // TODO: Disambiguate by type, so if values.xml contains both R.string.foo and R.dimen.foo we
             // pick the right one!
-            return document.lineNumber(nameIndex);
+            return document.sourcePosition(nameIndex);
         }
 
-        int lineNumber = findValueDeclarationViaParse(type, name, document);
-        if (lineNumber != -1) {
+        SourcePosition lineNumber = findValueDeclarationViaParse(type, name, document);
+        if (!SourcePosition.UNKNOWN.equals(lineNumber)) {
             return lineNumber;
         }
 
         // Just fall back to the first occurrence of the string
         //noinspection ConstantConditions
         assert index != -1;
-        return document.lineNumber(index);
+        return document.sourcePosition(index);
     }
 
-    private static int findValueDeclarationViaParse(final String type, final String name,
+    private static SourcePosition findValueDeclarationViaParse(final String type, final String name,
             ReadOnlyDocument document) {
         // Finally do a full SAX parse to identify the position
         final int[] certain = new int[]{-1, 0};  // line,column for exact match
@@ -361,8 +361,8 @@
                 myDepth++;
                 if (myDepth == 2) {
                     if (name.equals(attributes.getValue(ATTR_NAME))) {
-                        int lineNumber = myLocator.getLineNumber();
-                        int column = myLocator.getColumnNumber();
+                        int lineNumber = myLocator.getLineNumber() - 1;
+                        int column = myLocator.getColumnNumber() - 1;
                         if (qName.equals(type) || TAG_ITEM.equals(qName) && type
                                 .equals(attributes.getValue(ATTR_TYPE))) {
                             line.set(lineNumber);
@@ -393,31 +393,33 @@
             // Ignore parser errors; we might have found the error position earlier than the parse error position
         }
 
-        int lineNumber;
-        int column;
+        int endLineNumber;
+        int endColumn;
         if (certain[0] != -1) {
-            lineNumber = certain[0];
-            column = certain[1];
+            endLineNumber = certain[0];
+            endColumn = certain[1];
         } else {
-            lineNumber = possible[0];
-            column = possible[1];
+            endLineNumber = possible[0];
+            endColumn = possible[1];
         }
-        if (lineNumber != -1) {
+        if (endLineNumber != -1) {
             // SAX' locator will point to the END of the opening declaration, meaning that if it spans multiple lines, we are pointing
             // to the last line:
             //     <item
             //       type="dimen"
             //       name="attribute"
             //     >     <--- this is where the locator points, so we need to search backwards
-            int offset = document.lineOffset(lineNumber) + column;
-            offset = document.findTextBackwards(name, offset);
+            int endOffset = document.lineOffset(endLineNumber) + endColumn;
+            int offset = document.findTextBackwards(name, endOffset);
             if (offset != -1) {
-                lineNumber = document.lineNumber(offset);
+                SourcePosition start = document.sourcePosition(offset);
+                return new SourcePosition(start.getStartLine(), start.getStartColumn(), start.getStartOffset(),
+                                          endLineNumber, endColumn, endOffset);
             }
-            return lineNumber;
+            return new SourcePosition(endLineNumber, endColumn, endOffset);
         }
 
-        return -1;
+        return SourcePosition.UNKNOWN;
     }
 
     @Nullable
@@ -433,7 +435,7 @@
     }
 
     @NonNull
-    GradleMessage createMessage(@NonNull GradleMessage.Kind kind,
+    Message createMessage(@NonNull Message.Kind kind,
                                     @NonNull String text,
                                     @Nullable String sourcePath,
                                     @Nullable String lineNumberAsText,
@@ -449,12 +451,12 @@
 
         SourcePosition errorPosition = parseLineNumber(lineNumberAsText);
         if (sourcePath != null) {
-            FilePosition source = findSourcePosition(file, errorPosition.getStartLine(), text, logger);
+            SourceFilePosition source = findSourcePosition(file, errorPosition.getStartLine(), text, logger);
             if (source != null) {
-                file = source.getSourceFile();
+                file = source.getFile().getSourceFile();
                 sourcePath = file.getPath();
-                if (source.getStartLine() != -1) {
-                    errorPosition = source;
+                if (source.getPosition().getStartLine() != -1) {
+                    errorPosition = source.getPosition();
                 }
             }
         }
@@ -465,7 +467,7 @@
         if (file != null && errorPosition.getStartLine() != -1) {
             errorPosition = findMessagePositionInFile(file, text, errorPosition.getStartLine(), logger);
         }
-        return new GradleMessage(kind, text, sourcePath, errorPosition, original);
+        return new Message(kind, text, original, new SourceFilePosition(file, errorPosition));
     }
 
     private SourcePosition parseLineNumber(String lineNumberAsText) throws ParsingFailedException {
@@ -478,11 +480,19 @@
             }
         }
 
-        return new SourcePosition(lineNumber, -1, -1);
+        return new SourcePosition(lineNumber - 1, -1, -1);
     }
 
+    /**
+     *
+     * @param file
+     * @param locationLine
+     * @param message
+     * @param logger
+     * @return null if could not be found, new SourceFilePosition(new SourceFile file,
+     */
     @Nullable
-    protected static FilePosition findSourcePosition(@NonNull File file, int locationLine,
+    protected static SourceFilePosition findSourcePosition(@NonNull File file, int locationLine,
             String message, ILogger logger) {
         if (!file.getPath().endsWith(DOT_XML)) {
             return null;
@@ -552,9 +562,9 @@
             // Look up the line number
             SourcePosition position = findMessagePositionInFile(sourceFile, message,
                     1, logger); // Search from the beginning
-            return new FilePosition(sourceFile, position);
+            return new SourceFilePosition(new SourceFile(sourceFile), position);
         }
 
-        return new FilePosition(sourceFile, SourcePosition.UNKNOWN);
+        return new SourceFilePosition(new SourceFile(sourceFile), SourcePosition.UNKNOWN);
     }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java
index 46e6a98..52b46b7 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -31,7 +31,7 @@
             .compile("W/ResourceType\\(.*\\): Bad XML block: no root element node found");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java
index 774c820..d06e902 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -41,7 +41,7 @@
     );
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERNS.get(0).matcher(line);
         if (!m.matches()) {
@@ -56,7 +56,7 @@
         }
         String sourcePath = m.group(1);
 
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+        Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
                 lineNumber, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java
index 5ff8864..de0db3f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -40,7 +40,7 @@
                     Pattern.compile("Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"));
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERNS.get(0).matcher(line);
         if (!m.matches()) {
@@ -55,7 +55,7 @@
         String sourcePath = m.group(1);
         String lineNumber = m.group(2);
 
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+        Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
                 lineNumber, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java
index c3d3e0e..f496cfd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -36,7 +36,7 @@
     private static final Pattern MSG_PATTERN = Pattern.compile("^(.+)\\sline\\s(\\d+):\\s(.+)$");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -46,7 +46,7 @@
         String lineNumber = m.group(2);
         String msgText = m.group(3);
 
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+        Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
                 lineNumber, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java
index ddff220..17ee45b 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -40,7 +40,7 @@
                     Pattern.compile("^(.+)\\s+at\\s+line\\s+(\\d+)$"));
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERNS.get(0).matcher(line);
         if (!m.matches()) {
@@ -55,7 +55,7 @@
         String msgText = m.group(1);
         String lineNumber = m.group(2);
 
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+        Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
                 lineNumber, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java
index b13fd2a..449edd8 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java
@@ -17,7 +17,7 @@
 
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -44,7 +44,7 @@
     );
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         for (Pattern pattern : MSG_PATTERNS) {
             Matcher m = pattern.matcher(line);
@@ -52,15 +52,15 @@
                 String sourcePath = m.group(1);
                 String lineNumber = m.group(2);
                 String msgText = m.group(3);
-                GradleMessage.Kind kind = GradleMessage.Kind.ERROR;
+                Message.Kind kind = Message.Kind.ERROR;
                 if (msgText.startsWith("warning: ")) {
                     // NDK warning also matches this regexp
-                    kind = GradleMessage.Kind.WARNING;
+                    kind = Message.Kind.WARNING;
                 }
                 if (sourcePath.endsWith(SdkConstants.DOT_JAVA)) {
                     return false;
                 }
-                GradleMessage msg = createMessage(kind, msgText, sourcePath, lineNumber, "", logger);
+                Message msg = createMessage(kind, msgText, sourcePath, lineNumber, "", logger);
                 messages.add(msg);
                 return true;
             }
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java
index fe8f1de..fb464af 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -42,7 +42,7 @@
             .compile("^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -58,7 +58,7 @@
                 reader.skipNextLine();
             }
         }
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+        Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
                 null, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java
index 31644c4..27fafaa 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -31,7 +31,7 @@
             .compile("^(invalid resource directory name): (.*)$");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -39,7 +39,7 @@
         }
         String sourcePath = m.group(2);
         String text = m.group(1);
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, text, sourcePath, null, "", logger);
+        Message msg = createMessage(Message.Kind.ERROR, text, sourcePath, null, "", logger);
         messages.add(msg);
         return true;
     }
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java
index d09b485..a611704 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -38,7 +38,7 @@
     private static final Pattern MSG_PATTERN = Pattern.compile("^Invalid configuration: (.+)$");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -49,7 +49,7 @@
         // skip the next line
         reader.skipNextLine();
 
-        GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, null, null, "", logger);
+        Message msg = createMessage(Message.Kind.ERROR, msgText, null, null, "", logger);
         messages.add(msg);
         return true;
     }
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java
index 7fb92b2..3c2366b 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java
@@ -16,6 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
+import com.android.ide.common.blame.SourcePosition;
 import com.google.common.base.Charsets;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -23,7 +24,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -58,7 +58,6 @@
         myFile = file;
         myLastModified = file.lastModified();
         myOffsets = Lists.newArrayListWithExpectedSize(mFileContents.length() / 30);
-        myOffsets.add(0);
         for (int i = 0; i < mFileContents.length(); i++) {
             char c = mFileContents.charAt(i);
             if (c == '\n') {
@@ -76,7 +75,8 @@
     }
 
     /**
-     * Returns the offset of the given line number, relative to the beginning of the document.
+     * Returns the [0-based] offset of the given [0-based] line number,
+     * relative to the beginning of the document.
      *
      * @param lineNumber the given line number.
      * @return the offset of the given line. -1 is returned if the document is empty, or if the
@@ -91,7 +91,7 @@
     }
 
     /**
-     * Returns the line number of the given offset.
+     * Returns the [0-based] line number of the given [0-based] offset.
      *
      * @param offset the given offset.
      * @return the line number of the given offset. -1 is returned if the document is empty or if
@@ -99,14 +99,23 @@
      */
     int lineNumber(int offset) {
         for (int i = 0; i < myOffsets.size(); i++) {
-            int savedOffset = myOffsets.get(i);
-            if (offset <= savedOffset) {
+            if (offset < myOffsets.get(i)) {
                 return i;
             }
         }
         return -1;
     }
 
+    SourcePosition sourcePosition(int offset) {
+        for (int i = 0; i < myOffsets.size(); i++) {
+            if (offset < myOffsets.get(i)) {
+                int lineStartOffset = i==0 ? 0 : myOffsets.get(i-1);
+                return new SourcePosition(i, offset - lineStartOffset, offset);
+            }
+        }
+        return SourcePosition.UNKNOWN;
+    }
+
     /**
      * Finds the given text in the document, starting from the given offset.
      *
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java
index 9400b50..93e6511 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -37,7 +37,7 @@
             .compile("^\\s+\\(skipping hidden file\\s'(.*)'\\)$");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         return m.matches();
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java
index 067b975..0441583 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -35,7 +35,7 @@
     private static final Pattern MSG_PATTERN = Pattern.compile("    \\(skipping (.+) .+ '(.*)'\\)");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -50,7 +50,7 @@
                 || type.equals("index")) {    // thumbs.db, etc
             return true;
         }
-        GradleMessage msg = createMessage(GradleMessage.Kind.WARNING, line, sourcePath,
+        Message msg = createMessage(Message.Kind.WARNING, line, sourcePath,
                 null, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java
index 7919475..12b434d 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -36,7 +36,7 @@
             .compile("    \\(skipping .+ '(.+)' due to ANDROID_AAPT_IGNORE pattern '.+'\\)");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -46,7 +46,7 @@
         if (sourcePath != null && (sourcePath.startsWith(".") || sourcePath.endsWith("~"))) {
             return true;
         }
-        GradleMessage msg = createMessage(GradleMessage.Kind.WARNING, line, sourcePath,
+        Message msg = createMessage(Message.Kind.WARNING, line, sourcePath,
                 null, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java
index 4adf8d1..4041208 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java
@@ -16,7 +16,7 @@
 package com.android.ide.common.blame.parser.aapt;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.blame.parser.util.OutputLineReader;
 import com.android.ide.common.blame.parser.ParsingFailedException;
 import com.android.utils.ILogger;
@@ -33,7 +33,7 @@
     private static final Pattern MSG_PATTERN = Pattern.compile("^(.+?):(\\d+):\\s+WARNING:(.+)$");
 
     @Override
-    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+    public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
             throws ParsingFailedException {
         Matcher m = MSG_PATTERN.matcher(line);
         if (!m.matches()) {
@@ -44,7 +44,7 @@
         String lineNumber = m.group(2);
         String msgText = m.group(3);
 
-        GradleMessage msg = createMessage(GradleMessage.Kind.WARNING, msgText, sourcePath,
+        Message msg = createMessage(Message.Kind.WARNING, msgText, sourcePath,
                 lineNumber, "", logger);
         messages.add(msg);
         return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java b/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java
index 3850917..9fa3276 100644
--- a/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java
+++ b/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java
@@ -53,7 +53,7 @@
      * @param outputs the tested variant outpts.
      * @param variantAbiFilters a list of abi filters applied to the variant. This is used in place
      *                          of the outputs, if there is a single output with no abi filters.
-     *                          If the list is null, then the variant does not restrict ABI
+     *                          If the list is null or empty, then the variant does not restrict ABI
      *                          packaging.
      * @return the list of APK files to install.
      * @throws ProcessException
@@ -226,7 +226,9 @@
             Collection<String> variantAbiFilters,
             Collection<String> deviceAbis) {
         // so far, we are not dealing with the pure split files...
-        if (getFilter(mainOutputFile, OutputFile.ABI) == null && variantAbiFilters != null) {
+        if (getFilter(mainOutputFile, OutputFile.ABI) == null
+                && variantAbiFilters != null
+                && !variantAbiFilters.isEmpty()) {
             // if we have a match that has no abi filter, and we have variant-level filters, then
             // we need to make sure that the variant filters are compatible with the device abis.
             for (String abi : deviceAbis) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java b/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java
index b0f25de..37f9363 100644
--- a/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java
+++ b/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java
@@ -22,7 +22,6 @@
 import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 import com.android.annotations.concurrency.GuardedBy;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
 
 import java.util.Map;
@@ -166,7 +165,8 @@
      * State of values.
      */
     @VisibleForTesting
-    enum State { EXISTING_VALUE, NEW_VALUE, PROCESSED_VALUE; }
+    enum State { EXISTING_VALUE, NEW_VALUE, PROCESSED_VALUE
+    }
 
     /**
      * A Value State. This contains the Type as {@link State}, and a optional value {@link V}
diff --git a/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java b/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java
index 05fd1db..bd56920 100644
--- a/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java
+++ b/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java
@@ -26,10 +26,11 @@
 
     private static ExecutorService sExecutorService;
 
+    private static int sThreadPoolSize = Runtime.getRuntime().availableProcessors();
+
     public static synchronized ExecutorService getExecutor() {
         if (sExecutorService == null) {
-            sExecutorService = Executors.newFixedThreadPool(
-                    Runtime.getRuntime().availableProcessors());
+            sExecutorService = Executors.newFixedThreadPool(sThreadPoolSize);
         }
 
         return sExecutorService;
@@ -41,4 +42,16 @@
             sExecutorService = null;
         }
     }
+
+    /**
+     * Changes the thread pool size for the singleton ExecutorService.
+     *
+     * <b>Caution</b>: This will have no effect if getExecutor() has already been called until the
+     * executor is shutdown and reinitialized.
+     *
+     * @param threadPoolSize the number of threads to use.
+     */
+    public static void setThreadPoolSize(int threadPoolSize) {
+        sThreadPoolSize = threadPoolSize;
+    }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java b/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java
index 91fc182..19e4a19 100644
--- a/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java
+++ b/sdk-common/src/main/java/com/android/ide/common/packaging/PackagingUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.ide.common.packaging;
 
+import com.google.common.collect.ImmutableList;
+
 /**
  * Utility class for packaging.
  */
@@ -59,20 +61,55 @@
     public static boolean checkFileForPackaging(String fileName, String extension) {
         // ignore hidden files and backup files
         return !(fileName.charAt(0) == '.' || fileName.charAt(fileName.length() - 1) == '~') &&
-                !"aidl".equalsIgnoreCase(extension) &&        // Aidl files
-                !"rs".equalsIgnoreCase(extension) &&          // RenderScript files
-                !"fs".equalsIgnoreCase(extension) &&          // FilterScript files
-                !"rsh".equalsIgnoreCase(extension) &&         // RenderScript header files
-                !"d".equalsIgnoreCase(extension) &&           // Dependency files
-                !"java".equalsIgnoreCase(extension) &&        // Java files
-                !"scala".equalsIgnoreCase(extension) &&       // Scala files
-                !"class".equalsIgnoreCase(extension) &&       // Java class files
-                !"scc".equalsIgnoreCase(extension) &&         // VisualSourceSafe
-                !"swp".equalsIgnoreCase(extension) &&         // vi swap file
-                !"thumbs.db".equalsIgnoreCase(fileName) &&    // image index file
-                !"picasa.ini".equalsIgnoreCase(fileName) &&   // image index file
-                !"about.html".equalsIgnoreCase(fileName) &&   // Javadoc
-                !"package.html".equalsIgnoreCase(fileName) && // Javadoc
-                !"overview.html".equalsIgnoreCase(fileName);  // Javadoc
+                !isOfNonResourcesExtensions(extension) &&
+                !isNotAResourceFile(fileName);
     }
+
+    private static boolean isOfNonResourcesExtensions(String extension) {
+        for (String ext : NON_RESOURCES_EXTENSIONS) {
+            if (ext.equalsIgnoreCase(extension)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isNotAResourceFile(String fileName) {
+        for (String name : NON_RESOURCES_FILENAMES) {
+            if (name.equalsIgnoreCase(fileName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the list of file extensions that represents non resources files.
+     */
+    public static final ImmutableList<String> NON_RESOURCES_EXTENSIONS =
+            ImmutableList.<String>builder()
+                    .add("aidl")            // Aidl files
+                    .add("rs")              // RenderScript files
+                    .add("fs")              // FilterScript files
+                    .add("rsh")             // RenderScript header files
+                    .add("d")               // Dependency files
+                    .add("java")            // Java files
+                    .add("scala")           // Scala files
+                    .add("class")           // Java class files
+                    .add("so")              // native .so libraries
+                    .add("scc")             // VisualSourceSafe
+                    .add("swp")             // vi swap file
+                    .build();
+
+    /**
+     * Return file names that are not resource files.
+     */
+    public static final ImmutableList<String> NON_RESOURCES_FILENAMES =
+            ImmutableList.<String>builder()
+                    .add("thumbs.db")       // image index file
+                    .add("picasa.ini")      // image index file
+                    .add("about.html")      // Javadoc
+                    .add("package.html")    // Javadoc
+                    .add("overview.html")   // Javadoc
+                    .build();
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java b/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java
index be30551..a6b21e5 100644
--- a/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java
+++ b/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java
@@ -34,7 +34,7 @@
      * @return this
      */
     @NonNull
-    public T addEnvironments(@NonNull Map<String, ? extends Object> map) {
+    public T addEnvironments(@NonNull Map<String, ?> map) {
         mEnvironment.putAll(map);
         return thisAsT();
     }
diff --git a/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java b/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java
index 55fba98..b6a32dd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java
+++ b/sdk-common/src/main/java/com/android/ide/common/rendering/HardwareConfigHelper.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.HardwareConfig;
 import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
 import com.android.sdklib.devices.ButtonType;
 import com.android.sdklib.devices.Device;
 import com.android.sdklib.devices.Screen;
@@ -164,6 +165,7 @@
                 (float) screen.getYdpi(),
                 screen.getSize(),
                 mScreenOrientation,
+                mDevice.getDefaultHardware().getScreen().getScreenRound(),
                 mDevice.getDefaultHardware().getButtonType() == ButtonType.SOFT);
     }
 
@@ -199,7 +201,7 @@
      * Returns a user-displayable description of the given generic device
      * @param device the device to check
      * @return the label
-     * @see #isGeneric(com.android.sdklib.devices.Device)
+     * @see #isGeneric(Device)
      */
     @NonNull
     public static String getGenericLabel(@NonNull Device device) {
@@ -268,13 +270,6 @@
     }
 
     /**
-     * Whether the given device has a round screen
-     */
-    public static boolean isRound(@Nullable Device device) {
-        return device != null && ID_PREFIX_WEAR_ROUND.equals(device.getId());
-    }
-
-    /**
      * Whether the given device is a TV device
      */
     public static boolean isTv(@Nullable Device device) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java b/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java
index b534f48..627100a 100644
--- a/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java
+++ b/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java
@@ -18,8 +18,6 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java b/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java
index b05aa17..22c8f1e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java
+++ b/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java
@@ -110,7 +110,7 @@
     }
 
     /**
-     * Find the best matching {@link com.android.ide.common.repository.GradleCoordinate}
+     * Find the best matching {@link GradleCoordinate}
      *
      * @param sdkHome the SDK installation
      * @param groupId the artifact group id
@@ -136,7 +136,7 @@
     }
 
     /**
-     * Find the best matching {@link com.android.ide.common.repository.GradleCoordinate}
+     * Find the best matching {@link GradleCoordinate}
      *
      * @param groupId the artifact group id
      * @param artifactId the artifact id
@@ -188,6 +188,20 @@
         return null;
     }
 
+    @Nullable
+    public static SdkMavenRepository getByGroupId(@NonNull String groupId) {
+        if ("com.android.support".equals(groupId) || "com.android.support.test".equals(groupId)) {
+            return ANDROID;
+        }
+        if (groupId.startsWith("com.google.android.")) {
+            // com.google.android.gms, com.google.android.support.wearable,
+            // com.google.android.wearable, ... possibly more in the future
+            return GOOGLE;
+        }
+
+        return null;
+    }
+
     /** The directory name of the repository inside the extras folder */
     @NonNull
     public String getDirName() {
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java b/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java
index a7e35a4..7104ea5 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java
@@ -34,7 +34,7 @@
      * @param item the resource item
      */
     AssetFile(@NonNull File file, @NonNull AssetItem item) {
-        super(file, FileType.SINGLE);
+        super(file, FileType.SINGLE_FILE);
         init(item);
     }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java
index 44ad1cb..77bff3d 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java
@@ -17,7 +17,6 @@
 package com.android.ide.common.res2;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.packaging.PackagingUtils;
 import com.android.utils.ILogger;
 
 import org.w3c.dom.Attr;
@@ -37,7 +36,7 @@
      * @param configName the name of the config this set is associated with.
      */
     public AssetSet(String configName) {
-        super(configName);
+        super(configName, true /*validateEnabled*/);
     }
 
     @Override
@@ -54,7 +53,7 @@
     }
 
     @Override
-    protected AssetFile createFileAndItems(@NonNull File file, @NonNull Node fileNode) {
+    protected AssetFile createFileAndItemsFromXml(@NonNull File file, @NonNull Node fileNode) {
         Attr nameAttr = (Attr) fileNode.getAttributes().getNamedItem(ATTR_NAME);
         if (nameAttr == null) {
             return null;
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceItem.java
new file mode 100644
index 0000000..679d815
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceItem.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 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.ide.common.res2;
+
+import com.android.annotations.NonNull;
+
+public abstract class DataBindingResourceItem extends DataItem<ResourceFile> {
+
+    private DataBindingResourceType mType;
+
+    /**
+     * Constructs the object with a name, type and optional value. <p/> Note that the object is not
+     * fully usable as-is. It must be added to a DataFile first.
+     *
+     * @param name the name of the item
+     */
+    public DataBindingResourceItem(@NonNull String name, @NonNull DataBindingResourceType type) {
+        super(name);
+        mType = type;
+    }
+
+    @NonNull
+    public DataBindingResourceType getType() {
+        return mType;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        DataBindingResourceItem that = (DataBindingResourceItem) o;
+        if (mType != that.mType) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + mType.hashCode();
+        return result;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceType.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceType.java
new file mode 100644
index 0000000..17f0b99
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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.ide.common.res2;
+
+import com.android.SdkConstants;
+
+public enum DataBindingResourceType {
+    VARIABLE(SdkConstants.ATTR_NAME, SdkConstants.ATTR_TYPE),
+    IMPORT(SdkConstants.ATTR_ALIAS, SdkConstants.ATTR_TYPE);
+
+    public final String[] attributes;
+
+    DataBindingResourceType(String... attributes) {
+        this.attributes = attributes;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java
index 3f5437d..4e650fc 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java
@@ -35,7 +35,9 @@
 public abstract class DataFile<I extends DataItem> {
 
     enum FileType {
-        SINGLE, MULTI
+        SINGLE_FILE,
+        GENERATED_FILES,
+        XML_VALUES
     }
 
     private final FileType mType;
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java
index 228428a..ff0ce90 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java
@@ -18,9 +18,12 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
+import java.io.File;
+
 /**
  * Base item.
  *
@@ -79,6 +82,10 @@
         mSource = sourceFile;
     }
 
+    public File getFile() {
+        return getSource().getFile();
+    }
+
     /**
      * Resets the state of the item be nothing.
      * @return this
@@ -187,7 +194,12 @@
         // nothing
     }
 
-    Node getAdoptedNode(Document document) {
+    /**
+     * Returns a node that describes additional properties of this {@link DataItem}. If not null, it
+     * will be persisted in the merger XML blob and can be used used to restore the exact state of
+     * this item.
+     */
+    Node getDetailsXml(Document document) {
         return null;
     }
 
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
index fef86c1..c25926b 100755
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
@@ -57,7 +57,7 @@
     static final String FN_MERGER_XML = "merger.xml";
     static final String NODE_MERGER = "merger";
     static final String NODE_DATA_SET = "dataSet";
-    static final String NODE_MERGED_ITEMS = "mergedItems";
+
     static final String NODE_CONFIGURATION = "configuration";
 
     static final String ATTR_VERSION = "version";
@@ -332,23 +332,23 @@
             }
 
             // write merged items
-            writeMergedItems(document, rootNode);
+            writeAdditionalData(document, rootNode);
 
-            String content = XmlUtils.toXml(document, true /*preserveWhitespace*/);
+            String content = XmlUtils.toXml(document);
 
             try {
                 createDir(blobRootFolder);
             } catch (IOException ioe) {
-                throw new MergingException(ioe).addFile(blobRootFolder);
+                throw MergingException.wrapException(ioe).withFile(blobRootFolder).build();
             }
             File file = new File(blobRootFolder, FN_MERGER_XML);
             try {
                 Files.write(content, file, Charsets.UTF_8);
             } catch (IOException ioe) {
-                throw new MergingException(ioe).addFile(file);
+                throw MergingException.wrapException(ioe).withFile(file).build();
             }
         } catch (ParserConfigurationException e) {
-            throw new MergingException(e);
+            throw MergingException.wrapException(e).build();
         }
     }
 
@@ -410,13 +410,11 @@
                 if (NODE_DATA_SET.equals(node.getLocalName())) {
                     S dataSet = createFromXml(node);
                     if (dataSet != null) {
-                        mDataSets.add(dataSet);
+                        addDataSet(dataSet);
                     }
-                } else if (incrementalState && NODE_MERGED_ITEMS.equals(node.getLocalName())) {
-                    // only load the merged item in incremental state.
-                    // In non incremental state, they will be recreated by the touched
-                    // items anyway.
-                    loadMergedItems(node);
+                } else if (incrementalState
+                        && getAdditionalDataTagName().equals(node.getLocalName())) {
+                    loadAdditionalData(node, incrementalState);
                 }
             }
 
@@ -428,21 +426,29 @@
 
             return true;
         } catch (SAXParseException e) {
-            throw new MergingException(e).addFilePosition(file, e);
+            throw MergingException.wrapException(e).withFile(file).build();
         } catch (IOException e) {
-            throw new MergingException(e).addFile(file);
+            throw MergingException.wrapException(e).withFile(file).build();
         } catch (ParserConfigurationException e) {
-            throw new MergingException(e).addFile(file);
+            throw MergingException.wrapException(e).withFile(file).build();
         } catch (SAXException e) {
-            throw new MergingException(e).addFile(file);
+            throw MergingException.wrapException(e).withFile(file).build();
         }
     }
 
-    protected void loadMergedItems(@NonNull Node mergedItemsNode) throws MergingException {
+    @NonNull
+    protected String getAdditionalDataTagName() {
+        // No tag can have an empty name, so mergers that store additional data, have to provide
+        // this.
+        return "";
+    }
+
+    protected void loadAdditionalData(@NonNull Node additionalDataNode, boolean incrementalState)
+            throws MergingException {
         // do nothing by default.
     }
 
-    protected void writeMergedItems(Document document, Node rootNode) throws MergingException {
+    protected void writeAdditionalData(Document document, Node rootNode) throws MergingException {
         // do nothing by default.
     }
 
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java
index 870e852..8d662a0 100755
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java
@@ -18,7 +18,9 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
 import com.android.utils.ILogger;
+import com.google.common.base.Objects;
 import com.google.common.base.Splitter;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
@@ -63,6 +65,8 @@
 
     private final String mConfigName;
 
+    private final boolean mValidateEnabled;
+
     /**
      * List of source files. The may not have been loaded yet.
      */
@@ -92,8 +96,9 @@
      *
      * @param configName the name of the config this set is associated with.
      */
-    public DataSet(String configName) {
+    public DataSet(String configName, boolean validateEnabled) {
         mConfigName = configName;
+        mValidateEnabled = validateEnabled;
     }
 
     protected abstract DataSet<I, F> createSet(String name);
@@ -106,7 +111,7 @@
      * @param fileNode the XML node.
      * @return a DataFile
      */
-    protected abstract F createFileAndItems(@NonNull File file, @NonNull Node fileNode)
+    protected abstract F createFileAndItemsFromXml(@NonNull File file, @NonNull Node fileNode)
             throws MergingException;
 
     /**
@@ -229,14 +234,20 @@
      * @throws MergingException if something goes wrong
      */
     public void loadFromFiles(ILogger logger) throws MergingException {
+        List<Message> errors = Lists.newArrayList();
         for (File file : mSourceFiles) {
             if (file.isDirectory()) {
-                readSourceFolder(file, logger);
+                try {
+                    readSourceFolder(file, logger);
+                } catch (MergingException e) {
+                    errors.addAll(e.getMessages());
+                }
 
             } else if (file.isFile()) {
                 // TODO support resource bundle
             }
         }
+        MergingException.throwIfNonEmpty(errors);
         checkItems();
     }
 
@@ -276,22 +287,30 @@
                         dataFile.getFile().getAbsolutePath());
                 dataFile.addExtraAttributes(document, fileNode, null);
 
-                if (dataFile.getType() == DataFile.FileType.MULTI) {
-                    for (I item : dataFile.getItems()) {
-                        if (item.isRemoved()|| consumer.ignoreItemInMerge(item)) {
-                            continue;
+                switch (dataFile.getType()) {
+                    case GENERATED_FILES:
+                        // Fall through. getDetailsXml() will return the XML which describes the
+                        // generated files.
+                    case XML_VALUES:
+                        for (I item : dataFile.getItems()) {
+                            if (item.isRemoved()|| consumer.ignoreItemInMerge(item)) {
+                                continue;
+                            }
+                            Node adoptedNode = item.getDetailsXml(document);
+                            if (adoptedNode != null) {
+                                fileNode.appendChild(adoptedNode);
+                            }
                         }
-                        Node adoptedNode = item.getAdoptedNode(document);
-                        if (adoptedNode != null) {
-                            fileNode.appendChild(adoptedNode);
-                        }
-                    }
-                } else {
-                    // no need to check for isRemoved here since it's checked
-                    // at the file level and there's only one item.
-                    I dataItem = dataFile.getItem();
-                    NodeUtils.addAttribute(document, fileNode, null, ATTR_NAME, dataItem.getName());
-                    dataItem.addExtraAttributes(document, fileNode, null);
+                        break;
+                    case SINGLE_FILE:
+                        // no need to check for isRemoved here since it's checked
+                        // at the file level and there's only one item.
+                        I dataItem = dataFile.getItem();
+                        NodeUtils.addAttribute(document, fileNode, null, ATTR_NAME, dataItem.getName());
+                        dataItem.addExtraAttributes(document, fileNode, null);
+                        break;
+                    default:
+                        throw new IllegalStateException();
                 }
             }
         }
@@ -350,7 +369,7 @@
                     continue;
                 }
                 
-                F dataFile = createFileAndItems(new File(pathAttr.getValue()), fileNode);
+                F dataFile = createFileAndItemsFromXml(new File(pathAttr.getValue()), fileNode);
 
                 if (dataFile != null) {
                     dataSet.processNewDataFile(sourceFolder, dataFile, false /*setTouched*/);
@@ -367,6 +386,10 @@
      * @throws DuplicateDataException if a duplicated item is found.
      */
     protected void checkItems() throws DuplicateDataException {
+        if (!mValidateEnabled) {
+            return;
+        }
+        Collection<Collection<I>> duplicateCollections = Lists.newArrayList();
         // check a list for duplicate, ignoring removed items.
         for (Map.Entry<String, Collection<I>> entry : mItems.asMap().entrySet()) {
             Collection<I> items = entry.getValue();
@@ -378,11 +401,16 @@
                     if (lastItem == null) {
                         lastItem = item;
                     } else {
-                        throw new DuplicateDataException(item, lastItem);
+                        // We have duplicates, store them and throw the exception later, so
+                        // the user gets all the error messages at once.
+                        duplicateCollections.add(items);
                     }
                 }
             }
         }
+        if (!duplicateCollections.isEmpty()) {
+            throw new DuplicateDataException(DuplicateDataException.createMessages(duplicateCollections));
+        }
     }
 
     /**
@@ -401,24 +429,28 @@
             case NEW:
                 return handleNewFile(sourceFolder, changedFile, logger);
             case CHANGED:
-                return handleChangedFile(sourceFolder, changedFile);
+                return handleChangedFile(sourceFolder, changedFile, logger);
             case REMOVED:
-                F dataFile = mDataFileMap.get(changedFile);
-
-                if (dataFile == null) {
-                    return false;
-                }
-
-                // flag all resource items are removed
-                for (I dataItem : dataFile.getItems()) {
-                    dataItem.setRemoved();
-                }
-                return true;
+                return handleRemovedFile(changedFile);
         }
 
         return false;
     }
 
+    protected boolean handleRemovedFile(File removedFile) {
+        F dataFile = getDataFile(removedFile);
+
+        if (dataFile == null) {
+            return false;
+        }
+
+        // flag all resource items are removed
+        for (I dataItem : dataFile.getItems()) {
+            dataItem.setRemoved();
+        }
+        return true;
+    }
+
     protected boolean isValidSourceFile(@NonNull File sourceFolder, @NonNull File file) {
         return checkFileForAndroidRes(file);
     }
@@ -447,10 +479,14 @@
         }
     }
 
-    protected boolean handleChangedFile(@NonNull File sourceFolder,
-                                        @NonNull File changedFile) throws MergingException {
+    protected boolean handleChangedFile(
+            @NonNull File sourceFolder,
+            @NonNull File changedFile,
+            @NonNull ILogger logger) throws MergingException {
         F dataFile = mDataFileMap.get(changedFile);
-        dataFile.getItem().setTouched();
+        for (I item : dataFile.getItems()) {
+            item.setTouched();
+        }
         return true;
     }
 
@@ -479,7 +515,10 @@
 
     @Override
     public String toString() {
-        return Arrays.toString(mSourceFiles.toArray());
+        return Objects.toStringHelper(getClass())
+                .addValue(mConfigName)
+                .add("sources", Arrays.toString(mSourceFiles.toArray()))
+                .toString();
     }
 
     /**
@@ -487,7 +526,7 @@
      * @param file the file to check
      * @return true if it is a valid file, false if it should be ignored.
      */
-    protected boolean checkFileForAndroidRes(@NonNull File file) {
+    protected static boolean checkFileForAndroidRes(@NonNull File file) {
         return !isIgnored(file);
     }
 
@@ -588,4 +627,8 @@
 
         return ignore;
     }
+
+    protected boolean getValidateEnabled() {
+        return mValidateEnabled;
+    }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java b/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java
index f7ece6e..f7316fd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java
@@ -17,34 +17,56 @@
 package com.android.ide.common.res2;
 
 import com.android.annotations.NonNull;
-import com.android.ide.common.blame.FilePosition;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 
-import org.w3c.dom.Node;
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
 
 /**
  * Exception when a {@link DataItem} is declared more than once in a {@link DataSet}
  */
 public class DuplicateDataException extends MergingException {
 
-    private DataItem mOne;
-    private DataItem mTwo;
+    private static final String DUPLICATE_RESOURCES = "Duplicate resources";
 
-    DuplicateDataException(@NonNull DataItem one, @NonNull DataItem two) {
-        super(String.format("Duplicate resources: %1s:%2s, %3s:%4s",
-                one.getSource().getFile().getAbsolutePath(), one.getKey(),
-                two.getSource().getFile().getAbsolutePath(), two.getKey()));
-        mOne = one;
-        mTwo = two;
-        addFile(one.getSource().getFile());
-        addFile(two.getSource().getFile());
+    DuplicateDataException(Message[] messages) {
+        super(null, messages);
     }
 
-    public DataItem getOne() {
-        return mOne;
+    static <I extends DataItem> Message[] createMessages(
+            @NonNull Collection<Collection<I>> duplicateDataItemSets) {
+        List<Message> messages = Lists.newArrayListWithCapacity(duplicateDataItemSets.size());
+        for (Collection<I> duplicateItems : duplicateDataItemSets) {
+            ImmutableList.Builder<SourceFilePosition> positions = ImmutableList.builder();
+            for (I item : duplicateItems) {
+                if (!item.isRemoved()) {
+                    positions.add(getPosition(item));
+                }
+            }
+            messages.add(new Message(
+                    Message.Kind.ERROR,
+                    DUPLICATE_RESOURCES,
+                    DUPLICATE_RESOURCES,
+                    positions.build()));
+        }
+        return Iterables.toArray(messages, Message.class);
     }
 
-    public DataItem getTwo() {
-        return mTwo;
+    private static SourceFilePosition getPosition(DataItem item) {
+        DataFile dataFile = item.getSource();
+        if (dataFile == null) {
+            return new SourceFilePosition(new SourceFile(item.getKey()), SourcePosition.UNKNOWN);
+        }
+        File f = dataFile.getFile();
+        SourcePosition sourcePosition = SourcePosition.UNKNOWN;  // TODO: find position in file.
+        return new SourceFilePosition(new SourceFile(f, item.getKey()), sourcePosition);
     }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java b/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java
index 244d6bc..e94fc9f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java
@@ -18,6 +18,7 @@
 
 import static com.android.SdkConstants.DOT_9PNG;
 import static com.android.SdkConstants.DOT_XML;
+import static com.android.SdkConstants.DOT_XSD;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
@@ -45,7 +46,7 @@
             throws MergingException {
         String error = getErrorTextForFileResource(file.getName(), resourceType);
         if (error != null) {
-            throw new MergingException(error).setFile(file);
+            throw MergingException.withMessage(error).withFile(file).build();
         }
     }
 
@@ -119,6 +120,23 @@
                     }
                 }
             }
+        } else if (resourceType == ResourceType.XML) {
+            // Also allow xsd as they are xml files.
+            if (SdkUtils.endsWithIgnoreCase(fileNameWithExt, DOT_XML) ||
+                    SdkUtils.endsWithIgnoreCase(fileNameWithExt, DOT_XSD)) {
+                fileName = removeSingleExtension(fileNameWithExt);
+            } else {
+                if (!allowPartialOrMissingExtension) {
+                    return "The file name must end with .xml";
+                } else {
+                    fileName = removeSingleExtension(fileNameWithExt);
+                    String ext = fileNameWithExt.substring(fileName.length());
+                    if (!SdkUtils.startsWithIgnoreCase(DOT_XML, ext) &&
+                            !SdkUtils.startsWithIgnoreCase(DOT_XSD, ext)) {
+                        return "The file name must end with .xml";
+                    }
+                }
+            }
         } else {
             // Require xml extension
             if (SdkUtils.endsWithIgnoreCase(fileNameWithExt, DOT_XML)) {
@@ -136,7 +154,7 @@
                 }
             }
         }
-        return getErrorTextForFileResourceNoExt(fileName);
+        return getErrorTextForNameWithoutExtension(fileName);
     }
 
     /**
@@ -146,11 +164,11 @@
      * @return null if no error, otherwise a string describing the error.
      */
     @Nullable
-    private static String getErrorTextForFileResourceNoExt(
+    public static String getErrorTextForNameWithoutExtension(
             @NonNull final String fileNameWithoutExt) {
         char first = fileNameWithoutExt.charAt(0);
-        if (!(first >= 'a' && first <= 'z')) {
-            return "File-based resource names must start with a lowercase letter";
+        if (!Character.isJavaIdentifierStart(first)) {
+            return "The resource name must start with a letter";
         }
 
         // AAPT only allows lowercase+digits+_:
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java b/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java
index 36f6478..f2a6226 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java
@@ -41,4 +41,10 @@
     public File getSourceFile() {
         return sourceFile;
     }
+
+    public void clear() {
+        dataSet = null;
+        sourceFile = null;
+        status = FileStatus.UNKNOWN_FILE;
+    }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceItem.java
new file mode 100644
index 0000000..de129de
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 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.ide.common.res2;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.resources.ResourceType;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.io.File;
+
+/**
+ * A {@link ResourceItem} that is generated, it knows its generate file path, which is not the same
+ * as as the owner ResourceFile.
+ */
+public class GeneratedResourceItem extends ResourceItem {
+
+    private final File mGeneratedFile;
+
+    private final String mQualifiers;
+
+    public GeneratedResourceItem(
+            @NonNull String name,
+            @NonNull File generatedFile,
+            @NonNull ResourceType type,
+            @NonNull String qualifiers) {
+        super(name, type, null);
+        mGeneratedFile = generatedFile;
+        mQualifiers = qualifiers;
+    }
+
+    @NonNull
+    @Override
+    public String getQualifiers() {
+        return mQualifiers;
+    }
+
+    @Override
+    public File getFile() {
+        return mGeneratedFile;
+    }
+
+    @Override
+    Node getDetailsXml(Document document) {
+        Element element = document.createElement("generated-file");
+        element.setAttribute(SdkConstants.ATTR_PATH, mGeneratedFile.getAbsolutePath());
+        element.setAttribute(SdkConstants.ATTR_TYPE, getType().getName());
+        element.setAttribute(ResourceFile.ATTR_QUALIFIER, mQualifiers);
+        return element;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceSet.java
new file mode 100644
index 0000000..587d347
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceSet.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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.ide.common.res2;
+
+import com.android.annotations.NonNull;
+import com.android.utils.ILogger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.File;
+
+/**
+ * A {@link ResourceSet} that contains only generated files (e.g. PNGs generated from a vector
+ * drawable XML). It is always a mirror of a normal {@link ResourceSet} which delegates to this
+ * set when it encounters a file that needs to be replaced by generated files.
+ */
+public class GeneratedResourceSet extends ResourceSet {
+
+    public GeneratedResourceSet(ResourceSet originalSet) {
+        super(originalSet.getConfigName() + "$Generated", originalSet.getValidateEnabled());
+        for (File source : originalSet.getSourceFiles()) {
+            addSource(source);
+        }
+    }
+
+    public GeneratedResourceSet(String name) {
+        super(name);
+    }
+
+    @Override
+    protected DataSet<ResourceItem, ResourceFile> createSet(String name) {
+        return new GeneratedResourceSet(name);
+    }
+
+    @Override
+    void appendToXml(
+            @NonNull Node setNode,
+            @NonNull Document document,
+            @NonNull MergeConsumer<ResourceItem> consumer) {
+        NodeUtils.addAttribute(document, setNode, null, "generated", "true");
+        super.appendToXml(setNode, document, consumer);
+    }
+
+    @Override
+    public void loadFromFiles(ILogger logger) throws MergingException {
+        // Do nothing, the original set will hand us the generated files.
+    }
+
+    @Override
+    public File findMatchingSourceFile(File file) {
+        // Do nothing, the original set will hand us the generated files.
+        return null;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java b/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java
index 68203a6..3f4881e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java
@@ -18,6 +18,14 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+
+import java.io.File;
 
 import javax.xml.parsers.DocumentBuilderFactory;
 
@@ -27,24 +35,37 @@
 public interface MergeConsumer<I extends DataItem> {
 
     /**
-     * An exception thrown during by the consumer. It always contains the original exception
-     * as its cause.
+     * An exception thrown during by the consumer. It always contains the original exception as its
+     * cause.
      */
     class ConsumerException extends MergingException {
-        public ConsumerException(Throwable cause) {
-            super(cause);
+
+        public ConsumerException(@NonNull Throwable cause) {
+            this(cause, SourceFile.UNKNOWN);
+        }
+
+        public ConsumerException(@NonNull Throwable cause, @NonNull File file) {
+            this(cause, new SourceFile(file));
+        }
+
+        private ConsumerException(@NonNull Throwable cause, @NonNull SourceFile file) {
+            super(cause, new Message(
+                    Message.Kind.ERROR,
+                    Objects.firstNonNull(
+                            cause.getLocalizedMessage(),
+                            cause.getClass().getCanonicalName()),
+                    Throwables.getStackTraceAsString(cause),
+                    new SourceFilePosition(file, SourcePosition.UNKNOWN)));
         }
     }
 
     /**
      * Called before the merge starts.
-     * @throws ConsumerException
      */
     void start(@NonNull DocumentBuilderFactory factory) throws ConsumerException;
 
     /**
      * Called after the merge ends.
-     * @throws ConsumerException
      */
     void end() throws ConsumerException;
 
@@ -53,22 +74,19 @@
      * indicate whether the item actually changed.
      *
      * @param item the new item.
-     *
-     * @throws ConsumerException
      */
     void addItem(@NonNull I item) throws ConsumerException;
 
     /**
-     * Removes an item. Optionally pass the item that will replace this one.
-     * This methods does not do the replacement. The replaced item is just there
-     * in case the removal can be optimized when it's a replacement vs. a removal.
+     * Removes an item. Optionally pass the item that will replace this one. This methods does not
+     * do the replacement. The replaced item is just there in case the removal can be optimized when
+     * it's a replacement vs. a removal.
      *
      * @param removedItem the removed item.
-     * @param replacedBy the optional item that replaces the removed item.
-     *
-     * @throws ConsumerException
+     * @param replacedBy  the optional item that replaces the removed item.
      */
     void removeItem(@NonNull I removedItem, @Nullable I replacedBy) throws ConsumerException;
 
     boolean ignoreItemInMerge(I item);
+
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java b/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java
index d127317..ef56149 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java
@@ -25,6 +25,7 @@
 import static com.android.SdkConstants.TAG_EAT_COMMENT;
 import static com.android.SdkConstants.TAG_RESOURCES;
 import static com.android.utils.SdkUtils.createPathComment;
+import static com.google.common.base.Preconditions.checkState;
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
@@ -63,7 +64,12 @@
     @NonNull
     private final PngCruncher mCruncher;
 
-    /** If non-null, points to a File that we should write public.txt to */
+    @NonNull
+    private final ResourcePreprocessor mPreprocessor;
+
+    /**
+     * If non-null, points to a File that we should write public.txt to
+     */
     private final File mPublicFile;
 
     private DocumentBuilderFactory mFactory;
@@ -82,9 +88,9 @@
     private ListMultimap<String, ResourceItem> mValuesResMap;
 
     /**
-     * Set of qualifier that had a previously written resource now gone.
-     * This is to keep a list of values files that must be written out even with no
-     * touched or updated resources, in case one or more resources were removed.
+     * Set of qualifier that had a previously written resource now gone. This is to keep a list of
+     * values files that must be written out even with no touched or updated resources, in case one
+     * or more resources were removed.
      */
     private Set<String> mQualifierWithDeletedValues;
 
@@ -92,13 +98,15 @@
             @NonNull PngCruncher pngRunner,
             boolean crunchPng,
             boolean process9Patch,
-            @Nullable File publicFile) {
+            @Nullable File publicFile,
+            @NonNull ResourcePreprocessor preprocessor) {
         super(rootFolder);
         mCruncher = pngRunner;
         mCruncherKey = mCruncher.start();
         mCrunchPng = crunchPng;
         mProcess9Patch = process9Patch;
         mPublicFile = publicFile;
+        mPreprocessor = preprocessor;
     }
 
     /**
@@ -107,7 +115,7 @@
      * @param insertSourceMarkers if true, insert source markers
      */
     public void setInsertSourceMarkers(boolean insertSourceMarkers) {
-      mInsertSourceMarkers = insertSourceMarkers;
+        mInsertSourceMarkers = insertSourceMarkers;
     }
 
     /**
@@ -116,7 +124,7 @@
      * @return whether this manifest merger will insert source markers into the merged source
      */
     public boolean isInsertSourceMarkers() {
-      return mInsertSourceMarkers;
+        return mInsertSourceMarkers;
     }
 
     @Override
@@ -129,8 +137,10 @@
 
     @Override
     public void end() throws ConsumerException {
+        // Make sure all PNGs are generated first.
         super.end();
         try {
+            // Wait for all PNGs to be crunched.
             mCruncher.end(mCruncherKey);
         } catch (InterruptedException e) {
             throw new ConsumerException(e);
@@ -148,23 +158,23 @@
 
     @Override
     public void addItem(@NonNull final ResourceItem item) throws ConsumerException {
-        ResourceFile.FileType type = item.getSourceType();
+        final ResourceFile.FileType type = item.getSourceType();
 
-        if (type == ResourceFile.FileType.MULTI) {
+        if (type == ResourceFile.FileType.XML_VALUES) {
             // this is a resource for the values files
 
             // just add the node to write to the map based on the qualifier.
             // We'll figure out later if the files needs to be written or (not)
             mValuesResMap.put(item.getQualifiers(), item);
         } else {
-            // This is a single value file.
-            // Only write it if the state is TOUCHED.
+            checkState(item.getSource() != null);
+            // This is a single value file or a set of generated files. Only write it if the state
+            // is TOUCHED.
             if (item.isTouched()) {
                 getExecutor().execute(new Callable<Void>() {
                     @Override
                     public Void call() throws Exception {
-                        ResourceFile resourceFile = item.getSource();
-                        File file = resourceFile.getFile();
+                        File file = item.getFile();
 
                         String filename = file.getName();
                         String folderName = getFolderName(item);
@@ -172,11 +182,15 @@
                         try {
                             createDir(typeFolder);
                         } catch (IOException ioe) {
-                            throw new MergingException(ioe).setFile(typeFolder);
+                            throw MergingException.wrapException(ioe).withFile(typeFolder).build();
                         }
 
                         File outFile = new File(typeFolder, filename);
 
+                        if (type == DataFile.FileType.GENERATED_FILES) {
+                            mPreprocessor.generateFile(file, item.getSource().getFile());
+                        }
+
                         try {
                             if (item.getType() == ResourceType.RAW) {
                                 // Don't crunch, don't insert source comments, etc - leave alone.
@@ -199,9 +213,9 @@
                                 Files.copy(file, outFile);
                             }
                         } catch (PngException e) {
-                            throw new MergingException(e).setFile(file);
+                            throw MergingException.wrapException(e).withFile(file).build();
                         } catch (IOException ioe) {
-                            throw new MergingException(ioe).setFile(file);
+                            throw MergingException.wrapException(ioe).withFile(file).build();
                         }
                         return null;
                     }
@@ -214,29 +228,26 @@
     public void removeItem(@NonNull ResourceItem removedItem, @Nullable ResourceItem replacedBy)
             throws ConsumerException {
         ResourceFile.FileType removedType = removedItem.getSourceType();
-        ResourceFile.FileType replacedType = replacedBy != null ?
-                replacedBy.getSourceType() : null;
+        ResourceFile.FileType replacedType = replacedBy != null
+                ? replacedBy.getSourceType()
+                : null;
 
-        if (removedType == replacedType) {
-            // if the type is multi, then we make sure to flag the qualifier as deleted.
-            if (removedType == ResourceFile.FileType.MULTI) {
-                mQualifierWithDeletedValues.add(
-                        removedItem.getQualifiers());
-            } else {
-                // both are single type resources, so we actually don't delete the previous
-                // file as the new one will replace it instead.
-            }
-        } else if (removedType == ResourceFile.FileType.SINGLE) {
-            // removed type is single.
-            // The case of both single type is above, so here either, there is no replacement
-            // or the replacement is multi. We always need to remove the old file.
-            // if replacedType is non-null, then it was values, if not,
-            removeOutFile(removedItem);
-        } else {
-            // removed type is multi.
-            // whether the new type is single or doesn't exist, we always need to mark the qualifier
-            // for rewrite.
-            mQualifierWithDeletedValues.add(removedItem.getQualifiers());
+        switch (removedType) {
+            case SINGLE_FILE: // Fall through.
+            case GENERATED_FILES:
+                if (replacedType == DataFile.FileType.SINGLE_FILE
+                        || replacedType == DataFile.FileType.GENERATED_FILES) {
+                    // Save one IO operation and don't delete a file that will be overwritten
+                    // anyway.
+                    break;
+                }
+                removeOutFile(removedItem);
+                break;
+            case XML_VALUES:
+                mQualifierWithDeletedValues.add(removedItem.getQualifiers());
+                break;
+            default:
+                throw new IllegalStateException();
         }
     }
 
@@ -324,7 +335,7 @@
 
                     currentFile = null;
 
-                    String content = XmlUtils.toXml(document, true /*preserveWhitespace*/);
+                    String content = XmlUtils.toXml(document);
                     Files.write(content, outFile, Charsets.UTF_8);
 
                     if (publicNodes != null && mPublicFile != null) {
@@ -352,8 +363,8 @@
                         Files.write(text, mPublicFile, Charsets.UTF_8);
                     }
                 } catch (Throwable t) {
-                    ConsumerException exception = new ConsumerException(t);
-                    exception.setFile(currentFile != null ? currentFile.getFile() : outFile);
+                    ConsumerException exception = new ConsumerException(t,
+                            currentFile != null ? currentFile.getFile() : outFile);
                     throw exception;
                 }
             }
@@ -376,23 +387,14 @@
      * @return true if success.
      */
     private boolean removeOutFile(ResourceItem resourceItem) {
-        ResourceFile resourceFile = resourceItem.getSource();
-        if (resourceFile.getType() == ResourceFile.FileType.MULTI) {
-            throw new IllegalArgumentException("SourceFile cannot be a FileType.MULTI");
-        }
-
-        File file = resourceFile.getFile();
-        String fileName = file.getName();
-        String folderName = getFolderName(resourceItem);
-
-        return removeOutFile(folderName, fileName);
+        return removeOutFile(getFolderName(resourceItem), resourceItem.getFile().getName());
     }
 
     /**
      * Removes a file from a folder based on a sub folder name and a filename
      *
      * @param folderName the sub folder name
-     * @param fileName the file name.
+     * @param fileName   the file name.
      * @return true if success.
      */
     private boolean removeOutFile(String folderName, String fileName) {
@@ -409,6 +411,7 @@
 
     /**
      * Calculates the right folder name give a resource item.
+     *
      * @param resourceItem the resource item to calculate the folder name from.
      * @return a relative folder name
      */
@@ -416,7 +419,7 @@
     private static String getFolderName(ResourceItem resourceItem) {
         ResourceType itemType = resourceItem.getType();
         String folderName = itemType.getName();
-        String qualifiers = resourceItem.getSource().getQualifiers();
+        String qualifiers = resourceItem.getQualifiers();
         if (!qualifiers.isEmpty()) {
             folderName = folderName + RES_QUALIFIER_SEP + qualifiers;
         }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java b/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java
index 1da85ea..e0c7b68 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java
@@ -18,123 +18,200 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.ide.common.blame.FilePosition;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.Message.Kind;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
 import com.android.ide.common.blame.SourcePosition;
-import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
 import org.xml.sax.SAXParseException;
 
 import java.io.File;
+import java.util.Collection;
 import java.util.List;
 
-/** Exception for errors during merging */
+/**
+ * Exception for errors during merging.
+ */
 public class MergingException extends Exception {
-    private String mMessage; // Keeping our own copy since parent prepends exception class name
-    private List<FilePosition> mFilePositions = Lists.newArrayList();
 
-    public MergingException(@NonNull String message, @Nullable Throwable cause) {
-        super(message, cause);
-        mMessage = message;
+    public static final String MULTIPLE_ERRORS = "Multiple errors:";
+
+    @NonNull
+    private final List<Message> mMessages;
+
+    /**
+     * For internal use. Creates a new MergingException
+     *
+     * @param cause    the original exception. May be null.
+     * @param messages the messaged. Must contain at least one item.
+     */
+    protected MergingException(@Nullable Throwable cause, @NonNull Message... messages) {
+        super(messages.length == 1 ? messages[0].getText() : MULTIPLE_ERRORS, cause);
+        mMessages = ImmutableList.copyOf(messages);
     }
 
-    public MergingException(@NonNull String message) {
-        this(message, null);
+    public static class Builder {
+
+        @Nullable
+        private Throwable mCause = null;
+
+        @Nullable
+        private String mMessageText = null;
+
+        @Nullable
+        private String mOriginalMessageText = null;
+
+        @NonNull
+        private SourceFile mFile = SourceFile.UNKNOWN;
+
+        @NonNull
+        private SourcePosition mPosition = SourcePosition.UNKNOWN;
+
+        private Builder() {
+        }
+
+        public Builder wrapException(@NonNull Throwable cause) {
+            mCause = cause;
+            mOriginalMessageText = Throwables.getStackTraceAsString(cause);
+            return this;
+        }
+
+        public Builder withFile(@NonNull File file) {
+            mFile = new SourceFile(file);
+            return this;
+        }
+
+        public Builder withFile(@NonNull SourceFile file) {
+            mFile = file;
+            return this;
+        }
+
+        public Builder withPosition(@NonNull SourcePosition position) {
+            mPosition = position;
+            return this;
+        }
+
+        public Builder withMessage(@NonNull String messageText, Object... args) {
+            mMessageText = args.length == 0 ? messageText : String.format(messageText, args);
+            return this;
+        }
+
+        public MergingException build() {
+            if (mCause != null) {
+                if (mMessageText == null) {
+                    mMessageText = Objects.firstNonNull(
+                            mCause.getLocalizedMessage(), mCause.getClass().getCanonicalName());
+                }
+                if (mPosition == SourcePosition.UNKNOWN && mCause instanceof SAXParseException) {
+                    SAXParseException exception = (SAXParseException) mCause;
+                    int lineNumber = exception.getLineNumber();
+                    if (lineNumber != -1) {
+                        // Convert positions to be 0-based for SourceFilePosition.
+                        mPosition = new SourcePosition(lineNumber - 1,
+                                exception.getColumnNumber() - 1, -1);
+                    }
+                }
+            }
+
+            if (mMessageText == null) {
+                mMessageText = "Unknown error.";
+            }
+
+            return new MergingException(
+                    mCause,
+                    new Message(
+                            Kind.ERROR,
+                            mMessageText,
+                            Objects.firstNonNull(mOriginalMessageText, mMessageText),
+                            new SourceFilePosition(mFile, mPosition)));
+        }
+
     }
 
-    public MergingException(@NonNull Throwable cause) {
-        this(cause.getLocalizedMessage(), cause);
+    public static Builder wrapException(@NonNull Throwable cause) {
+        return new Builder().wrapException(cause);
+    }
+
+    public static Builder withMessage(@NonNull String message, Object... args) {
+        return new Builder().withMessage(message, args);
+    }
+
+
+    public static void throwIfNonEmpty(Collection<Message> messages) throws MergingException {
+        if (!messages.isEmpty()) {
+            throw new MergingException(null, Iterables.toArray(messages, Message.class));
+        }
+    }
+
+    @NonNull
+    public List<Message> getMessages() {
+        return mMessages;
     }
 
     /**
-     *  Add the file if it hasn't already been included in the list of file positions.
+     * Computes the error message to display for this error
      */
-    public MergingException setFile(@NonNull File file) {
-        for (FilePosition filePosition : mFilePositions) {
-            if (filePosition.getSourceFile().equals(file)) {
-                return this;
-            }
-        }
-        addFile(file);
-        return this;
-    }
-
-    public MergingException addFile(@NonNull File file) {
-        mFilePositions.add(new FilePosition(file, SourcePosition.UNKNOWN));
-        return this;
-    }
-
-    public MergingException addFileIfNonNull(@Nullable File file) {
-        return (file != null) ? addFile(file) : this;
-    }
-
-    public MergingException addFilePosition(@NonNull FilePosition filePosition) {
-        mFilePositions.add(filePosition);
-        return this;
-    }
-
-    public MergingException addFilePosition(@NonNull File file, @NonNull SAXParseException exception) {
-        int lineNumber = exception.getLineNumber();
-        if (lineNumber != -1) {
-            addFilePosition(new FilePosition(file, new SourcePosition(
-                    exception.getLineNumber() - 1, exception.getColumnNumber() - 1, -1)));
-        } else {
-            addFile(file);
-        }
-        return this;
-    }
-
-    public MergingException setCause(@NonNull Throwable cause) {
-        initCause(cause);
-        return this;
-    }
-
-    /** Computes the error message to display for this error */
+    @NonNull
     @Override
     public String getMessage() {
-        StringBuilder sb = new StringBuilder();
-        sb.append(Joiner.on('\t').join(mFilePositions));
+        List<String> messages = Lists.newArrayListWithCapacity(mMessages.size());
+        for (Message message : mMessages) {
+            StringBuilder sb = new StringBuilder();
+            List<SourceFilePosition> sourceFilePositions = message.getSourceFilePositions();
+            if (sourceFilePositions.size() > 1 || !sourceFilePositions.get(0)
+                    .equals(SourceFilePosition.UNKNOWN)) {
+                sb.append(Joiner.on('\t').join(sourceFilePositions));
+            }
 
-        if (sb.length() > 0) {
-            sb.append(':').append(' ');
+            String text = message.getText();
+            if (sb.length() > 0) {
+                sb.append(':').append(' ');
 
-            // ALWAYS insert the string "Error:" between the path and the message.
-            // This is done to make the error messages more simple to detect
-            // (since a generic path: message pattern can match a lot of output, basically
-            // any labeled output, and we don't want to do file existence checks on any random
-            // string to the left of a colon.)
-            if (!mMessage.startsWith("Error: ")) {
+                // ALWAYS insert the string "Error:" between the path and the message.
+                // This is done to make the error messages more simple to detect
+                // (since a generic path: message pattern can match a lot of output, basically
+                // any labeled output, and we don't want to do file existence checks on any random
+                // string to the left of a colon.)
+                if (!text.startsWith("Error: ")) {
+                    sb.append("Error: ");
+                }
+            } else if (!text.contains("Error: ")) {
                 sb.append("Error: ");
             }
-        } else if (!mMessage.contains("Error: ")) {
-            sb.append("Error: ");
-        }
 
-        String message = mMessage;
-
-        // If the error message already starts with the path, strip it out.
-        // This avoids redundant looking error messages you can end up with
-        // like for example for permission denied errors where the error message
-        // string itself contains the path as a prefix:
-        //    /my/full/path: /my/full/path (Permission denied)
-        if (mFilePositions.size() == 1) {
-           String path = mFilePositions.get(0).getSourceFile().getAbsolutePath();
-            if (path != null && message.startsWith(path)) {
-                int stripStart = path.length();
-                if (message.length() > stripStart && message.charAt(stripStart) == ':') {
-                    stripStart++;
+            // If the error message already starts with the path, strip it out.
+            // This avoids redundant looking error messages you can end up with
+            // like for example for permission denied errors where the error message
+            // string itself contains the path as a prefix:
+            //    /my/full/path: /my/full/path (Permission denied)
+            if (sourceFilePositions.size() == 1) {
+                File file = sourceFilePositions.get(0).getFile().getSourceFile();
+                if (file != null) {
+                    String path = file.getAbsolutePath();
+                    if (text.startsWith(path)) {
+                        int stripStart = path.length();
+                        if (text.length() > stripStart && text.charAt(stripStart) == ':') {
+                            stripStart++;
+                        }
+                        if (text.length() > stripStart && text.charAt(stripStart) == ' ') {
+                            stripStart++;
+                        }
+                        text = text.substring(stripStart);
+                    }
                 }
-                if (message.length() > stripStart && message.charAt(stripStart) == ' ') {
-                    stripStart++;
-                }
-                message = message.substring(stripStart);
             }
-        }
 
-        sb.append(message);
-        return sb.toString();
+            sb.append(text);
+            messages.add(sb.toString());
+        }
+        return Joiner.on('\n').join(messages);
     }
 
     @Override
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java b/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java
index 615b6f9..f124398 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/NodeUtils.java
@@ -18,6 +18,7 @@
 
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
 import com.android.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 
@@ -344,4 +345,13 @@
 
         return true;
     }
+
+    @Nullable
+    static String getAttribute(@NonNull Node node, @NonNull String attrName) {
+        Attr attr = (Attr) node.getAttributes().getNamedItem(attrName);
+        if (attr != null) {
+            return attr.getValue();
+        }
+        return null;
+    }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java
index e9243b8..3274494 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java
@@ -16,7 +16,9 @@
 
 package com.android.ide.common.res2;
 
+import com.android.SdkConstants;
 import com.android.annotations.NonNull;
+import com.google.common.base.Objects;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
@@ -44,7 +46,7 @@
      *
      * The source file is set on the item with {@link ResourceItem#setSource(DataFile)}
      *
-     * The type of the ResourceFile will be {@link FileType#SINGLE}.
+     * The type of the ResourceFile will be {@link FileType#SINGLE_FILE}.
      *
      * @param file the File
      * @param item the resource item
@@ -52,7 +54,7 @@
      */
     public ResourceFile(@NonNull File file, @NonNull ResourceItem item,
             @NonNull String qualifiers) {
-        super(file, FileType.SINGLE);
+        super(file, FileType.SINGLE_FILE);
         mQualifiers = qualifiers;
         init(item);
     }
@@ -62,7 +64,7 @@
      *
      * The source file is set on the items with {@link ResourceItem#setSource(DataFile)}
      *
-     * The type of the ResourceFile will be {@link FileType#MULTI}.
+     * The type of the ResourceFile will be {@link FileType#XML_VALUES}.
      *
      * @param file the File
      * @param items the resource items
@@ -70,11 +72,25 @@
      */
     public ResourceFile(@NonNull File file, @NonNull List<ResourceItem> items,
             @NonNull String qualifiers) {
-        super(file, FileType.MULTI);
+        this(file, items, qualifiers, FileType.XML_VALUES);
+    }
+
+    private ResourceFile(@NonNull File file, @NonNull List<ResourceItem> items,
+            @NonNull String qualifiers, @NonNull FileType fileType) {
+        super(file, fileType);
         mQualifiers = qualifiers;
         init(items);
     }
 
+    public static ResourceFile generatedFiles(
+            @NonNull File file,
+            @NonNull List<ResourceItem> items,
+            @NonNull String qualifiers) {
+        // TODO: Replace other constructors with named methods.
+        return new ResourceFile(file, items, qualifiers, FileType.GENERATED_FILES);
+    }
+
+
     @NonNull
     public String getQualifiers() {
         return mQualifiers;
@@ -89,13 +105,17 @@
     void addExtraAttributes(Document document, Node node, String namespaceUri) {
         NodeUtils.addAttribute(document, node, namespaceUri, ATTR_QUALIFIER,
                 getQualifiers());
+
+        if (getType() == FileType.GENERATED_FILES) {
+            NodeUtils.addAttribute(document, node, namespaceUri, SdkConstants.ATTR_PREPROCESSING, "true");
+        }
     }
 
     @Override
     public String toString() {
-        return "ResourceFile{" +
-                "mFile='" + getFile() + '\'' +
-                ", mQualifiers='" + mQualifiers + '\'' +
-                '}';
+        return Objects.toStringHelper(getClass())
+                .add("mFile", mFile)
+                .add("mQualifiers", mQualifiers)
+                .toString();
     }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java
index 3248524..0af7176 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java
@@ -68,10 +68,13 @@
 public class ResourceItem extends DataItem<ResourceFile>
         implements Configurable, Comparable<ResourceItem> {
 
+    @NonNull
     private final ResourceType mType;
 
+    @Nullable
     private Node mValue;
 
+    @Nullable
     protected ResourceValue mResourceValue;
 
     /**
@@ -148,7 +151,7 @@
      *
      * @param from the resource to copy the value from.
      */
-    void setValue(ResourceItem from) {
+    void setValue(@NonNull ResourceItem from) {
         mValue = from.mValue;
         setTouched();
     }
@@ -180,11 +183,20 @@
                     "ResourceItem.getKey called on object with no ResourceFile: " + this);
         }
         String qualifiers = getQualifiers();
-        if (!qualifiers.isEmpty()) {
-            return mType.getName() + "-" + qualifiers + "/" + getName();
+
+        String typeName = mType.getName();
+        if (mType == ResourceType.PUBLIC && mValue != null) {
+            String typeAttribute = ((Element) mValue).getAttribute(ATTR_TYPE);
+            if (typeAttribute != null) {
+                typeName += "_" + typeAttribute;
+            }
         }
 
-        return mType.getName() + "/" + getName();
+        if (!qualifiers.isEmpty()) {
+            return typeName + "-" + qualifiers + "/" + getName();
+        }
+
+        return typeName + "/" + getName();
     }
 
     @Override
@@ -660,9 +672,7 @@
     }
 
     @Override
-    Node getAdoptedNode(Document document) {
+    Node getDetailsXml(Document document) {
         return NodeUtils.adoptNode(document, mValue);
     }
-
-
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java
index d0563ad..2cfba18 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java
@@ -46,6 +46,7 @@
  * {@link ResourceFile}.
  */
 public class ResourceMerger extends DataMerger<ResourceItem, ResourceFile, ResourceSet> {
+    private static final String NODE_MERGED_ITEMS = "mergedItems";
 
     /**
      * Override of the normal ResourceItem to handle merged item cases.
@@ -86,7 +87,7 @@
         @Override
         @NonNull
         public FileType getSourceType() {
-            return FileType.MULTI;
+            return FileType.XML_VALUES;
         }
     }
 
@@ -98,10 +99,36 @@
     protected final Map<String, Map<String, ResourceItem>> mMergedItems = Maps.newHashMap();
 
 
+    /**
+     * Reads the {@link ResourceSet} from the blob XML. {@link ResourceMerger} deals with two kinds
+     * of sets - {@link GeneratedResourceSet} and "plain" {@link ResourceSet} . Instances of the
+     * former are marked with {@code generated="true"} attribute. Instances of the latter have a
+     * {@code generated-set} attribute that references the corresponding generated set by name.
+     * For any variant, the generated set has a lower priority, so it comes in the XML first. This
+     * means we will find it by name at this stage.
+     */
     @Override
     protected ResourceSet createFromXml(Node node) throws MergingException {
-        ResourceSet set = new ResourceSet("");
-        return (ResourceSet) set.createFromXml(node);
+        String generated = NodeUtils.getAttribute(node, "generated");
+        ResourceSet set;
+        if ("true".equals(generated)) {
+            set = new GeneratedResourceSet("");
+        } else {
+            set = new ResourceSet("");
+        }
+        ResourceSet newResourceSet = (ResourceSet) set.createFromXml(node);
+
+        String generatedSetName = NodeUtils.getAttribute(node, "generated-set");
+        if (generatedSetName != null) {
+            for (ResourceSet resourceSet : getDataSets()) {
+                if (resourceSet.getConfigName().equals(generatedSetName)) {
+                    newResourceSet.setGeneratedSet(resourceSet);
+                    break;
+                }
+            }
+        }
+
+        return newResourceSet;
     }
 
     @Override
@@ -214,7 +241,7 @@
                 }
             }
         } catch (ParserConfigurationException e) {
-            throw new MergingException(e);
+            throw MergingException.wrapException(e).build();
         }
     }
 
@@ -228,8 +255,21 @@
         return null;
     }
 
+    @NonNull
     @Override
-    protected void loadMergedItems(@NonNull Node mergedItemsNode) throws MergingException {
+    protected String getAdditionalDataTagName() {
+        return NODE_MERGED_ITEMS;
+    }
+
+    @Override
+    protected void loadAdditionalData(@NonNull Node mergedItemsNode, boolean incrementalState) throws MergingException {
+        // only load the merged item in incremental state.
+        // In non incremental state, they will be recreated by the touched
+        // items anyway.
+        if (!incrementalState) {
+            return;
+        }
+
         // loop on the qualifiers.
         NodeList configurationList = mergedItemsNode.getChildNodes();
 
@@ -269,8 +309,8 @@
     }
 
     @Override
-    protected void writeMergedItems(Document document, Node rootNode) {
-        Node mergedItemsNode = document.createElement(NODE_MERGED_ITEMS);
+    protected void writeAdditionalData(Document document, Node rootNode) {
+        Node mergedItemsNode = document.createElement(getAdditionalDataTagName());
         rootNode.appendChild(mergedItemsNode);
 
         for (String qualifier : mMergedItems.keySet()) {
@@ -283,7 +323,7 @@
             mergedItemsNode.appendChild(qualifierNode);
 
             for (ResourceItem item : itemMap.values()) {
-                Node adoptedNode = item.getAdoptedNode(document);
+                Node adoptedNode = item.getDetailsXml(document);
                 if (adoptedNode != null) {
                     qualifierNode.appendChild(adoptedNode);
                 }
@@ -317,4 +357,10 @@
 
         return null;
     }
+
+    @Override
+    public void addDataSet(ResourceSet resourceSet) {
+
+        super.addDataSet(resourceSet);
+    }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourcePreprocessor.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourcePreprocessor.java
new file mode 100644
index 0000000..71f20b3
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourcePreprocessor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.ide.common.res2;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * Provides functionality the resource merger needs for preprocessing resources during merge.
+ */
+public interface ResourcePreprocessor {
+    /** Checks if the given file should be replaced by N generated files. */
+    boolean needsPreprocessing(File file);
+
+    /** Returns the paths that should be generated for the given file. */
+    Collection<File> getFilesToBeGenerated(File original);
+
+    /** Actually generate the file based on the original file. */
+    void generateFile(File toBeGenerated, File original) throws IOException;
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java
index 88763b6..09795bf 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java
@@ -17,10 +17,13 @@
 package com.android.ide.common.res2;
 
 import static com.android.ide.common.res2.ResourceFile.ATTR_QUALIFIER;
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Preconditions.checkState;
 
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
 import com.android.ide.common.resources.configuration.FolderConfiguration;
 import com.android.resources.FolderTypeRelationship;
 import com.android.resources.ResourceConstants;
@@ -30,11 +33,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
-import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -47,15 +51,29 @@
 public class ResourceSet extends DataSet<ResourceItem, ResourceFile> {
 
     private boolean mNormalizeResources = false;
+    private ResourceSet mGeneratedSet;
+    private ResourcePreprocessor mPreprocessor;
 
     public ResourceSet(String name) {
-        super(name);
+        this(name, true /*validateEnabled*/);
+    }
+
+    public ResourceSet(String name, boolean validateEnabled) {
+        super(name, validateEnabled);
     }
 
     public void setNormalizeResources(boolean normalizeResources) {
         mNormalizeResources = normalizeResources;
     }
 
+    public void setGeneratedSet(ResourceSet generatedSet) {
+        mGeneratedSet = generatedSet;
+    }
+
+    public void setPreprocessor(ResourcePreprocessor preprocessor) {
+        mPreprocessor = preprocessor;
+    }
+
     @Override
     protected DataSet<ResourceItem, ResourceFile> createSet(String name) {
         return new ResourceSet(name);
@@ -75,14 +93,52 @@
     }
 
     @Override
-    protected ResourceFile createFileAndItems(@NonNull File file, @NonNull Node fileNode)
+    protected ResourceFile createFileAndItemsFromXml(@NonNull File file, @NonNull Node fileNode)
             throws MergingException {
-        Attr qualifierAttr = (Attr) fileNode.getAttributes().getNamedItem(ATTR_QUALIFIER);
-        String qualifier = qualifierAttr != null ? qualifierAttr.getValue() : "";
+        String qualifier = firstNonNull(NodeUtils.getAttribute(fileNode, ATTR_QUALIFIER), "");
+        String typeAttr = NodeUtils.getAttribute(fileNode, SdkConstants.ATTR_TYPE);
 
-        Attr typeAttr = (Attr) fileNode.getAttributes().getNamedItem(SdkConstants.ATTR_TYPE);
-        if (typeAttr == null) {
-            // multi res file
+        if (NodeUtils.getAttribute(fileNode, SdkConstants.ATTR_PREPROCESSING) != null) {
+            // FileType.GENERATED_FILES
+            NodeList childNodes = fileNode.getChildNodes();
+            int childCount = childNodes.getLength();
+
+            List<ResourceItem> resourceItems =
+                    Lists.newArrayListWithCapacity(childCount);
+
+            for (int i = 0; i < childCount; i++) {
+                Node childNode = childNodes.item(i);
+
+                String path = NodeUtils.getAttribute(childNode, SdkConstants.ATTR_PATH);
+                if (path == null) {
+                    continue;
+                }
+
+                File generatedFile = new File(path);
+                String resourceType = NodeUtils.getAttribute(childNode, SdkConstants.ATTR_TYPE);
+                if (resourceType == null) {
+                    continue;
+                }
+                String qualifers = NodeUtils.getAttribute(childNode, ATTR_QUALIFIER);
+                if (qualifers == null) {
+                    continue;
+                }
+
+                resourceItems.add(
+                        new GeneratedResourceItem(
+                                getNameForFile(generatedFile),
+                                generatedFile,
+                                FolderTypeRelationship
+                                        .getRelatedResourceTypes(
+                                                ResourceFolderType.getTypeByName(resourceType))
+                                        .get(0),
+                                qualifers));
+            }
+
+            return ResourceFile.generatedFiles(file, resourceItems, qualifier);
+        }
+        else if (typeAttr == null) {
+            // FileType.XML_VALUES
             List<ResourceItem> resourceList = Lists.newArrayList();
 
             // loop on each node that represent a resource
@@ -113,18 +169,20 @@
 
         } else {
             // single res file
-            ResourceType type = ResourceType.getEnum(typeAttr.getValue());
+            ResourceType type = ResourceType.getEnum(typeAttr);
             if (type == null) {
                 return null;
             }
 
-            Attr nameAttr = (Attr) fileNode.getAttributes().getNamedItem(ATTR_NAME);
+            String nameAttr = NodeUtils.getAttribute(fileNode, ATTR_NAME);
             if (nameAttr == null) {
                 return null;
             }
 
-            FileResourceNameValidator.validate(file, type);
-            ResourceItem item = new ResourceItem(nameAttr.getValue(), type, null);
+            if (getValidateEnabled()) {
+                FileResourceNameValidator.validate(file, type);
+            }
+            ResourceItem item = new ResourceItem(nameAttr, type, null);
             return new ResourceFile(file, item, qualifier);
         }
     }
@@ -132,17 +190,23 @@
     @Override
     protected void readSourceFolder(File sourceFolder, ILogger logger)
             throws MergingException {
+        List<Message> errors = Lists.newArrayList();
         File[] folders = sourceFolder.listFiles();
         if (folders != null) {
             for (File folder : folders) {
                 if (folder.isDirectory() && !isIgnored(folder)) {
                     FolderData folderData = getFolderData(folder);
                     if (folderData != null) {
-                        parseFolder(sourceFolder, folder, folderData, logger);
+                        try {
+                            parseFolder(sourceFolder, folder, folderData, logger);
+                        } catch (MergingException e) {
+                            errors.addAll(e.getMessages());
+                        }
                     }
                 }
             }
         }
+        MergingException.throwIfNonEmpty(errors);
     }
 
     @Override
@@ -159,79 +223,144 @@
     }
 
     @Override
-    protected boolean handleChangedFile(@NonNull File sourceFolder, @NonNull File changedFile)
+    protected boolean handleNewFile(File sourceFolder, File file, ILogger logger)
             throws MergingException {
+        ResourceFile resourceFile = createFileAndItems(sourceFolder, file, logger);
+        processNewResourceFile(sourceFolder, resourceFile);
+        return true;
+    }
 
+    @Override
+    protected boolean handleRemovedFile(File removedFile) {
+        if (mGeneratedSet != null && mGeneratedSet.getDataFile(removedFile) != null) {
+            return mGeneratedSet.handleRemovedFile(removedFile);
+        } else {
+            return super.handleRemovedFile(removedFile);
+        }
+    }
+
+    @Override
+    protected boolean handleChangedFile(
+            @NonNull File sourceFolder,
+            @NonNull File changedFile,
+            @NonNull ILogger logger) throws MergingException {
         FolderData folderData = getFolderData(changedFile.getParentFile());
         if (folderData == null) {
             return true;
         }
 
         ResourceFile resourceFile = getDataFile(changedFile);
-
-        if (resourceFile == null) {
-            String message = String.format(
-                    "In DataSet '%s', no data file for changedFile '%s'. "
-                            + "This is an internal error in the incremental builds code; "
-                            + "to work around it, try doing a full clean build.",
-                    getConfigName(), changedFile.getAbsolutePath());
-            throw new MergingException(message).addFile(changedFile);
+        if (mGeneratedSet == null) {
+            // This is a generated set.
+            doHandleChangedFile(changedFile, resourceFile);
+            return true;
         }
 
-        //noinspection VariableNotUsedInsideIf
-        if (folderData.type != null) {
-            // single res file
-            resourceFile.getItem().setTouched();
+        ResourceFile generatedSetResourceFile = mGeneratedSet.getDataFile(changedFile);
+        boolean needsPreprocessing = needsPreprocessing(changedFile);
+
+        if (resourceFile != null && generatedSetResourceFile == null && needsPreprocessing) {
+            // It didn't use to need preprocessing, but it does now.
+            handleRemovedFile(changedFile);
+            mGeneratedSet.handleNewFile(sourceFolder, changedFile, logger);
+        } else if (resourceFile == null
+                && generatedSetResourceFile != null
+                && !needsPreprocessing) {
+            // It used to need preprocessing, but not anymore.
+            mGeneratedSet.handleRemovedFile(changedFile);
+            handleNewFile(sourceFolder, changedFile, logger);
+        } else if (resourceFile == null
+                && generatedSetResourceFile != null
+                && needsPreprocessing) {
+            // Delegate to the generated set.
+            mGeneratedSet.handleChangedFile(sourceFolder, changedFile, logger);
+        } else if (resourceFile != null
+                && !needsPreprocessing
+                && generatedSetResourceFile == null) {
+            // The "normal" case, handle it here.
+            doHandleChangedFile(changedFile, resourceFile);
         } else {
-            // multi res. Need to parse the file and compare the items one by one.
-            ValueResourceParser2 parser = new ValueResourceParser2(changedFile);
+            // Something strange happened.
+            throw MergingException.withMessage("In DataSet '%s', no data file for changedFile. "
+                            + "This is an internal error in the incremental builds code; "
+                            + "to work around it, try doing a full clean build.",
+                    getConfigName()).withFile(changedFile).build();
+        }
 
-            List<ResourceItem> parsedItems = parser.parseFile();
-            Map<String, ResourceItem> oldItems = Maps.newHashMap(resourceFile.getItemMap());
-            Map<String, ResourceItem> newItems  = Maps.newHashMap();
+        return true;
+    }
 
-            // create a fake ResourceFile to be able to call resource.getKey();
-            // It's ok because we never use this instance anyway.
-            ResourceFile fakeResourceFile = new ResourceFile(changedFile, parsedItems,
-                    resourceFile.getQualifiers());
+    private void doHandleChangedFile(@NonNull File changedFile, ResourceFile resourceFile)
+            throws MergingException {
+        switch (resourceFile.getType()) {
+            case SINGLE_FILE:
+                // single res file
+                resourceFile.getItem().setTouched();
+                break;
+            case GENERATED_FILES:
+                handleChangedItems(resourceFile,
+                        getResourceItemsForGeneratedFiles(changedFile));
+                break;
+            case XML_VALUES:
+                // multi res. Need to parse the file and compare the items one by one.
+                ValueResourceParser2 parser = new ValueResourceParser2(changedFile);
 
-            for (ResourceItem newItem : parsedItems) {
-                String newKey = newItem.getKey();
-                ResourceItem oldItem = oldItems.get(newKey);
+                List<ResourceItem> parsedItems = parser.parseFile();
+                handleChangedItems(resourceFile, parsedItems);
+                break;
+            default:
+                throw new IllegalStateException();
+        }
+    }
 
-                if (oldItem == null) {
-                    // this is a new item
-                    newItem.setTouched();
-                    newItems.put(newKey, newItem);
-                } else {
-                    // remove it from the list of oldItems (this is to detect deletion)
-                    //noinspection SuspiciousMethodCalls
-                    oldItems.remove(oldItem.getKey());
+    private void handleChangedItems(
+            ResourceFile resourceFile,
+            List<ResourceItem> currentItems) throws MergingException {
+        Map<String, ResourceItem> oldItems = Maps.newHashMap(resourceFile.getItemMap());
+        Map<String, ResourceItem> addedItems = Maps.newHashMap();
 
-                    // now compare the items
+        // Set the source of newly determined items, so we can call getKey() on them.
+        for (ResourceItem currentItem : currentItems) {
+            currentItem.setSource(resourceFile);
+        }
+
+        for (ResourceItem newItem : currentItems) {
+            String newKey = newItem.getKey();
+            ResourceItem oldItem = oldItems.get(newKey);
+
+            if (oldItem == null) {
+                // this is a new item
+                newItem.setTouched();
+                addedItems.put(newKey, newItem);
+            } else {
+                // remove it from the list of oldItems (this is to detect deletion)
+                //noinspection SuspiciousMethodCalls
+                oldItems.remove(oldItem.getKey());
+
+                if (oldItem.getSource().getType() == DataFile.FileType.XML_VALUES) {
                     if (!oldItem.compareValueWith(newItem)) {
-                        // if the values are different, take the values from the newItems
+                        // if the values are different, take the values from the newItem
                         // and update the old item status.
 
                         oldItem.setValue(newItem);
                     }
+                } else {
+                    oldItem.setTouched();
                 }
             }
-
-            // at this point oldItems is left with the deleted items.
-            // just update their status to removed.
-            for (ResourceItem deletedItem : oldItems.values()) {
-                deletedItem.setRemoved();
-            }
-
-            // Now we need to add the new items to the resource file and the main map
-            resourceFile.addItems(newItems.values());
-            for (Map.Entry<String, ResourceItem> entry : newItems.entrySet()) {
-                addItem(entry.getValue(), entry.getKey());
-            }
         }
 
-       return true;
+        // at this point oldItems is left with the deleted items.
+        // just update their status to removed.
+        for (ResourceItem deletedItem : oldItems.values()) {
+            deletedItem.setRemoved();
+        }
+
+        // Now we need to add the new items to the resource file and the main map
+        resourceFile.addItems(addedItems.values());
+        for (Map.Entry<String, ResourceItem> entry : addedItems.entrySet()) {
+            addItem(entry.getValue(), entry.getKey());
+        }
     }
 
     /**
@@ -256,27 +385,40 @@
                 }
 
                 ResourceFile resourceFile = createResourceFile(file, folderData, logger);
-                if (resourceFile != null) {
-                    processNewDataFile(sourceFolder, resourceFile, true /*setTouched*/);
-                }
+                processNewResourceFile(sourceFolder, resourceFile);
             }
         }
     }
 
-    private static ResourceFile createResourceFile(@NonNull File file,
+    private void processNewResourceFile(File sourceFolder, ResourceFile resourceFile)
+            throws MergingException {
+        if (resourceFile != null) {
+            if (resourceFile.getType() == DataFile.FileType.GENERATED_FILES
+                    && mGeneratedSet != null) {
+                mGeneratedSet.processNewDataFile(sourceFolder, resourceFile, true);
+            } else {
+                processNewDataFile(sourceFolder, resourceFile, true /*setTouched*/);
+            }
+        }
+    }
+
+    private ResourceFile createResourceFile(@NonNull File file,
             @NonNull FolderData folderData, @NonNull ILogger logger) throws MergingException {
         if (folderData.type != null) {
             FileResourceNameValidator.validate(file, folderData.type);
-            String name = file.getName();
-            int pos = name.indexOf('.'); // get the resource name based on the filename
-            if (pos >= 0) {
-                name = name.substring(0, pos);
-            }
+            String name = getNameForFile(file);
 
-            return new ResourceFile(
-                    file,
-                    new ResourceItem(name, folderData.type, null),
-                    folderData.qualifiers);
+            if (needsPreprocessing(file)) {
+                return ResourceFile.generatedFiles(
+                        file,
+                        getResourceItemsForGeneratedFiles(file),
+                        folderData.qualifiers);
+            } else {
+                return new ResourceFile(
+                        file,
+                        new ResourceItem(name, folderData.type, null),
+                        folderData.qualifiers);
+            }
         } else {
             try {
                 ValueResourceParser2 parser = new ValueResourceParser2(file);
@@ -284,13 +426,51 @@
 
                 return new ResourceFile(file, items, folderData.qualifiers);
             } catch (MergingException e) {
-                e.setFile(file);
                 logger.error(e, "Failed to parse %s", file.getAbsolutePath());
                 throw e;
             }
         }
     }
 
+    private boolean needsPreprocessing(@NonNull File file) {
+        return mPreprocessor != null && mPreprocessor.needsPreprocessing(file);
+    }
+
+    @NonNull
+    private List<ResourceItem> getResourceItemsForGeneratedFiles(
+            @NonNull File file)
+            throws MergingException {
+        List<ResourceItem> resourceItems = new ArrayList<ResourceItem>();
+
+        for (File generatedFile : mPreprocessor.getFilesToBeGenerated(file)) {
+            FolderData generatedFileFolderData =
+                    getFolderData(generatedFile.getParentFile());
+
+            checkState(
+                    generatedFileFolderData != null,
+                    "Can't determine folder type for %s",
+                    generatedFile.getPath());
+
+            resourceItems.add(
+                    new GeneratedResourceItem(
+                            getNameForFile(generatedFile),
+                            generatedFile,
+                            generatedFileFolderData.type,
+                            generatedFileFolderData.qualifiers));
+        }
+        return resourceItems;
+    }
+
+    @NonNull
+    private static String getNameForFile(@NonNull File file) {
+        String name = file.getName();
+        int pos = name.indexOf('.'); // get the resource name based on the filename
+        if (pos >= 0) {
+            name = name.substring(0, pos);
+        }
+        return name;
+    }
+
     /**
      * temp structure containing a qualifier string and a {@link com.android.resources.ResourceType}.
      */
@@ -301,9 +481,11 @@
     }
 
     /**
-     * Returns a FolderData for the given folder
+     * Returns a FolderData for the given folder.
+     *
      * @param folder the folder.
-     * @return the FolderData object.
+     * @return the FolderData object, or null if we can't determine the {#link ResourceFolderType}
+     *         of the folder.
      */
     @Nullable
     private FolderData getFolderData(File folder) throws MergingException {
@@ -319,7 +501,8 @@
 
             FolderConfiguration folderConfiguration = FolderConfiguration.getConfigForFolder(folderName);
             if (folderConfiguration == null) {
-                throw new MergingException("Invalid resource directory name").addFile(folder);
+                throw MergingException.withMessage("Invalid resource directory name")
+                        .withFile(folder).build();
             }
 
             if (mNormalizeResources) {
@@ -341,4 +524,18 @@
 
         return fd;
     }
+
+    @Override
+    void appendToXml(@NonNull Node setNode, @NonNull Document document,
+            @NonNull MergeConsumer<ResourceItem> consumer) {
+        if (mGeneratedSet != null) {
+            NodeUtils.addAttribute(
+                    document,
+                    setNode,
+                    null,
+                    "generated-set",
+                    mGeneratedSet.getConfigName());
+        }
+        super.appendToXml(setNode, document, consumer);
+    }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java
index e8cb0c3..0639052 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java
@@ -43,11 +43,7 @@
         String error = getErrorText(resourceName, resourceType);
         if (error != null) {
             // TODO find location in file.
-            if (file != null) {
-                throw new MergingException(error).setFile(file);
-            } else {
-                throw new MergingException(error);
-            }
+            throw MergingException.withMessage( error).withFile(file).build();
         }
     }
 
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java
index 58e8145..fdf46fd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java
@@ -27,6 +27,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.resources.ResourceType;
+import com.android.utils.PositionXmlParser;
 import com.android.utils.XmlUtils;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -37,9 +38,10 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
 
+import java.io.BufferedInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
@@ -99,18 +101,13 @@
             ResourceItem resource = getResource(node, mFile);
             if (resource != null) {
                 // check this is not a dup
-                checkDuplicate(resource, map);
+                checkDuplicate(resource, map, mFile);
 
                 resources.add(resource);
 
                 if (resource.getType() == ResourceType.DECLARE_STYLEABLE) {
                     // Need to also create ATTR items for its children
-                    try {
-                        addStyleableItems(node, resources, map, mFile);
-                    } catch (MergingException e) {
-                        e.setFile(mFile);
-                        throw e;
-                    }
+                    addStyleableItems(node, resources, map, mFile);
                 }
             }
         }
@@ -167,12 +164,11 @@
             if (type != null) {
                 return type;
             }
-            throw new MergingException(String.format("Unsupported type '%s'", typeString))
-                    .addFileIfNonNull(from);
+            throw MergingException.withMessage("Unsupported type '%s'", typeString).withFile(from)
+                    .build();
         }
 
-        throw new MergingException(String.format("Unsupported node '%s'", nodeName))
-                .addFileIfNonNull(from);
+        throw MergingException.withMessage("Unsupported node '%s'", nodeName).withFile(from).build();
     }
 
     /**
@@ -199,15 +195,13 @@
     @NonNull
     static Document parseDocument(@NonNull File file) throws MergingException {
         try {
-            return XmlUtils.parseUtfXmlFile(file, true /*namespaceAware*/);
-        } catch (SAXParseException e) {
-            throw new MergingException(e).addFilePosition(file, e);
-        } catch (ParserConfigurationException e) {
-            throw new MergingException(e).addFile(file);
+            return PositionXmlParser.parse(new BufferedInputStream(new FileInputStream(file)));
         } catch (SAXException e) {
-            throw new MergingException(e).addFile(file);
+            throw MergingException.wrapException(e).withFile(file).build();
+        } catch (ParserConfigurationException e) {
+            throw MergingException.wrapException(e).withFile(file).build();
         } catch (IOException e) {
-            throw new MergingException(e).addFile(file);
+            throw MergingException.wrapException(e).withFile(file).build();
         }
     }
 
@@ -241,7 +235,7 @@
                 // is the attribute in the android namespace?
                 if (!resource.getName().startsWith(ANDROID_NS_NAME_PREFIX)) {
                     if (hasFormatAttribute(node) || XmlUtils.hasElementChildren(node)) {
-                        checkDuplicate(resource, map);
+                        checkDuplicate(resource, map, from);
                         resource.setIgnoredFromDiskMerge(true);
                         list.add(resource);
                     }
@@ -251,7 +245,8 @@
     }
 
     private static void checkDuplicate(@NonNull ResourceItem resource,
-                                       @Nullable Map<ResourceType, Set<String>> map)
+                                       @Nullable Map<ResourceType, Set<String>> map,
+                                       @Nullable File from)
             throws MergingException {
         if (map == null) {
             return;
@@ -263,10 +258,10 @@
             set = Sets.newHashSet(name);
             map.put(resource.getType(), set);
         } else {
-            if (set.contains(name)) {
-                throw new MergingException(String.format(
+            if (set.contains(name) && resource.getType() != ResourceType.PUBLIC) {
+                throw MergingException.withMessage(
                         "Found item %s/%s more than one time",
-                        resource.getType().getDisplayName(), name));
+                        resource.getType().getDisplayName(), name).withFile(from).build();
             }
 
             set.add(name);
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java b/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java
index c895c3a..416ae39 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java
@@ -1345,7 +1345,7 @@
             "Haitian; Haitian Creole",              // Code hat/ht
             "Hausa",                                // Code hau/ha
             "Hawaiian",                             // Code haw
-            "Hebrew",                               // Code heb/he
+            "Hebrew",                               // Code heb/iw
             "Herero",                               // Code her/hz
             "Hiligaynon",                           // Code hil
             "Himachali languages; Western Pahari languages",// Code him
@@ -1368,7 +1368,7 @@
             "Iloko",                                // Code ilo
             "Interlingua (International Auxiliary Language Association)",// Code ina/ia
             "Indic languages",                      // Code inc
-            "Indonesian",                           // Code ind/id
+            "Indonesian",                           // Code ind/in
             "Indo-European languages",              // Code ine
             "Ingush",                               // Code inh
             "Inupiaq",                              // Code ipk/ik
@@ -1684,7 +1684,7 @@
             "Yao",                                  // Code yao
             "Yapese",                               // Code yap
             "Yangben",                              // Code yav
-            "Yiddish",                              // Code yid/yi
+            "Yiddish",                              // Code yid/ji
             "Yoruba",                               // Code yor/yo
             "Yupik languages",                      // Code ypk
             "Zapotec",                              // Code zap
@@ -1762,9 +1762,9 @@
             -1,  -1,  47,  -1,  -1,  -1,  -1,  48,  43,  -1,  -1,
             -1,  -1,  -1,  -1,  -1,  50,  49,  51,  54,  -1,  -1,
             -1,  -1,  -1,  -1,  -1,  52,  -1,  53,  -1,  -1,  -1,
-            60,  55,  -1,  56,  63,  -1,  -1,  57,  -1,  -1,  58,
+            60,  55,  -1,  75,  63,  -1,  -1,  57,  -1,  -1,  58,
             59,  -1,  61,  -1,  62,  -1,  67,  71,  68,  -1,  74,
-            66,  -1,  64,  -1,  65,  -1,  -1,  69,  -1,  -1,  72,
+            66,  -1,  64,  -1,  70,  -1,  -1,  69,  -1,  -1,  72,
             73,  78,  -1,  -1,  -1,  76,  -1,  -1,  -1,  -1,  -1,
             84,  -1,  86,  -1,  89,  79,  88,  -1,  83,  -1,  -1,
             -1,  -1,  -1,  85,  -1,  -1,  81, 139,  93,  -1,  -1,
@@ -1793,7 +1793,7 @@
             -1,  -1, 170,  -1,  -1,  -1,  -1,  -1, 172, 173,  -1,
             -1, 174, 175,  -1, 176, 177, 178,  -1,  -1,  -1,  -1,
             -1,  -1,  -1,  -1, 179, 180,  -1, 181,  -1,  -1,  -1,
-            -1, 182, 183,  -1,  -1,  -1,  -1,  -1, 184, 185,  -1,
+            -1,  77, 183,  -1,  -1,  -1,  -1,  -1, 184, 185,  -1,
             186,  -1,  -1
     };
     private static final String[] ISO_3166_2_CODES = new String[] {
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java b/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java
index 8531f3e..a1f053f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java
@@ -132,7 +132,7 @@
     @Override
     public boolean hasResources(ResourceType type) {
         Map<String, ResourceValue> list = mResourceItems.get(type);
-        return (list != null && list.size() > 0);
+        return (list != null && !list.isEmpty());
     }
 
     private void updateResourceItems(ScanningContext context) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java
index bed1a5a..3059cb8 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java
@@ -197,7 +197,7 @@
         }
 
         // We only want to return false if there's no default and more than 0 items.
-        return (mFiles.size() == 0);
+        return (mFiles.isEmpty());
     }
 
     /**
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java
index b3106a4..f218abe 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java
@@ -26,7 +26,6 @@
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.ide.common.res2.AbstractResourceRepository;
-import com.android.ide.common.res2.MergingException;
 import com.android.ide.common.resources.configuration.FolderConfiguration;
 import com.android.resources.ResourceType;
 
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java
index d308751..237bdbf 100755
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java
@@ -515,7 +515,7 @@
         ensureInitialized();
 
         Map<String, ResourceItem> items = mResourceMap.get(type);
-        return (items != null && items.size() > 0);
+        return (items != null && !items.isEmpty());
     }
 
     /**
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java
index a0749c8..23f471b 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceResolver.java
@@ -758,7 +758,7 @@
         if (value instanceof StyleResourceValue) {
             StyleResourceValue srv = (StyleResourceValue) value;
             String name = srv.getName();
-            if (name.startsWith(THEME_NAME_DOT) || name.equals(THEME_NAME)) {
+            if (srv.isFramework() && (name.equals(THEME_NAME) || name.startsWith(THEME_NAME_DOT))) {
                 if (cache != null) {
                     cache.put(value, true);
                 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java
index 076d9f3..c1b92ce 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java
@@ -47,14 +47,15 @@
     public final boolean create;
 
     /** Whether this is a theme resource reference */
-    public boolean theme;
+    public final boolean theme;
 
     private ResourceUrl(@NonNull ResourceType type, @NonNull String name,
-            boolean framework, boolean create) {
+            boolean framework, boolean create, boolean theme) {
         this.type = type;
         this.name = name;
         this.framework = framework;
         this.create = create;
+        this.theme = theme;
     }
 
     /**
@@ -67,7 +68,7 @@
      */
     public static ResourceUrl create(@NonNull ResourceType type, @NonNull String name,
             boolean framework, boolean create) {
-        return new ResourceUrl(type, name, framework, create);
+        return new ResourceUrl(type, name, framework, create, false);
     }
 
     public static ResourceUrl create(@NonNull ResourceValue value) {
@@ -82,33 +83,44 @@
      */
     @Nullable
     public static ResourceUrl parse(@NonNull String url) {
+        return parse(url, false);
+    }
+
+    /**
+     * Return the resource type of the given url, and the resource name.
+     *
+     * @param url the resource url to be parsed
+     * @param forceFramework force the returned value to be a framework resource.
+     * @return a pair of the resource type and the resource name
+     */
+    @Nullable
+    public static ResourceUrl parse(@NonNull String url, boolean forceFramework) {
+        boolean isTheme = false;
         // Handle theme references
         if (url.startsWith(PREFIX_THEME_REF)) {
+            isTheme = true;
             String remainder = url.substring(PREFIX_THEME_REF.length());
             if (url.startsWith(ATTR_REF_PREFIX)) {
                 url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length());
-                return setTheme(parse(url));
-            }
-            int colon = url.indexOf(':');
-            if (colon != -1) {
-                // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig
-                if (remainder.indexOf('/', colon) == -1) {
-                    remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/'
-                            + remainder.substring(colon);
-                }
-                url = PREFIX_RESOURCE_REF + remainder;
-                return setTheme(parse(url));
             } else {
-                int slash = url.indexOf('/');
-                if (slash == -1) {
-                    url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder;
-                    return setTheme(parse(url));
+                int colon = url.indexOf(':');
+                if (colon != -1) {
+                    // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig
+                    if (remainder.indexOf('/', colon) == -1) {
+                        remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/'
+                                + remainder.substring(colon);
+                    }
+                    url = PREFIX_RESOURCE_REF + remainder;
+                } else {
+                    int slash = url.indexOf('/');
+                    if (slash == -1) {
+                        url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder;
+                    }
                 }
             }
         }
 
-        if (!url.startsWith(PREFIX_RESOURCE_REF) || url.equals(REFERENCE_NULL)
-                || url.equals(REFERENCE_EMPTY) || url.equals(REFERENCE_UNDEFINED)) {
+        if (!url.startsWith(PREFIX_RESOURCE_REF) || isNullOrEmpty(url)) {
             return null;
         }
 
@@ -123,7 +135,7 @@
         int typeBegin = create ? 2 : 1;
 
         int colon = url.lastIndexOf(':', typeEnd);
-        boolean framework = false;
+        boolean framework = forceFramework;
         if (colon != -1) {
             if (url.startsWith(ANDROID_NS_NAME, typeBegin)) {
                 framework = true;
@@ -136,16 +148,13 @@
             return null;
         }
         String name = url.substring(nameBegin);
-        return new ResourceUrl(type, name, framework, create);
+        return new ResourceUrl(type, name, framework, create, isTheme);
     }
 
-    /** Marks the given url, if any, as corresponding to a theme attribute */
-    @Nullable
-    private static ResourceUrl setTheme(@Nullable ResourceUrl url) {
-        if (url != null) {
-            url.theme = true;
-        }
-        return url;
+    /** Returns if the resource url is @null, @empty or @undefined. */
+    public static boolean isNullOrEmpty(@NonNull String url) {
+        return url.equals(REFERENCE_NULL) || url.equals(REFERENCE_EMPTY) ||
+                url.equals(REFERENCE_UNDEFINED);
     }
 
     /**
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java b/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java
index 936808d..19cc5fc 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java
@@ -16,6 +16,16 @@
 
 package com.android.ide.common.resources;
 
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX_LEN;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_PARENT;
+import static com.android.SdkConstants.ATTR_TYPE;
+import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.SdkConstants.TAG_ITEM;
+import static com.android.SdkConstants.TAG_RESOURCES;
+
+import com.android.ide.common.rendering.api.ArrayResourceValue;
 import com.android.ide.common.rendering.api.AttrResourceValue;
 import com.android.ide.common.rendering.api.DeclareStyleableResourceValue;
 import com.android.ide.common.rendering.api.ItemResourceValue;
@@ -33,27 +43,17 @@
  */
 public final class ValueResourceParser extends DefaultHandler {
 
-    // TODO: reuse definitions from somewhere else.
-    private static final String NODE_RESOURCES = "resources";
-    private static final String NODE_ITEM = "item";
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_TYPE = "type";
-    private static final String ATTR_PARENT = "parent";
-    private static final String ATTR_VALUE = "value";
-
-    private static final String DEFAULT_NS_PREFIX = "android:";
-    private static final int DEFAULT_NS_PREFIX_LEN = DEFAULT_NS_PREFIX.length();
-
     public interface IValueResourceRepository {
         void addResourceValue(ResourceValue value);
         boolean hasResourceValue(ResourceType type, String name);
     }
 
-    private boolean inResources = false;
-    private int mDepth = 0;
-    private ResourceValue mCurrentValue = null;
-    private StyleResourceValue mCurrentStyle = null;
-    private DeclareStyleableResourceValue mCurrentDeclareStyleable = null;
+    private boolean inResources;
+    private int mDepth;
+    private ResourceValue mCurrentValue;
+    private ArrayResourceValue mArrayResourceValue;
+    private StyleResourceValue mCurrentStyle;
+    private DeclareStyleableResourceValue mCurrentDeclareStyleable;
     private AttrResourceValue mCurrentAttr;
     private IValueResourceRepository mRepository;
     private final boolean mIsFramework;
@@ -71,15 +71,20 @@
             mCurrentValue.setValue(value);
         }
 
-        if (inResources && qName.equals(NODE_RESOURCES)) {
+        if (inResources && qName.equals(TAG_RESOURCES)) {
             inResources = false;
         } else if (mDepth == 2) {
             mCurrentValue = null;
             mCurrentStyle = null;
             mCurrentDeclareStyleable = null;
             mCurrentAttr = null;
+            mArrayResourceValue = null;
         } else if (mDepth == 3) {
+            if (mArrayResourceValue != null && mCurrentValue != null) {
+                mArrayResourceValue.addElement(mCurrentValue.getValue());
+            }
             mCurrentValue = null;
+            //noinspection VariableNotUsedInsideIf
             if (mCurrentDeclareStyleable != null) {
                 mCurrentAttr = null;
             }
@@ -94,11 +99,11 @@
             throws SAXException {
         try {
             mDepth++;
-            if (inResources == false && mDepth == 1) {
-                if (qName.equals(NODE_RESOURCES)) {
+            if (!inResources && mDepth == 1) {
+                if (qName.equals(TAG_RESOURCES)) {
                     inResources = true;
                 }
-            } else if (mDepth == 2 && inResources == true) {
+            } else if (mDepth == 2 && inResources) {
                 ResourceType type = getType(qName, attributes);
 
                 if (type != null) {
@@ -121,6 +126,10 @@
                                 mCurrentAttr = new AttrResourceValue(type, name, mIsFramework);
                                 mRepository.addResourceValue(mCurrentAttr);
                                 break;
+                            case ARRAY:
+                                mArrayResourceValue = new ArrayResourceValue(name, mIsFramework);
+                                mRepository.addResourceValue(mArrayResourceValue);
+                                break;
                             default:
                                 mCurrentValue = new ResourceValue(type, name, mIsFramework);
                                 mRepository.addResourceValue(mCurrentValue);
@@ -136,8 +145,8 @@
                     if (mCurrentStyle != null) {
                         // is the attribute in the android namespace?
                         boolean isFrameworkAttr = mIsFramework;
-                        if (name.startsWith(DEFAULT_NS_PREFIX)) {
-                            name = name.substring(DEFAULT_NS_PREFIX_LEN);
+                        if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+                            name = name.substring(ANDROID_NS_NAME_PREFIX_LEN);
                             isFrameworkAttr = true;
                         }
 
@@ -146,8 +155,8 @@
                     } else if (mCurrentDeclareStyleable != null) {
                         // is the attribute in the android namespace?
                         boolean isFramework = mIsFramework;
-                        if (name.startsWith(DEFAULT_NS_PREFIX)) {
-                            name = name.substring(DEFAULT_NS_PREFIX_LEN);
+                        if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+                            name = name.substring(ANDROID_NS_NAME_PREFIX_LEN);
                             isFramework = true;
                         }
 
@@ -168,9 +177,13 @@
                         } catch (NumberFormatException e) {
                             // pass, we'll just ignore this value
                         }
-
                     }
-                }
+                } else //noinspection VariableNotUsedInsideIf
+                    if (mArrayResourceValue != null) {
+                        // Create a temporary resource value to hold the item's value. The value is
+                        // not added to the repository, since it's just a holder.
+                        mCurrentValue = new ResourceValue(null, null, mIsFramework);
+                    }
             } else if (mDepth == 4 && mCurrentAttr != null) {
                 // get the enum/flag name
                 String name = attributes.getValue(ATTR_NAME);
@@ -189,22 +202,20 @@
         }
     }
 
-    private ResourceType getType(String qName, Attributes attributes) {
+    private static ResourceType getType(String qName, Attributes attributes) {
         String typeValue;
 
         // if the node is <item>, we get the type from the attribute "type"
-        if (NODE_ITEM.equals(qName)) {
+        if (TAG_ITEM.equals(qName)) {
             typeValue = attributes.getValue(ATTR_TYPE);
         } else {
             // the type is the name of the node.
             typeValue = qName;
         }
 
-        ResourceType type = ResourceType.getEnum(typeValue);
-        return type;
+        return ResourceType.getEnum(typeValue);
     }
 
-
     @Override
     public void characters(char[] ch, int start, int length) throws SAXException {
         if (mCurrentValue != null) {
@@ -216,6 +227,4 @@
             }
         }
     }
-
-
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java
index f3ba97d..217a1b8 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java
@@ -18,6 +18,7 @@
 
 import com.android.annotations.Nullable;
 import com.android.resources.NightMode;
+import com.android.resources.ScreenRound;
 import com.android.resources.UiMode;
 import com.android.sdklib.devices.Device;
 import com.android.sdklib.devices.Hardware;
@@ -58,6 +59,12 @@
         config.setScreenSizeQualifier(new ScreenSizeQualifier(screen.getSize()));
         config.setTextInputMethodQualifier(new TextInputMethodQualifier(hw.getKeyboard()));
         config.setTouchTypeQualifier(new TouchScreenQualifier(screen.getMechanism()));
+        ScreenRound screenRound = screen.getScreenRound();
+        if (screenRound == null) {
+            // The default is not round.
+            screenRound = ScreenRound.NOTROUND;
+        }
+        config.setScreenRoundQualifier(new ScreenRoundQualifier(screenRound));
 
         config.setKeyboardStateQualifier(new KeyboardStateQualifier(state.getKeyState()));
         config.setNavigationStateQualifier(new NavigationStateQualifier(state.getNavState()));
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java
index 1adad89..ccb66d0 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/FolderConfiguration.java
@@ -53,7 +53,7 @@
     public static final Splitter QUALIFIER_SPLITTER = Splitter.on('-');
 
 
-    private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
+    private final ResourceQualifier[] mQualifiers;
 
     private static final int INDEX_COUNTRY_CODE          = 0;
     private static final int INDEX_NETWORK_CODE          = 1;
@@ -64,19 +64,28 @@
     private static final int INDEX_SCREEN_HEIGHT         = 6;
     private static final int INDEX_SCREEN_LAYOUT_SIZE    = 7;
     private static final int INDEX_SCREEN_RATIO          = 8;
-    private static final int INDEX_SCREEN_ORIENTATION    = 9;
-    private static final int INDEX_UI_MODE               = 10;
-    private static final int INDEX_NIGHT_MODE            = 11;
-    private static final int INDEX_PIXEL_DENSITY         = 12;
-    private static final int INDEX_TOUCH_TYPE            = 13;
-    private static final int INDEX_KEYBOARD_STATE        = 14;
-    private static final int INDEX_TEXT_INPUT_METHOD     = 15;
-    private static final int INDEX_NAVIGATION_STATE      = 16;
-    private static final int INDEX_NAVIGATION_METHOD     = 17;
-    private static final int INDEX_SCREEN_DIMENSION      = 18;
-    private static final int INDEX_VERSION               = 19;
-    private static final int INDEX_COUNT                 = 20;
+    private static final int INDEX_SCREEN_ROUND          = 9;
+    private static final int INDEX_SCREEN_ORIENTATION    = 10;
+    private static final int INDEX_UI_MODE               = 11;
+    private static final int INDEX_NIGHT_MODE            = 12;
+    private static final int INDEX_PIXEL_DENSITY         = 13;
+    private static final int INDEX_TOUCH_TYPE            = 14;
+    private static final int INDEX_KEYBOARD_STATE        = 15;
+    private static final int INDEX_TEXT_INPUT_METHOD     = 16;
+    private static final int INDEX_NAVIGATION_STATE      = 17;
+    private static final int INDEX_NAVIGATION_METHOD     = 18;
+    private static final int INDEX_SCREEN_DIMENSION      = 19;
+    private static final int INDEX_VERSION               = 20;
+    private static final int INDEX_COUNT                 = 21;
 
+    public FolderConfiguration() {
+        mQualifiers = new ResourceQualifier[INDEX_COUNT];
+    }
+
+    private FolderConfiguration(ResourceQualifier[] qualifiers) {
+        this();
+        System.arraycopy(qualifiers, 0, mQualifiers, 0, INDEX_COUNT);
+    }
     /**
      * Creates a {@link FolderConfiguration} matching the folder segments.
      * @param folderSegments The segments of the folder name. The first segments should contain
@@ -302,6 +311,15 @@
     }
 
     /**
+     * Creates a copy of the given {@link FolderConfiguration}, that can be modified without
+     * affecting the original.
+     */
+    @NonNull
+    public static FolderConfiguration copyOf(@NonNull FolderConfiguration original) {
+        return new FolderConfiguration(original.mQualifiers);
+    }
+
+    /**
      * Creates a {@link FolderConfiguration} matching the given qualifier string
      * (just the qualifiers; e.g. for a folder like "values-en-rUS" this would be "en-rUS").
      *
@@ -310,7 +328,11 @@
      */
     @Nullable
     public static FolderConfiguration getConfigForQualifierString(@NonNull String qualifierString) {
-        return getConfigFromQualifiers(QUALIFIER_SPLITTER.split(qualifierString));
+        if (qualifierString.isEmpty()) {
+            return new FolderConfiguration();
+        } else {
+            return getConfigFromQualifiers(QUALIFIER_SPLITTER.split(qualifierString));
+        }
     }
 
     /**
@@ -432,6 +454,9 @@
         } else if (qualifier instanceof ScreenRatioQualifier) {
             mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
 
+        } else if (qualifier instanceof ScreenRoundQualifier) {
+            mQualifiers[INDEX_SCREEN_ROUND] = qualifier;
+
         } else if (qualifier instanceof ScreenOrientationQualifier) {
             mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
 
@@ -573,6 +598,15 @@
         return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
     }
 
+    public void setScreenRoundQualifier(ScreenRoundQualifier qualifier) {
+        mQualifiers[INDEX_SCREEN_ROUND] = qualifier;
+    }
+
+    @Nullable
+    public ScreenRoundQualifier getScreenRoundQualifier() {
+        return (ScreenRoundQualifier)mQualifiers[INDEX_SCREEN_ROUND];
+    }
+
     public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
         mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
     }
@@ -1124,6 +1158,7 @@
         mQualifiers[INDEX_SCREEN_HEIGHT] = new ScreenHeightQualifier();
         mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = new ScreenSizeQualifier();
         mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier();
+        mQualifiers[INDEX_SCREEN_ROUND] = new ScreenRoundQualifier();
         mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier();
         mQualifiers[INDEX_UI_MODE] = new UiModeQualifier();
         mQualifiers[INDEX_NIGHT_MODE] = new NightModeQualifier();
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java
index 454a956..26fddd0 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java
@@ -387,7 +387,11 @@
         return false;
     }
 
-    public void setRegionSegment(@NonNull String segment) {
+    /**
+     * Used only when constructing the qualifier, don't use after it's been assigned to a
+     * {@link FolderConfiguration}.
+     */
+    void setRegionSegment(@NonNull String segment) {
         assert segment.length() == 3 : segment;
         mRegion = new String(new char[] {
                 Character.toUpperCase(segment.charAt(1)),
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java
index fb43de7..c4ab88f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java
@@ -95,7 +95,7 @@
     public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) {
         // the default is to always return false. This gives less overhead than always returning
         // true, as it would only compare same values anyway.
-        return false;
+        return compareTo == null;
     }
 
     @Override
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ScreenRoundQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ScreenRoundQualifier.java
new file mode 100644
index 0000000..936f835
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ScreenRoundQualifier.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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.ide.common.resources.configuration;
+
+import com.android.annotations.Nullable;
+import com.android.resources.ResourceEnum;
+import com.android.resources.ScreenRound;
+
+public class ScreenRoundQualifier extends EnumBasedResourceQualifier {
+
+    public static final String NAME = "Screen Roundness";
+
+    @Nullable
+    private ScreenRound mValue = null;
+
+    public ScreenRoundQualifier() {
+    }
+
+    public ScreenRoundQualifier(@Nullable ScreenRound value) {
+        mValue = value;
+    }
+
+    public ScreenRound getValue() {
+        return mValue;
+    }
+
+    @Override
+    ResourceEnum getEnumValue() {
+        return mValue;
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getShortName() {
+        return "Roundness";
+    }
+
+    @Override
+    public int since() {
+        return 23;
+    }
+
+    @Override
+    public boolean checkAndSet(String value, FolderConfiguration config) {
+        ScreenRound roundness = ScreenRound.getEnum(value);
+        if (roundness != null) {
+            ScreenRoundQualifier qualifier = new ScreenRoundQualifier(roundness);
+            config.setScreenRoundQualifier(qualifier);
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java
index 1626b23..87d23a1 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java
@@ -23,7 +23,7 @@
  * Resource Qualifier for Platform Version.
  */
 public final class VersionQualifier extends ResourceQualifier {
-    /** Default pixel density value. This means the property is not set. */
+    /** Default version. This means the property is not set. */
     private static final int DEFAULT_VERSION = -1;
 
     private static final Pattern sVersionPattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$
diff --git a/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java b/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java
index 4da72c5..6ecf95e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java
+++ b/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java
@@ -107,7 +107,7 @@
 
         String javaHome = System.getProperty("java.home");
 
-        if (javaHome != null && javaHome.length() > 0) {
+        if (javaHome != null && !javaHome.isEmpty()) {
             keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand;
         }
 
@@ -192,14 +192,11 @@
     /**
      * Returns the CertificateInfo for the given signing configuration.
      *
-     * Returns null if the key could not be found. If the passwords are wrong,
-     * it throws an exception
-     *
-     * @param signingConfig the signing configuration
      * @return the certificate info if it could be loaded.
-     * @throws KeytoolException
-     * @throws FileNotFoundException
+     * @throws KeytoolException If the password is wrong.
+     * @throws FileNotFoundException If the store file cannot be found.
      */
+    @NonNull
     public static CertificateInfo getCertificateInfo(@Nullable String storeType, @NonNull File storeFile,
                                                      @NonNull String storePassword, @NonNull String keyPassword,
                                                      @NonNull String keyAlias)
@@ -210,19 +207,24 @@
                             storeType : KeyStore.getDefaultType());
 
             FileInputStream fis = new FileInputStream(storeFile);
-            //noinspection ConstantConditions
             keyStore.load(fis, storePassword.toCharArray());
             fis.close();
 
-            //noinspection ConstantConditions
             char[] keyPasswordArray = keyPassword.toCharArray();
             PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
                     keyAlias, new KeyStore.PasswordProtection(keyPasswordArray));
 
-            if (entry != null) {
-                return new CertificateInfo(entry.getPrivateKey(),
-                        (X509Certificate) entry.getCertificate());
+            if (entry == null) {
+                throw new KeytoolException(
+                        String.format(
+                                "No key with alias '%1$s' found in keystore %2$s",
+                                keyAlias,
+                                storeFile.getAbsolutePath()));
             }
+
+            return new CertificateInfo(
+                    entry.getPrivateKey(),
+                    (X509Certificate) entry.getCertificate());
         } catch (FileNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -231,7 +233,5 @@
                             keyAlias, storeFile, e.getMessage()),
                     e);
         }
-
-        return null;
     }
 }
diff --git a/sdk-common/src/main/java/com/android/ide/common/util/AssetUtil.java b/sdk-common/src/main/java/com/android/ide/common/util/AssetUtil.java
new file mode 100644
index 0000000..3c1b560
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/util/AssetUtil.java
@@ -0,0 +1,451 @@
+/*
+ * 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.ide.common.util;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+import java.awt.image.Raster;
+import java.awt.image.RescaleOp;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A set of utility classes for manipulating {@link BufferedImage} objects and drawing them to
+ * {@link Graphics2D} canvases.
+ */
+public class AssetUtil {
+    /**
+     * Scales the given rectangle by the given scale factor.
+     *
+     * @param rect        The rectangle to scale.
+     * @param scaleFactor The factor to scale by.
+     * @return The scaled rectangle.
+     */
+    public static Rectangle scaleRectangle(Rectangle rect, float scaleFactor) {
+        return new Rectangle(
+                Math.round(rect.x * scaleFactor),
+                Math.round(rect.y * scaleFactor),
+                Math.round(rect.width * scaleFactor),
+                Math.round(rect.height * scaleFactor));
+    }
+
+    /**
+     * Creates a new ARGB {@link BufferedImage} of the given width and height.
+     *
+     * @param width  The width of the new image.
+     * @param height The height of the new image.
+     * @return The newly created image.
+     */
+    public static BufferedImage newArgbBufferedImage(int width, int height) {
+        return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+    }
+
+    /**
+     * Smoothly scales the given {@link BufferedImage} to the given width and height using the
+     * {@link Image#SCALE_SMOOTH} algorithm (generally bicubic resampling or bilinear filtering).
+     *
+     * @param source The source image.
+     * @param width  The destination width to scale to.
+     * @param height The destination height to scale to.
+     * @return A new, scaled image.
+     */
+    public static BufferedImage scaledImage(BufferedImage source, int width, int height) {
+        Image scaledImage = source.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+        BufferedImage scaledBufImage = new BufferedImage(width, height,
+                BufferedImage.TYPE_INT_ARGB);
+        Graphics g = scaledBufImage.createGraphics();
+        g.drawImage(scaledImage, 0, 0, null);
+        g.dispose();
+        return scaledBufImage;
+    }
+
+    /**
+     * Applies a gaussian blur of the given radius to the given {@link BufferedImage} using a kernel
+     * convolution.
+     *
+     * @param source The source image.
+     * @param radius The blur radius, in pixels.
+     * @return A new, blurred image, or the source image if no blur is performed.
+     */
+    public static BufferedImage blurredImage(BufferedImage source, double radius) {
+        if (radius == 0) {
+            return source;
+        }
+
+        final int r = (int) Math.ceil(radius);
+        final int rows = r * 2 + 1;
+        final float[] kernelData = new float[rows * rows];
+
+        final double sigma = radius / 3;
+        final double sigma22 = 2 * sigma * sigma;
+        final double sqrtPiSigma22 = Math.sqrt(Math.PI * sigma22);
+        final double radius2 = radius * radius;
+
+        double total = 0;
+        int index = 0;
+        double distance2;
+
+        int x, y;
+        for (y = -r; y <= r; y++) {
+            for (x = -r; x <= r; x++) {
+                distance2 = 1.0 * x * x + 1.0 * y * y;
+                if (distance2 > radius2) {
+                    kernelData[index] = 0;
+                } else {
+                    kernelData[index] = (float) (Math.exp(-distance2 / sigma22) / sqrtPiSigma22);
+                }
+                total += kernelData[index];
+                ++index;
+            }
+        }
+
+        for (index = 0; index < kernelData.length; index++) {
+            kernelData[index] /= total;
+        }
+
+        // We first pad the image so the kernel can operate at the edges.
+        BufferedImage paddedSource = paddedImage(source, r);
+        BufferedImage blurredPaddedImage = operatedImage(paddedSource, new ConvolveOp(
+                new Kernel(rows, rows, kernelData), ConvolveOp.EDGE_ZERO_FILL, null));
+        return blurredPaddedImage.getSubimage(r, r, source.getWidth(), source.getHeight());
+    }
+
+    /**
+     * Inverts the alpha channel of the given {@link BufferedImage}. RGB data for the inverted area
+     * are undefined, so it's generally best to fill the resulting image with a color.
+     *
+     * @param source The source image.
+     * @return A new image with an alpha channel inverted from the original.
+     */
+    public static BufferedImage invertedAlphaImage(BufferedImage source) {
+        final float[] scaleFactors = new float[]{1, 1, 1, -1};
+        final float[] offsets = new float[]{0, 0, 0, 255};
+
+        return operatedImage(source, new RescaleOp(scaleFactors, offsets, null));
+    }
+
+    /**
+     * Applies a {@link BufferedImageOp} on the given {@link BufferedImage}.
+     *
+     * @param source The source image.
+     * @param op     The operation to perform.
+     * @return A new image with the operation performed.
+     */
+    public static BufferedImage operatedImage(BufferedImage source, BufferedImageOp op) {
+        BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
+        Graphics2D g = (Graphics2D) newImage.getGraphics();
+        g.drawImage(source, op, 0, 0);
+        return newImage;
+    }
+
+    /**
+     * Fills the given {@link BufferedImage} with a {@link Paint}, preserving its alpha channel.
+     *
+     * @param source The source image.
+     * @param paint  The paint to fill with.
+     * @return A new, painted/filled image.
+     */
+    public static BufferedImage filledImage(BufferedImage source, Paint paint) {
+        BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
+        Graphics2D g = (Graphics2D) newImage.getGraphics();
+        g.drawImage(source, 0, 0, null);
+        g.setComposite(AlphaComposite.SrcAtop);
+        g.setPaint(paint);
+        g.fillRect(0, 0, source.getWidth(), source.getHeight());
+        return newImage;
+    }
+
+    /**
+     * Pads the given {@link BufferedImage} on all sides by the given padding amount.
+     *
+     * @param source  The source image.
+     * @param padding The amount to pad on all sides, in pixels.
+     * @return A new, padded image, or the source image if no padding is performed.
+     */
+    public static BufferedImage paddedImage(BufferedImage source, int padding) {
+        if (padding == 0) {
+            return source;
+        }
+
+        BufferedImage newImage = newArgbBufferedImage(
+                source.getWidth() + padding * 2, source.getHeight() + padding * 2);
+        Graphics2D g = (Graphics2D) newImage.getGraphics();
+        g.drawImage(source, padding, padding, null);
+        return newImage;
+    }
+
+    /**
+     * Trims the transparent pixels from the given {@link BufferedImage} (returns a sub-image).
+     *
+     * @param source The source image.
+     * @return A new, trimmed image, or the source image if no trim is performed.
+     */
+    public static BufferedImage trimmedImage(BufferedImage source) {
+        final int minAlpha = 1;
+        final int srcWidth = source.getWidth();
+        final int srcHeight = source.getHeight();
+        Raster raster = source.getRaster();
+        int l = srcWidth, t = srcHeight, r = 0, b = 0;
+
+        int alpha, x, y;
+        int[] pixel = new int[4];
+        for (y = 0; y < srcHeight; y++) {
+            for (x = 0; x < srcWidth; x++) {
+                raster.getPixel(x, y, pixel);
+                alpha = pixel[3];
+                if (alpha >= minAlpha) {
+                    l = Math.min(x, l);
+                    t = Math.min(y, t);
+                    r = Math.max(x, r);
+                    b = Math.max(y, b);
+                }
+            }
+        }
+
+        if (l > r || t > b) {
+            // No pixels, couldn't trim
+            return source;
+        }
+
+        return source.getSubimage(l, t, r - l + 1, b - t + 1);
+    }
+
+    /**
+     * Draws the given {@link BufferedImage} to the canvas, at the given coordinates, with the given
+     * {@link Effect}s applied. Note that drawn effects may be outside the bounds of the source
+     * image.
+     *
+     * @param g       The destination canvas.
+     * @param source  The source image.
+     * @param x       The x offset at which to draw the image.
+     * @param y       The y offset at which to draw the image.
+     * @param effects The list of effects to apply.
+     */
+    public static void drawEffects(Graphics2D g, BufferedImage source, int x, int y,
+            Effect[] effects) {
+        List<ShadowEffect> shadowEffects = new ArrayList<ShadowEffect>();
+        List<FillEffect> fillEffects = new ArrayList<FillEffect>();
+
+        for (Effect effect : effects) {
+            if (effect instanceof ShadowEffect) {
+                shadowEffects.add((ShadowEffect) effect);
+            } else if (effect instanceof FillEffect) {
+                fillEffects.add((FillEffect) effect);
+            }
+        }
+
+        Composite oldComposite = g.getComposite();
+        for (ShadowEffect effect : shadowEffects) {
+            if (effect.inner) {
+                continue;
+            }
+
+            // Outer shadow
+            g.setComposite(AlphaComposite.getInstance(
+                    AlphaComposite.SRC_OVER, (float) effect.opacity));
+            g.drawImage(
+                    filledImage(
+                            blurredImage(source, effect.radius),
+                            effect.color),
+                    (int) effect.xOffset, (int) effect.yOffset, null);
+        }
+        g.setComposite(oldComposite);
+
+        // Inner shadow & fill effects.
+        final Rectangle imageRect = new Rectangle(0, 0, source.getWidth(), source.getHeight());
+        BufferedImage out = newArgbBufferedImage(imageRect.width, imageRect.height);
+        Graphics2D g2 = (Graphics2D) out.getGraphics();
+        double fillOpacity = 1.0;
+
+        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
+        g2.drawImage(source, 0, 0, null);
+        g2.setComposite(AlphaComposite.SrcAtop);
+
+        // Gradient fill
+        for (FillEffect effect : fillEffects) {
+            g2.setPaint(effect.paint);
+            g2.fillRect(0, 0, imageRect.width, imageRect.height);
+            fillOpacity = Math.max(0, Math.min(1, effect.opacity));
+        }
+
+        // Inner shadows
+        for (ShadowEffect effect : shadowEffects) {
+            if (!effect.inner) {
+                continue;
+            }
+
+            BufferedImage innerShadowImage = newArgbBufferedImage(
+                    imageRect.width, imageRect.height);
+            Graphics2D g3 = (Graphics2D) innerShadowImage.getGraphics();
+            g3.drawImage(source, (int) effect.xOffset, (int) effect.yOffset, null);
+            g2.setComposite(AlphaComposite.getInstance(
+                    AlphaComposite.SRC_ATOP, (float) effect.opacity));
+            g2.drawImage(
+                    filledImage(
+                            blurredImage(invertedAlphaImage(innerShadowImage), effect.radius),
+                            effect.color),
+                    0, 0, null);
+        }
+
+        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) fillOpacity));
+        g.drawImage(out, x, y, null);
+        g.setComposite(oldComposite);
+    }
+
+    /**
+     * Draws the given {@link BufferedImage} to the canvas, centered, wholly contained within the
+     * bounds defined by the destination rectangle, and with preserved aspect ratio.
+     *
+     * @param g       The destination canvas.
+     * @param source  The source image.
+     * @param dstRect The destination rectangle in the destination canvas into which to draw the
+     *                image.
+     */
+    public static void drawCenterInside(Graphics2D g, BufferedImage source, Rectangle dstRect) {
+        final int srcWidth = source.getWidth();
+        final int srcHeight = source.getHeight();
+        if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
+            final int scaledWidth = Math.max(1, dstRect.width);
+            final int scaledHeight = Math.max(1, dstRect.width * srcHeight / srcWidth);
+            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+            g.drawImage(scaledImage,
+                    dstRect.x,
+                    dstRect.y + (dstRect.height - scaledHeight) / 2,
+                    dstRect.x + dstRect.width,
+                    dstRect.y + (dstRect.height - scaledHeight) / 2 + scaledHeight,
+                    0,
+                    0,
+                    0 + scaledWidth,
+                    0 + scaledHeight,
+                    null);
+        } else {
+            final int scaledWidth = Math.max(1, dstRect.height * srcWidth / srcHeight);
+            final int scaledHeight = Math.max(1, dstRect.height);
+            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+            g.drawImage(scaledImage,
+                    dstRect.x + (dstRect.width - scaledWidth) / 2,
+                    dstRect.y,
+                    dstRect.x + (dstRect.width - scaledWidth) / 2 + scaledWidth,
+                    dstRect.y + dstRect.height,
+                    0,
+                    0,
+                    0 + scaledWidth,
+                    0 + scaledHeight,
+                    null);
+        }
+    }
+
+    /**
+     * Draws the given {@link BufferedImage} to the canvas, centered and cropped to fill the
+     * bounds defined by the destination rectangle, and with preserved aspect ratio.
+     *
+     * @param g       The destination canvas.
+     * @param source  The source image.
+     * @param dstRect The destination rectangle in the destination canvas into which to draw the
+     *                image.
+     */
+    public static void drawCenterCrop(Graphics2D g, BufferedImage source, Rectangle dstRect) {
+        final int srcWidth = source.getWidth();
+        final int srcHeight = source.getHeight();
+        if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
+            final int scaledWidth = dstRect.height * srcWidth / srcHeight;
+            final int scaledHeight = dstRect.height;
+            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+            g.drawImage(scaledImage,
+                    dstRect.x,
+                    dstRect.y,
+                    dstRect.x + dstRect.width,
+                    dstRect.y + dstRect.height,
+                    0 + (scaledWidth - dstRect.width) / 2,
+                    0,
+                    0 + (scaledWidth - dstRect.width) / 2 + dstRect.width,
+                    0 + dstRect.height,
+                    null);
+        } else {
+            final int scaledWidth = dstRect.width;
+            final int scaledHeight = dstRect.width * srcHeight / srcWidth;
+            Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+            g.drawImage(scaledImage,
+                    dstRect.x,
+                    dstRect.y,
+                    dstRect.x + dstRect.width,
+                    dstRect.y + dstRect.height,
+                    0,
+                    0 + (scaledHeight - dstRect.height) / 2,
+                    0 + dstRect.width,
+                    0 + (scaledHeight - dstRect.height) / 2 + dstRect.height,
+                    null);
+        }
+    }
+
+    /**
+     * An effect to apply in
+     * {@link AssetUtil#drawEffects(java.awt.Graphics2D, java.awt.image.BufferedImage, int, int, AssetUtil.Effect[])}
+     */
+    public abstract static class Effect {
+    }
+
+    /**
+     * An inner or outer shadow.
+     */
+    public static class ShadowEffect extends Effect {
+        public double xOffset;
+        public double yOffset;
+        public double radius;
+        public Color color;
+        public double opacity;
+        public boolean inner;
+
+        public ShadowEffect(double xOffset, double yOffset, double radius, Color color,
+                double opacity, boolean inner) {
+            this.xOffset = xOffset;
+            this.yOffset = yOffset;
+            this.radius = radius;
+            this.color = color;
+            this.opacity = opacity;
+            this.inner = inner;
+        }
+    }
+
+    /**
+     * A fill, defined by a paint.
+     */
+    public static class FillEffect extends Effect {
+        public Paint paint;
+        public double opacity;
+
+        public FillEffect(Paint paint, double opacity) {
+            this.paint = paint;
+            this.opacity = opacity;
+        }
+
+        public FillEffect(Paint paint) {
+            this.paint = paint;
+            this.opacity = 1.0;
+        }
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathBuilder.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathBuilder.java
new file mode 100644
index 0000000..2a814f4
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+/**
+ * Build a string for Svg file's path data.
+ */
+class PathBuilder {
+  private StringBuilder mPathData = new StringBuilder();
+
+  private String booleanToString(boolean flag) {
+    return flag ? "1" : "0";
+  }
+
+  public PathBuilder absoluteMoveTo(float x, float y) {
+    mPathData.append("M"+ x + "," + y);
+    return this;
+  }
+
+  public PathBuilder relativeMoveTo(float x, float y) {
+    mPathData.append("m"+ x + "," + y);
+    return this;
+  }
+
+  public PathBuilder absoluteLineTo(float x, float y) {
+    mPathData.append("L"+ x + "," + y);
+    return this;
+  }
+
+  public PathBuilder relativeLineTo(float x, float y) {
+    mPathData.append("l"+ x + "," + y);
+    return this;
+  }
+
+  public PathBuilder absoluteVerticalTo(float v) {
+    mPathData.append("V"+ v);
+    return this;
+  }
+
+  public PathBuilder relativeVerticalTo(float v) {
+    mPathData.append("v"+ v);
+    return this;
+  }
+
+  public PathBuilder absoluteHorizontalTo(float h) {
+    mPathData.append("H"+ h);
+    return this;
+  }
+
+  public PathBuilder relativeHorizontalTo(float h) {
+    mPathData.append("h"+ h);
+    return this;
+  }
+
+  public PathBuilder absoluteArcTo(float rx, float ry, boolean rotation,
+                                   boolean largeArc, boolean sweep, float x, float y) {
+    mPathData.append("A"+ rx + "," + ry + "," + booleanToString(rotation) + "," +
+                     booleanToString(largeArc) + "," + booleanToString(sweep) + "," + x + "," + y );
+    return this;
+  }
+
+  public PathBuilder relativeArcTo(float rx, float ry, boolean rotation,
+                                   boolean largeArc, boolean sweep, float x, float y) {
+    mPathData.append("a"+ rx + "," + ry + "," + booleanToString(rotation) + "," +
+                     booleanToString(largeArc) + "," + booleanToString(sweep) + "," + x + "," + y );
+    return this;
+  }
+
+  public PathBuilder absoluteClose() {
+    mPathData.append("Z");
+    return this;
+  }
+
+  public PathBuilder relativeClose() {
+    mPathData.append("z");
+    return this;
+  }
+
+  public String toString() {
+    return mPathData.toString();
+  }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/Svg2Vector.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/Svg2Vector.java
new file mode 100644
index 0000000..c3a1512
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/Svg2Vector.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.annotations.NonNull;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.*;
+import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Converts SVG to VectorDrawable's XML
+ */
+public class Svg2Vector {
+    private static Logger logger = Logger.getLogger(Svg2Vector.class.getSimpleName());
+
+    public static final String SVG_POLYGON = "polygon";
+    public static final String SVG_RECT = "rect";
+    public static final String SVG_CIRCLE = "circle";
+    public static final String SVG_LINE = "line";
+    public static final String SVG_PATH = "path";
+    public static final String SVG_GROUP = "g";
+    public static final String SVG_TRANSFORM = "transform";
+    public static final String SVG_WIDTH = "width";
+    public static final String SVG_HEIGHT = "height";
+    public static final String SVG_VIEW_BOX = "viewBox";
+    public static final String SVG_STYLE = "style";
+    public static final String SVG_DISPLAY = "display";
+
+    public static final String SVG_D = "d";
+    public static final String SVG_STROKE_COLOR = "stroke";
+    public static final String SVG_STROKE_OPACITY = "stroke-opacity";
+    public static final String SVG_STROKE_LINEJOINE = "stroke-linejoin";
+    public static final String SVG_STROKE_LINECAP = "stroke-linecap";
+    public static final String SVG_STROKE_WIDTH = "stroke-width";
+    public static final String SVG_FILL_COLOR = "fill";
+    public static final String SVG_FILL_OPACITY = "fill-opacity";
+    public static final String SVG_OPACITY = "opacity";
+    public static final String SVG_CLIP = "clip";
+    public static final String SVG_POINTS = "points";
+
+    public static final ImmutableMap<String, String> presentationMap =
+        ImmutableMap.<String, String>builder()
+            .put(SVG_STROKE_COLOR, "android:strokeColor")
+            .put(SVG_STROKE_OPACITY, "android:strokeAlpha")
+            .put(SVG_STROKE_LINEJOINE, "android:strokeLinejoin")
+            .put(SVG_STROKE_LINECAP, "android:strokeLinecap")
+            .put(SVG_STROKE_WIDTH, "android:strokeWidth")
+            .put(SVG_FILL_COLOR, "android:fillColor")
+            .put(SVG_FILL_OPACITY, "android:fillAlpha")
+            .put(SVG_CLIP, "android:clip").put(SVG_OPACITY, "android:fillAlpha")
+            .build();
+
+    // List all the Svg nodes that we don't support. Categorized by the types.
+    private static final HashSet<String> unsupportedSvgNodes = Sets.newHashSet(
+        // Animation elements
+        "animate", "animateColor", "animateMotion", "animateTransform", "mpath", "set",
+        // Container elements
+        "a", "defs", "glyph", "marker", "mask", "missing-glyph", "pattern", "switch", "symbol",
+        // Filter primitive elements
+        "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix",
+        "feDiffuseLighting", "feDisplacementMap", "feFlood", "feFuncA", "feFuncB", "feFuncG",
+        "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology",
+        "feOffset", "feSpecularLighting", "feTile", "feTurbulence",
+        // Font elements
+        "font", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri",
+        "hkern", "vkern",
+        // Gradient elements
+        "linearGradient", "radialGradient", "stop",
+        // Graphics elements
+        "ellipse", "polyline", "text", "use",
+        // Light source elements
+        "feDistantLight", "fePointLight", "feSpotLight",
+        // Structural elements
+        "defs", "symbol", "use",
+        // Text content elements
+        "altGlyph", "altGlyphDef", "altGlyphItem", "glyph", "glyphRef", "textPath", "text", "tref",
+        "tspan",
+        // Text content child elements
+        "altGlyph", "textPath", "tref", "tspan",
+        // Uncategorized elements
+        "clipPath", "color-profile", "cursor", "filter", "foreignObject", "script", "view");
+
+    @NonNull
+    private static SvgTree parse(File f) throws Exception {
+        SvgTree svgTree = new SvgTree();
+        Document doc = svgTree.parse(f);
+        NodeList nSvgNode;
+
+        // Parse svg elements
+        nSvgNode = doc.getElementsByTagName("svg");
+        if (nSvgNode.getLength() != 1) {
+            throw new IllegalStateException("Not a proper SVG file");
+        }
+        Node rootNode = nSvgNode.item(0);
+        for (int i = 0; i < nSvgNode.getLength(); i++) {
+            Node nNode = nSvgNode.item(i);
+            if (nNode.getNodeType() == Node.ELEMENT_NODE) {
+                parseDimension(svgTree, nNode);
+            }
+        }
+
+        if (svgTree.viewBox == null) {
+            svgTree.logErrorLine("Missing \"viewBox\" in <svg> element", rootNode, SvgTree.SvgLogLevel.ERROR);
+            return svgTree;
+        }
+
+        if ((svgTree.w == 0 || svgTree.h == 0) && svgTree.viewBox[2] > 0 && svgTree.viewBox[3] > 0) {
+            svgTree.w = svgTree.viewBox[2];
+            svgTree.h = svgTree.viewBox[3];
+        }
+
+        // Parse transformation information.
+        // TODO: Properly handle transformation in the group level. In the "use" case, we treat
+        // it as global for now.
+        NodeList nUseTags;
+        svgTree.matrix = new float[6];
+        svgTree.matrix[0] = 1;
+        svgTree.matrix[3] = 1;
+
+        nUseTags = doc.getElementsByTagName("use");
+        for (int temp = 0; temp < nUseTags.getLength(); temp++) {
+            Node nNode = nUseTags.item(temp);
+            if (nNode.getNodeType() == Node.ELEMENT_NODE) {
+                parseTransformation(svgTree, nNode);
+            }
+        }
+
+        SvgGroupNode root = new SvgGroupNode(svgTree, rootNode, "root");
+        svgTree.setRoot(root);
+
+        // Parse all the group and path node recursively.
+        traverseSVGAndExtract(svgTree, root, rootNode);
+
+        svgTree.dump(root);
+
+        return svgTree;
+    }
+
+    private static void traverseSVGAndExtract(SvgTree svgTree, SvgGroupNode currentGroup, Node item) {
+        // Recursively traverse all the group and path nodes
+        NodeList allChildren = item.getChildNodes();
+
+        for (int i = 0; i < allChildren.getLength(); i++) {
+            Node currentNode = allChildren.item(i);
+            String nodeName = currentNode.getNodeName();
+            if (SVG_PATH.equals(nodeName) ||
+                SVG_RECT.equals(nodeName) ||
+                SVG_CIRCLE.equals(nodeName) ||
+                SVG_POLYGON.equals(nodeName) ||
+                SVG_LINE.equals(nodeName)) {
+                SvgLeafNode child = new SvgLeafNode(svgTree, currentNode, nodeName + i);
+
+                extractAllItemsAs(svgTree, child, currentNode);
+
+                currentGroup.addChild(child);
+            } else if (SVG_GROUP.equals(nodeName)) {
+                SvgGroupNode childGroup = new SvgGroupNode(svgTree, currentNode, "child" + i);
+                currentGroup.addChild(childGroup);
+                traverseSVGAndExtract(svgTree, childGroup, currentNode);
+            } else {
+                // For other fancy tags, like <refs>, they can contain children too.
+                // Report the unsupported nodes.
+                if (unsupportedSvgNodes.contains(nodeName)) {
+                    svgTree.logErrorLine("<" + nodeName + "> is not supported", currentNode,
+                                         SvgTree.SvgLogLevel.ERROR);
+                }
+                traverseSVGAndExtract(svgTree, currentGroup, currentNode);
+            }
+        }
+
+    }
+
+    private static void parseTransformation(SvgTree avg, Node nNode) {
+        NamedNodeMap a = nNode.getAttributes();
+        int len = a.getLength();
+
+        for (int i = 0; i < len; i++) {
+            Node n = a.item(i);
+            String name = n.getNodeName();
+            String value = n.getNodeValue();
+            if (SVG_TRANSFORM.equals(name)) {
+                if (value.startsWith("matrix(")) {
+                    value = value.substring("matrix(".length(), value.length() - 1);
+                    String[] sp = value.split(" ");
+                    for (int j = 0; j < sp.length; j++) {
+                        avg.matrix[j] = Float.parseFloat(sp[j]);
+                    }
+                }
+            } else if (name.equals("y")) {
+                Float.parseFloat(value);
+            } else if (name.equals("x")) {
+                Float.parseFloat(value);
+            }
+
+        }
+    }
+
+    private static void parseDimension(SvgTree avg, Node nNode) {
+        NamedNodeMap a = nNode.getAttributes();
+        int len = a.getLength();
+
+        for (int i = 0; i < len; i++) {
+            Node n = a.item(i);
+            String name = n.getNodeName();
+            String value = n.getNodeValue();
+            int subStringSize = value.length();
+            if (subStringSize > 2) {
+                if (value.endsWith("px")) {
+                    subStringSize = subStringSize - 2;
+                }
+            }
+
+            if (SVG_WIDTH.equals(name)) {
+                avg.w = Float.parseFloat(value.substring(0, subStringSize));
+            } else if (SVG_HEIGHT.equals(name)) {
+                avg.h = Float.parseFloat(value.substring(0, subStringSize));
+            } else if (SVG_VIEW_BOX.equals(name)) {
+                avg.viewBox = new float[4];
+                String[] strbox = value.split(" ");
+                for (int j = 0; j < avg.viewBox.length; j++) {
+                    avg.viewBox[j] = Float.parseFloat(strbox[j]);
+                }
+            }
+        }
+        if (avg.viewBox == null && avg.w != 0 && avg.h != 0) {
+            avg.viewBox = new float[4];
+            avg.viewBox[2] = avg.w;
+            avg.viewBox[3] = avg.h;
+        }
+    }
+
+    // Read the content from currentItem, and fill into "child"
+    private static void extractAllItemsAs(SvgTree avg, SvgLeafNode child, Node currentItem) {
+        Node currentGroup = currentItem.getParentNode();
+
+        boolean hasNodeAttr = false;
+        String styleContent = "";
+        boolean nothingToDisplay = false;
+
+        while (currentGroup != null && currentGroup.getNodeName().equals("g")) {
+            // Parse the group's attributes.
+            logger.log(Level.FINE, "Printing current parent");
+            printlnCommon(currentGroup);
+
+            NamedNodeMap attr = currentGroup.getAttributes();
+            Node nodeAttr = attr.getNamedItem(SVG_STYLE);
+            // Search for the "display:none", if existed, then skip this item.
+            if (nodeAttr != null) {
+                styleContent += nodeAttr.getTextContent() + ";";
+                logger.log(Level.FINE, "styleContent is :" + styleContent + "at number group ");
+                if (styleContent.contains("display:none")) {
+                    logger.log(Level.FINE, "Found none style, skip the whole group");
+                    nothingToDisplay = true;
+                    break;
+                } else {
+                    hasNodeAttr = true;
+                }
+            }
+
+            Node displayAttr = attr.getNamedItem(SVG_DISPLAY);
+            if (displayAttr != null && "none".equals(displayAttr.getNodeValue())) {
+                logger.log(Level.FINE, "Found display:none style, skip the whole group");
+                nothingToDisplay = true;
+                break;
+            }
+            currentGroup = currentGroup.getParentNode();
+        }
+
+        if (nothingToDisplay) {
+            // Skip this current whole item.
+            return;
+        }
+
+        logger.log(Level.FINE, "Print current item");
+        printlnCommon(currentItem);
+
+        if (hasNodeAttr && styleContent != null) {
+            addStyleToPath(child, styleContent);
+        }
+
+        Node currentGroupNode = currentItem;
+
+        if (SVG_PATH.equals(currentGroupNode.getNodeName())) {
+            extractPathItem(avg, child, currentGroupNode);
+        }
+
+        if (SVG_RECT.equals(currentGroupNode.getNodeName())) {
+            extractRectItem(avg, child, currentGroupNode);
+        }
+
+        if (SVG_CIRCLE.equals(currentGroupNode.getNodeName())) {
+            extractCircleItem(avg, child, currentGroupNode);
+        }
+
+        if (SVG_POLYGON.equals(currentGroupNode.getNodeName())) {
+            extractPolyItem(avg, child, currentGroupNode);
+        }
+
+        if (SVG_LINE.equals(currentGroupNode.getNodeName())) {
+            extractLineItem(avg, child, currentGroupNode);
+        }
+    }
+
+    private static void printlnCommon(Node n) {
+        logger.log(Level.FINE, " nodeName=\"" + n.getNodeName() + "\"");
+
+        String val = n.getNamespaceURI();
+        if (val != null) {
+            logger.log(Level.FINE, " uri=\"" + val + "\"");
+        }
+
+        val = n.getPrefix();
+
+        if (val != null) {
+            logger.log(Level.FINE, " pre=\"" + val + "\"");
+        }
+
+        val = n.getLocalName();
+        if (val != null) {
+            logger.log(Level.FINE, " local=\"" + val + "\"");
+        }
+
+        val = n.getNodeValue();
+        if (val != null) {
+            logger.log(Level.FINE, " nodeValue=");
+            if (val.trim().equals("")) {
+                // Whitespace
+                logger.log(Level.FINE, "[WS]");
+            } else {
+                logger.log(Level.FINE, "\"" + n.getNodeValue() + "\"");
+            }
+        }
+    }
+
+    /**
+     * Convert polygon element into a path.
+     */
+    private static void extractPolyItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+        logger.log(Level.FINE, "Rect found" + currentGroupNode.getTextContent());
+        if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+
+            NamedNodeMap a = currentGroupNode.getAttributes();
+            int len = a.getLength();
+
+            for (int itemIndex = 0; itemIndex < len; itemIndex++) {
+                Node n = a.item(itemIndex);
+                String name = n.getNodeName();
+                String value = n.getNodeValue();
+                if (name.equals(SVG_STYLE)) {
+                    addStyleToPath(child, value);
+                } else if (presentationMap.containsKey(name)) {
+                    child.fillPresentationAttributes(name, value);
+                } else if (name.equals(SVG_POINTS)) {
+                    PathBuilder builder = new PathBuilder();
+                    String[] split = value.split("[\\s,]+");
+                    float baseX = Float.parseFloat(split[0]);
+                    float baseY = Float.parseFloat(split[1]);
+                    builder.absoluteMoveTo(baseX, baseY);
+                    for (int j = 2; j < split.length; j += 2) {
+                        float x = Float.parseFloat(split[j]);
+                        float y = Float.parseFloat(split[j + 1]);
+                        builder.relativeLineTo(x - baseX, y - baseY);
+                        baseX = x;
+                        baseY = y;
+                    }
+                    builder.relativeClose();
+                    child.setPathData(builder.toString());
+                }
+            }
+        }
+    }
+
+    /**
+     * Convert rectangle element into a path.
+     */
+    private static void extractRectItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+        logger.log(Level.FINE, "Rect found" + currentGroupNode.getTextContent());
+
+        if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+            float x = 0;
+            float y = 0;
+            float width = Float.NaN;
+            float height = Float.NaN;
+
+            NamedNodeMap a = currentGroupNode.getAttributes();
+            int len = a.getLength();
+            boolean pureTransparent = false;
+            for (int j = 0; j < len; j++) {
+                Node n = a.item(j);
+                String name = n.getNodeName();
+                String value = n.getNodeValue();
+                if (name.equals(SVG_STYLE)) {
+                    addStyleToPath(child, value);
+                    if (value.contains("opacity:0;")) {
+                        pureTransparent = true;
+                    }
+                } else if (presentationMap.containsKey(name)) {
+                    child.fillPresentationAttributes(name, value);
+                } else if (name.equals("clip-path") && value.startsWith("url(#SVGID_")) {
+
+                } else if (name.equals("x")) {
+                    x = Float.parseFloat(value);
+                } else if (name.equals("y")) {
+                    y = Float.parseFloat(value);
+                } else if (name.equals("width")) {
+                    width = Float.parseFloat(value);
+                } else if (name.equals("height")) {
+                    height = Float.parseFloat(value);
+                } else if (name.equals("style")) {
+
+                }
+
+            }
+
+            if (!pureTransparent && avg != null && !Float.isNaN(x) && !Float.isNaN(y)
+                    && !Float.isNaN(width)
+                    && !Float.isNaN(height)) {
+                // "M x, y h width v height h -width z"
+                PathBuilder builder = new PathBuilder();
+                builder.absoluteMoveTo(x, y);
+                builder.relativeHorizontalTo(width);
+                builder.relativeVerticalTo(height);
+                builder.relativeHorizontalTo(-width);
+                builder.relativeClose();
+                child.setPathData(builder.toString());
+            }
+        }
+    }
+
+    /**
+     * Convert circle element into a path.
+     */
+    private static void extractCircleItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+        logger.log(Level.FINE, "circle found" + currentGroupNode.getTextContent());
+
+        if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+            float cx = 0;
+            float cy = 0;
+            float radius = 0;
+
+            NamedNodeMap a = currentGroupNode.getAttributes();
+            int len = a.getLength();
+            boolean pureTransparent = false;
+            for (int j = 0; j < len; j++) {
+                Node n = a.item(j);
+                String name = n.getNodeName();
+                String value = n.getNodeValue();
+                if (name.equals(SVG_STYLE)) {
+                    addStyleToPath(child, value);
+                    if (value.contains("opacity:0;")) {
+                        pureTransparent = true;
+                    }
+                } else if (presentationMap.containsKey(name)) {
+                    child.fillPresentationAttributes(name, value);
+                } else if (name.equals("clip-path") && value.startsWith("url(#SVGID_")) {
+
+                } else if (name.equals("cx")) {
+                    cx = Float.parseFloat(value);
+                } else if (name.equals("cy")) {
+                    cy = Float.parseFloat(value);
+                } else if (name.equals("r")) {
+                    radius = Float.parseFloat(value);
+                }
+
+            }
+
+            if (!pureTransparent && avg != null && !Float.isNaN(cx) && !Float.isNaN(cy)) {
+                // "M cx cy m -r, 0 a r,r 0 1,1 (r * 2),0 a r,r 0 1,1 -(r * 2),0"
+                PathBuilder builder = new PathBuilder();
+                builder.absoluteMoveTo(cx, cy);
+                builder.relativeMoveTo(-radius, 0);
+                builder.relativeArcTo(radius, radius, false, true, true, 2 * radius, 0);
+                builder.relativeArcTo(radius, radius, false, true, true, -2 * radius, 0);
+                child.setPathData(builder.toString());
+            }
+        }
+    }
+
+    /**
+     * Convert line element into a path.
+     */
+    private static void extractLineItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+        logger.log(Level.FINE, "line found" + currentGroupNode.getTextContent());
+
+        if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+            float x1 = 0;
+            float y1 = 0;
+            float x2 = 0;
+            float y2 = 0;
+
+            NamedNodeMap a = currentGroupNode.getAttributes();
+            int len = a.getLength();
+            boolean pureTransparent = false;
+            for (int j = 0; j < len; j++) {
+                Node n = a.item(j);
+                String name = n.getNodeName();
+                String value = n.getNodeValue();
+                if (name.equals(SVG_STYLE)) {
+                    addStyleToPath(child, value);
+                    if (value.contains("opacity:0;")) {
+                        pureTransparent = true;
+                    }
+                } else if (presentationMap.containsKey(name)) {
+                    child.fillPresentationAttributes(name, value);
+                } else if (name.equals("clip-path") && value.startsWith("url(#SVGID_")) {
+                    // TODO: Handle clip path here.
+                } else if (name.equals("x1")) {
+                    x1 = Float.parseFloat(value);
+                } else if (name.equals("y1")) {
+                    y1 = Float.parseFloat(value);
+                } else if (name.equals("x2")) {
+                    x2 = Float.parseFloat(value);
+                } else if (name.equals("y2")) {
+                    y2 = Float.parseFloat(value);
+                }
+            }
+
+            if (!pureTransparent && avg != null && !Float.isNaN(x1) && !Float.isNaN(y1)
+                    && !Float.isNaN(x2) && !Float.isNaN(y2)) {
+                // "M x1, y1 L x2, y2"
+                PathBuilder builder = new PathBuilder();
+                builder.absoluteMoveTo(x1, y1);
+                builder.absoluteLineTo(x2, y2);
+                child.setPathData(builder.toString());
+            }
+        }
+
+    }
+
+    private static void extractPathItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+        logger.log(Level.FINE, "Path found " + currentGroupNode.getTextContent());
+
+        if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+            Element eElement = (Element)currentGroupNode;
+
+            NamedNodeMap a = currentGroupNode.getAttributes();
+            int len = a.getLength();
+
+            for (int j = 0; j < len; j++) {
+                Node n = a.item(j);
+                String name = n.getNodeName();
+                String value = n.getNodeValue();
+                if (name.equals(SVG_STYLE)) {
+                    addStyleToPath(child, value);
+                } else if (presentationMap.containsKey(name)) {
+                    child.fillPresentationAttributes(name, value);
+                } else if (name.equals(SVG_D)) {
+                    String pathData = value.replaceAll("(\\d)-", "$1,-");
+                    child.setPathData(pathData);
+                }
+
+            }
+        }
+    }
+
+    private static void addStyleToPath(SvgLeafNode path, String value) {
+        logger.log(Level.FINE, "Style found is " + value);
+        if (value != null) {
+            String[] parts = value.split(";");
+            for (int k = parts.length - 1; k >= 0; k--) {
+                String subStyle = parts[k];
+                String[] nameValue = subStyle.split(":");
+                if (nameValue.length == 2 && nameValue[0] != null && nameValue[1] != null) {
+                    if (presentationMap.containsKey(nameValue[0])) {
+                        path.fillPresentationAttributes(nameValue[0], nameValue[1]);
+                    } else if (nameValue[0].equals(SVG_OPACITY)) {
+                        // TODO: This is hacky, since we don't have a group level
+                        // android:opacity. This only works when the path didn't overlap.
+                        path.fillPresentationAttributes(SVG_FILL_OPACITY, nameValue[1]);
+                    }
+                }
+            }
+        }
+    }
+
+    private static final String head = "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n";
+
+    private static String getSizeString(float w, float h, float scaleFactor) {
+        String size = "        android:width=\"" + (int) (w * scaleFactor) + "dp\"\n" +
+                      "        android:height=\"" + (int) (h * scaleFactor) + "dp\"\n";
+        return size;
+    }
+
+    private static void writeFile(OutputStream outStream, SvgTree svgTree) throws IOException {
+
+        OutputStreamWriter fw = new OutputStreamWriter(outStream);
+        fw.write(head);
+        float finalWidth = svgTree.w;
+        float finalHeight = svgTree.h;
+
+        fw.write(getSizeString(finalWidth, finalHeight, svgTree.mScaleFactor));
+
+        fw.write("        android:viewportWidth=\"" + svgTree.w + "\"\n");
+        fw.write("        android:viewportHeight=\"" + svgTree.h + "\">\n");
+
+        svgTree.normalize();
+        // TODO: this has to happen in the tree mode!!!
+        writeXML(svgTree, fw);
+        fw.write("</vector>\n");
+
+        fw.close();
+    }
+
+    private static void writeXML(SvgTree svgTree, OutputStreamWriter fw) throws IOException {
+        svgTree.getRoot().writeXML(fw);
+    }
+
+    /**
+     * Convert a SVG file into VectorDrawable's XML content, if no error is found.
+     *
+     * @param inputSVG the input SVG file
+     * @param outStream the converted VectorDrawable's content. This can be
+     *                  empty if there is any error found during parsing
+     * @return the error messages, which contain things like all the tags
+     *         VectorDrawble don't support or exception message.
+     */
+    public static String parseSvgToXml(File inputSVG, OutputStream outStream) {
+        // Write all the error message during parsing into SvgTree. and return here as getErrorLog().
+        // We will also log the exceptions here.
+        String errorLog = null;
+        try {
+            SvgTree svgTree = parse(inputSVG);
+            errorLog = svgTree.getErrorLog();
+            // When there was anything in the input SVG file that we can't
+            // convert to VectorDrawable, we logged them as errors.
+            // After we logged all the errors, we skipped the XML file generation.
+            if (svgTree.canConvertToVectorDrawable()) {
+                writeFile(outStream, svgTree);
+            }
+        } catch (Exception e) {
+            errorLog = "EXCEPTION in parsing " + inputSVG.getName() + ":\n" + e.getMessage();
+        }
+        return errorLog;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgGroupNode.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgGroupNode.java
new file mode 100644
index 0000000..3a0082d
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgGroupNode.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represent a SVG file's group element.
+ */
+class SvgGroupNode extends SvgNode {
+    private static Logger logger = Logger.getLogger(SvgGroupNode.class.getSimpleName());
+    private static final String INDENT_LEVEL = "    ";
+    private ArrayList<SvgNode> mChildren = new ArrayList<SvgNode>();
+
+    public SvgGroupNode(SvgTree svgTree, Node docNode, String name) {
+        super(svgTree, docNode, name);
+    }
+
+    public void addChild(SvgNode child) {
+        mChildren.add(child);
+    }
+
+    @Override
+    public void dumpNode(String indent) {
+        // Print the current group.
+        logger.log(Level.FINE, indent + "current group is :" + getName());
+
+        // Then print all the children.
+        for (SvgNode node : mChildren) {
+            node.dumpNode(indent + INDENT_LEVEL);
+        }
+    }
+
+    @Override
+    public boolean isGroupNode() {
+        return true;
+    }
+
+    @Override
+    public void transform(float a, float b, float c, float d, float e, float f) {
+        for (SvgNode p : mChildren) {
+            p.transform(a, b, c, d, e, f);
+        }
+    }
+
+    @Override
+    public void writeXML(OutputStreamWriter writer) throws IOException {
+        for (SvgNode node : mChildren) {
+            node.writeXML(writer);
+        }
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgLeafNode.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgLeafNode.java
new file mode 100644
index 0000000..72bdd70
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgLeafNode.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.annotations.Nullable;
+import com.google.common.collect.ImmutableMap;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represent a SVG file's leave element.
+ */
+class SvgLeafNode extends SvgNode {
+    private static Logger logger = Logger.getLogger(SvgLeafNode.class.getSimpleName());
+
+    private String mPathData;
+
+    // Key is the attributes for vector drawable, and the value is the converted from SVG.
+    private HashMap<String, String> mVdAttributesMap = new HashMap<String, String>();
+
+    public SvgLeafNode(SvgTree svgTree, Node node, String nodeName) {
+        super(svgTree, node, nodeName);
+    }
+
+    private String getAttributeValues(ImmutableMap<String, String> presentationMap) {
+        StringBuilder sb = new StringBuilder("/>\n");
+        for (String key : mVdAttributesMap.keySet()) {
+            String vectorDrawableAttr = presentationMap.get(key);
+            String svgValue = mVdAttributesMap.get(key);
+            String vdValue = svgValue.trim();
+            // There are several cases we need to convert from SVG format to
+            // VectorDrawable format. Like "none", "3px" or "rgb(255, 0, 0)"
+            if ("none".equals(vdValue)) {
+                vdValue = "#00000000";
+            } else if (vdValue.endsWith("px")){
+                vdValue = vdValue.substring(0, vdValue.length() - 2);
+            } else if (vdValue.startsWith("rgb")) {
+                vdValue = vdValue.substring(3, vdValue.length());
+                vdValue = convertRGBToHex(vdValue);
+                if (vdValue == null) {
+                    getTree().logErrorLine("Unsupported Color format " + vdValue, getDocumentNode(),
+                                           SvgTree.SvgLogLevel.ERROR);
+                }
+            }
+            String attr = "\n        " + vectorDrawableAttr + "=\"" +
+                          vdValue + "\"";
+            sb.insert(0, attr);
+
+        }
+        return sb.toString();
+    }
+
+    public static int clamp(int val, int min, int max) {
+        return Math.max(min, Math.min(max, val));
+    }
+
+    /**
+     * SVG allows using rgb(int, int, int) or rgb(float%, float%, float%) to
+     * represent a color, but Android doesn't. Therefore, we need to convert
+     * them into #RRGGBB format.
+     * @param svgValue in either "(int, int, int)" or "(float%, float%, float%)"
+     * @return #RRGGBB in hex format, or null, if an error is found.
+     */
+    @Nullable
+    private String convertRGBToHex(String svgValue) {
+        // We don't support color keyword yet.
+        // http://www.w3.org/TR/SVG11/types.html#ColorKeywords
+        String result = null;
+        String functionValue = svgValue.trim();
+        functionValue = svgValue.substring(1, functionValue.length() - 1);
+        // After we cut the "(", ")", we can deal with the numbers.
+        String[] numbers = functionValue.split(",");
+        if (numbers.length != 3) {
+            return null;
+        }
+        int[] color = new int[3];
+        for (int i = 0; i < 3; i ++) {
+            String number = numbers[i];
+            number = number.trim();
+            if (number.endsWith("%")) {
+                float value = Float.parseFloat(number.substring(0, number.length() - 1));
+                color[i] = clamp((int)(value * 255.0f / 100.0f), 0, 255);
+            } else {
+                int value = Integer.parseInt(number);
+                color[i] = clamp(value, 0, 255);
+            }
+        }
+        StringBuilder builder = new StringBuilder();
+        builder.append("#");
+        for (int i = 0; i < 3; i ++) {
+            builder.append(String.format("%02X", color[i]));
+        }
+        result = builder.toString();
+        assert result.length() == 7;
+        return result;
+    }
+
+    @Override
+    public void dumpNode(String indent) {
+        logger.log(Level.FINE, indent + (mPathData != null ? mPathData : " null pathData ") +
+                               (mName != null ? mName : " null name "));
+    }
+
+    public void setPathData(String pathData) {
+        mPathData = pathData;
+    }
+
+    @Override
+    public boolean isGroupNode() {
+        return false;
+    }
+
+    @Override
+    public void transform(float a, float b, float c, float d, float e, float f) {
+        if ("none".equals(mVdAttributesMap.get("fill")) || (mPathData == null)) {
+            // Nothing to draw and transform, early return.
+            return;
+        }
+        // TODO: We need to just apply the transformation to group.
+        VdPath.Node[] n = VdParser.parsePath(mPathData);
+        if (!(a == 1 && d == 1 && b == 0 && c == 0 && e == 0 && f == 0)) {
+            VdPath.Node.transform(a, b, c, d, e, f, n);
+        }
+        mPathData = VdPath.Node.NodeListToString(n);
+    }
+
+    @Override
+    public void writeXML(OutputStreamWriter writer) throws IOException {
+        String fillColor = mVdAttributesMap.get(Svg2Vector.SVG_FILL_COLOR);
+        String strokeColor = mVdAttributesMap.get(Svg2Vector.SVG_STROKE_COLOR);
+        logger.log(Level.FINE, "fill color " + fillColor);
+        boolean emptyFill = fillColor != null && ("none".equals(fillColor) || "#0000000".equals(fillColor));
+        boolean emptyStroke = strokeColor == null || "none".equals(strokeColor);
+        boolean emptyPath = mPathData == null;
+        boolean nothingToDraw = emptyPath || emptyFill && emptyStroke;
+        if (nothingToDraw) {
+            return;
+        }
+
+        writer.write("    <path\n");
+        if (!mVdAttributesMap.containsKey(Svg2Vector.SVG_FILL_COLOR)) {
+            logger.log(Level.FINE, "ADDING FILL SVG_FILL_COLOR");
+            writer.write("        android:fillColor=\"#FF000000\"\n");
+        }
+        writer.write("        android:pathData=\"" + mPathData + "\"");
+        writer.write(getAttributeValues(Svg2Vector.presentationMap));
+    }
+
+    public void fillPresentationAttributes(String name, String value) {
+        logger.log(Level.FINE, ">>>> PROP " + name + " = " + value);
+        if (value.startsWith("url("))  {
+            getTree().logErrorLine("Unsupported URL value: " + value, getDocumentNode(),
+                                   SvgTree.SvgLogLevel.ERROR);
+            return;
+        }
+        mVdAttributesMap.put(name, value);
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgNode.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgNode.java
new file mode 100644
index 0000000..494dafa
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgNode.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+/**
+ * Parent class for a SVG file's node, can be either group or leave element.
+ */
+abstract class SvgNode {
+
+    protected String mName;
+    // Keep a reference to the tree in order to dump the error log.
+    private SvgTree mSvgTree;
+    // Use document node to get the line number for error reporting.
+    private Node mDocumentNode;
+
+    public SvgNode(SvgTree svgTree, Node node, String name) {
+        mName = name;
+        mSvgTree = svgTree;
+        mDocumentNode = node;
+    }
+
+    protected SvgTree getTree() {
+        return mSvgTree;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public Node getDocumentNode() {
+        return mDocumentNode;
+    }
+
+    /**
+     * dump the current node's debug info.
+     */
+    public abstract void dumpNode(String indent);
+
+    /**
+     * Write the Node content into the VectorDrawable's XML file.
+     */
+    public abstract void writeXML(OutputStreamWriter writer) throws IOException;
+
+    /**
+     * @return true the node is a group node.
+     */
+    public abstract boolean isGroupNode();
+
+    /**
+     * Transform the current Node with the transformation matrix.
+     */
+    public abstract void transform(float a, float b, float c, float d, float e, float f);
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgTree.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgTree.java
new file mode 100644
index 0000000..86263e5
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgTree.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
+import com.android.utils.PositionXmlParser;
+import com.google.common.base.Strings;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represent the SVG file in an internal data structure as a tree.
+ */
+class SvgTree {
+    private static Logger logger = Logger.getLogger(SvgTree.class.getSimpleName());
+
+    public float w;
+    public float h;
+    public float[] matrix;
+    public float[] viewBox;
+    public float mScaleFactor = 1;
+
+    private SvgGroupNode mRoot;
+    private String mFileName;
+
+    private ArrayList<String> mErrorLines = new ArrayList<String>();
+
+    public enum SvgLogLevel {
+        ERROR,
+        WARNING
+    }
+
+    public Document parse(File f) throws Exception {
+        mFileName = f.getName();
+        Document doc = PositionXmlParser.parse(new FileInputStream(f), false);
+        return doc;
+    }
+
+    public void normalize() {
+        if (matrix != null) {
+            transform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+        }
+
+        if (viewBox != null && (viewBox[0] != 0 || viewBox[1] != 0)) {
+            transform(1, 0, 0, 1, -viewBox[0], -viewBox[1]);
+        }
+        logger.log(Level.FINE, "matrix=" + Arrays.toString(matrix));
+    }
+
+    private void transform(float a, float b, float c, float d, float e, float f) {
+        mRoot.transform(a, b, c, d, e, f);
+    }
+
+    public void dump(SvgGroupNode root) {
+        logger.log(Level.FINE, "current file is :" + mFileName);
+        root.dumpNode("");
+    }
+
+    public void setRoot(SvgGroupNode root) {
+        mRoot = root;
+    }
+
+    @Nullable
+    public SvgGroupNode getRoot() {
+        return mRoot;
+    }
+
+    public void logErrorLine(String s, Node node, SvgLogLevel level) {
+        if (!Strings.isNullOrEmpty(s)) {
+            if (node != null) {
+                SourcePosition position = getPosition(node);
+                mErrorLines.add(level.name() + "@ line " + (position.getStartLine() + 1) +
+                                " " + s + "\n");
+            } else {
+                mErrorLines.add(s);
+            }
+        }
+    }
+
+    /**
+     * @return Error log. Empty string if there are no errors.
+     */
+    @NonNull
+    public String getErrorLog() {
+        StringBuilder errorBuilder = new StringBuilder();
+        if (!mErrorLines.isEmpty()) {
+            errorBuilder.append("In " + mFileName + ":\n");
+        }
+        for (String log : mErrorLines) {
+            errorBuilder.append(log);
+        }
+        return errorBuilder.toString();
+    }
+
+    /**
+     * @return true when there is no error found when parsing the SVG file.
+     */
+    public boolean canConvertToVectorDrawable() {
+        return mErrorLines.isEmpty();
+    }
+
+    private SourcePosition getPosition(Node node) {
+        return PositionXmlParser.getPosition(node);
+    }
+
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdElement.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdElement.java
new file mode 100644
index 0000000..9422e8d
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdElement.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+/**
+ * Used to represent one VectorDrawble's element, can be a group or path.
+ */
+abstract class VdElement {
+    String mName;
+
+    public String getName() {
+        return mName;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdGroup.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdGroup.java
new file mode 100644
index 0000000..5a6d37b
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdGroup.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import java.util.ArrayList;
+
+/**
+ * Used to represent one VectorDrawble's group element.
+ * TODO: Add group transformation here.
+ */
+class VdGroup extends VdElement{
+
+    public VdGroup() {
+        mName = this.toString(); // to ensure paths have unique names
+    }
+    // Children can be either a {@link VdPath} or {@link VdGroup}
+    private ArrayList<VdElement> mChildren = new ArrayList<VdElement>();
+
+    public void add(VdElement pathOrGroup) {
+        mChildren.add(pathOrGroup);
+    }
+
+    public ArrayList<VdElement> getChildren() {
+        return mChildren;
+    }
+
+    public int size() {
+        return mChildren.size();
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdIcon.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdIcon.java
new file mode 100644
index 0000000..f61e4cb
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdIcon.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.ide.common.util.AssetUtil;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.net.URL;
+
+/**
+ * VdIcon wrap every vector drawable from Material Library into an icon.
+ * All of them are shown in a table for developer to pick.
+ */
+public class VdIcon implements Icon, Comparable<VdIcon> {
+    private VdTree mVdTree;
+    private final String mName;
+    private final URL mUrl;
+
+    public VdIcon(URL url) {
+        setDynamicIcon(url);
+        mUrl = url;
+        String fileName = url.getFile();
+        mName = fileName.substring(fileName.lastIndexOf("/") + 1);
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public URL getURL() {
+        return mUrl;
+    }
+
+    public void setDynamicIcon(URL url) {
+        final VdParser p = new VdParser();
+        try {
+            mVdTree = p.parse(url.openStream(), null);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void paintIcon(Component c, Graphics g, int x, int y) {
+        // We knew all the icons from Material library are square shape.
+        int minSize = Math.min(c.getWidth(), c.getHeight());
+        final BufferedImage image = AssetUtil.newArgbBufferedImage(minSize, minSize);
+        mVdTree.drawIntoImage(image);
+
+        // Draw in the center of the component.
+        Rectangle rect = new Rectangle(0, 0, c.getWidth(), c.getHeight());
+        AssetUtil.drawCenterInside((Graphics2D)g, image, rect);
+    }
+
+    @Override
+    public int getIconWidth() {
+        return (int) (mVdTree != null ? mVdTree.mPortWidth : 0);
+    }
+
+    @Override
+    public int getIconHeight() {
+        return (int) (mVdTree != null ? mVdTree.mPortHeight : 0);
+    }
+
+    @Override
+    public int compareTo(VdIcon other) {
+        return mName.compareTo(other.mName);
+    }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdNodeRender.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdNodeRender.java
new file mode 100644
index 0000000..9cba65f
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdNodeRender.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import java.awt.geom.Path2D;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Given an array of VdPath.Node, generate a Path2D object.
+ * In another word, this is the engine which converts the pathData into
+ * a Path2D object, which is able to draw on Swing components.
+ * The logic and math here are the same as PathParser.java in framework.
+ */
+class VdNodeRender {
+    private static Logger logger = Logger.getLogger(VdNodeRender.class
+            .getSimpleName());
+
+    public static void creatPath(VdPath.Node[] node, Path2D path) {
+        float[] current = new float[6];
+        char lastCmd = ' ';
+        for (int i = 0; i < node.length; i++) {
+            addCommand(path, current, node[i].type, lastCmd,node[i].params);
+            lastCmd = node[i].type;
+        }
+    }
+
+    private static void addCommand(Path2D path, float[] current, char cmd,
+            char lastCmd, float[] val) {
+
+        int incr = 2;
+
+        float cx = current[0];
+        float cy = current[1];
+        float cpx = current[2];
+        float cpy = current[3];
+        float loopX = current[4];
+        float loopY = current[5];
+
+        switch (cmd) {
+            case 'z':
+            case 'Z':
+                path.closePath();
+                cx = loopX;
+                cy = loopY;
+            case 'm':
+            case 'M':
+            case 'l':
+            case 'L':
+            case 't':
+            case 'T':
+                incr = 2;
+                break;
+            case 'h':
+            case 'H':
+            case 'v':
+            case 'V':
+                incr = 1;
+                break;
+            case 'c':
+            case 'C':
+                incr = 6;
+                break;
+            case 's':
+            case 'S':
+            case 'q':
+            case 'Q':
+                incr = 4;
+                break;
+            case 'a':
+            case 'A':
+                incr = 7;
+        }
+
+        for (int k = 0; k < val.length; k += incr) {
+            boolean reflectCtrl = false;
+            float tempReflectedX, tempReflectedY;
+
+            switch (cmd) {
+                case 'm':
+                    cx += val[k + 0];
+                    cy += val[k + 1];
+                    path.moveTo(cx, cy);
+                    loopX = cx;
+                    loopY = cy;
+                    break;
+                case 'M':
+                    cx = val[k + 0];
+                    cy = val[k + 1];
+                    path.moveTo(cx, cy);
+                    loopX = cx;
+                    loopY = cy;
+                    break;
+                case 'l':
+                    cx += val[k + 0];
+                    cy += val[k + 1];
+                    path.lineTo(cx, cy);
+                    break;
+                case 'L':
+                    cx = val[k + 0];
+                    cy = val[k + 1];
+                    path.lineTo(cx, cy);
+                    break;
+                case 'z':
+                case 'Z':
+                    path.closePath();
+                    cx = loopX;
+                    cy = loopY;
+                    break;
+                case 'h':
+                    cx += val[k + 0];
+                    path.lineTo(cx, cy);
+                    break;
+                case 'H':
+                    path.lineTo(val[k + 0], cy);
+                    cx = val[k + 0];
+                    break;
+                case 'v':
+                    cy += val[k + 0];
+                    path.lineTo(cx, cy);
+                    break;
+                case 'V':
+                    path.lineTo(cx, val[k + 0]);
+                    cy = val[k + 0];
+                    break;
+                case 'c':
+                    path.curveTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2],
+                            cy + val[k + 3], cx + val[k + 4], cy + val[k + 5]);
+                    cpx = cx + val[k + 2];
+                    cpy = cy + val[k + 3];
+                    cx += val[k + 4];
+                    cy += val[k + 5];
+                    break;
+                case 'C':
+                    path.curveTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                            val[k + 4], val[k + 5]);
+                    cx = val[k + 4];
+                    cy = val[k + 5];
+                    cpx = val[k + 2];
+                    cpy = val[k + 3];
+                    break;
+                case 's':
+                    reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' || lastCmd == 'S');
+                    path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2
+                            * cy - cpy : cy, cx + val[k + 0], cy + val[k + 1], cx
+                            + val[k + 2], cy + val[k + 3]);
+
+                    cpx = cx + val[k + 0];
+                    cpy = cy + val[k + 1];
+                    cx += val[k + 2];
+                    cy += val[k + 3];
+                    break;
+                case 'S':
+                    reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' || lastCmd == 'S');
+                    path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2
+                            * cy - cpy : cy, val[k + 0], val[k + 1], val[k + 2],
+                            val[k + 3]);
+                    cpx = (val[k + 0]);
+                    cpy = (val[k + 1]);
+                    cx = val[k + 2];
+                    cy = val[k + 3];
+                    break;
+                case 'q':
+                    path.quadTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2],
+                            cy + val[k + 3]);
+                    cpx = cx + val[k + 0];
+                    cpy = cy + val[k + 1];
+                    // Note that we have to update cpx first, since cx will be updated here.
+                    cx += val[k + 2];
+                    cy += val[k + 3];
+                    break;
+                case 'Q':
+                    path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                    cx = val[k + 2];
+                    cy = val[k + 3];
+                    cpx = val[k + 0];
+                    cpy = val[k + 1];
+                    break;
+                case 't':
+                    reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' || lastCmd == 'T');
+                    tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx;
+                    tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy;
+                    path.quadTo(tempReflectedX, tempReflectedY, cx + val[k + 0], cy + val[k + 1]);
+                    cpx = tempReflectedX;
+                    cpy = tempReflectedY;
+                    cx += val[k + 0];
+                    cy += val[k + 1];
+                    break;
+                case 'T':
+                    reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' || lastCmd == 'T');
+                    tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx;
+                    tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy;
+                    path.quadTo(tempReflectedX, tempReflectedY, val[k + 0], val[k + 1]);
+                    cx = val[k + 0];
+                    cy = val[k + 1];
+                    cpx = tempReflectedX;
+                    cpy = tempReflectedY;
+                    break;
+                case 'a':
+                    // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+                    drawArc(path, cx, cy, val[k + 5] + cx, val[k + 6] + cy,
+                            val[k + 0], val[k + 1], val[k + 2], val[k + 3] != 0,
+                                    val[k + 4] != 0);
+                    cx += val[k + 5];
+                    cy += val[k + 6];
+                    cpx = cx;
+                    cpy = cy;
+
+                    break;
+                case 'A':
+                    drawArc(path, cx, cy, val[k + 5], val[k + 6], val[k + 0],
+                            val[k + 1], val[k + 2], val[k + 3] != 0,
+                                    val[k + 4] != 0);
+                    cx = val[k + 5];
+                    cy = val[k + 6];
+                    cpx = cx;
+                    cpy = cy;
+                    break;
+
+            }
+            lastCmd = cmd;
+        }
+        current[0] = cx;
+        current[1] = cy;
+        current[2] = cpx;
+        current[3] = cpy;
+        current[4] = loopX;
+        current[5] = loopY;
+
+    }
+
+    private static void drawArc(Path2D p, float x0, float y0, float x1,
+            float y1, float a, float b, float theta, boolean isMoreThanHalf,
+            boolean isPositiveArc) {
+
+        logger.log(Level.FINE, "(" + x0 + "," + y0 + ")-(" + x1 + "," + y1
+                + ") {" + a + " " + b + "}");
+        /* Convert rotation angle from degrees to radians */
+        double thetaD = theta * Math.PI / 180.0f;
+        /* Pre-compute rotation matrix entries */
+        double cosTheta = Math.cos(thetaD);
+        double sinTheta = Math.sin(thetaD);
+        /* Transform (x0, y0) and (x1, y1) into unit space */
+        /* using (inverse) rotation, followed by (inverse) scale */
+        double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+        double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+        double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+        double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+        logger.log(Level.FINE, "unit space (" + x0p + "," + y0p + ")-(" + x1p
+                + "," + y1p + ")");
+        /* Compute differences and averages */
+        double dx = x0p - x1p;
+        double dy = y0p - y1p;
+        double xm = (x0p + x1p) / 2;
+        double ym = (y0p + y1p) / 2;
+        /* Solve for intersecting unit circles */
+        double dsq = dx * dx + dy * dy;
+        if (dsq == 0.0) {
+            logger.log(Level.FINE, " Points are coincident");
+            return; /* Points are coincident */
+        }
+        double disc = 1.0 / dsq - 1.0 / 4.0;
+        if (disc < 0.0) {
+            logger.log(Level.FINE, "Points are too far apart " + dsq);
+            float adjust = (float) (Math.sqrt(dsq) / 1.99999);
+            drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta,
+                    isMoreThanHalf, isPositiveArc);
+            return; /* Points are too far apart */
+        }
+        double s = Math.sqrt(disc);
+        double sdx = s * dx;
+        double sdy = s * dy;
+        double cx;
+        double cy;
+        if (isMoreThanHalf == isPositiveArc) {
+            cx = xm - sdy;
+            cy = ym + sdx;
+        } else {
+            cx = xm + sdy;
+            cy = ym - sdx;
+        }
+
+        double eta0 = Math.atan2((y0p - cy), (x0p - cx));
+        logger.log(Level.FINE, "eta0 = Math.atan2( " + (y0p - cy) + " , "
+                + (x0p - cx) + ") = " + Math.toDegrees(eta0));
+
+        double eta1 = Math.atan2((y1p - cy), (x1p - cx));
+        logger.log(Level.FINE, "eta1 = Math.atan2( " + (y1p - cy) + " , "
+                + (x1p - cx) + ") = " + Math.toDegrees(eta1));
+        double sweep = (eta1 - eta0);
+        if (isPositiveArc != (sweep >= 0)) {
+            if (sweep > 0) {
+                sweep -= 2 * Math.PI;
+            } else {
+                sweep += 2 * Math.PI;
+            }
+        }
+
+        cx *= a;
+        cy *= b;
+        double tcx = cx;
+        cx = cx * cosTheta - cy * sinTheta;
+        cy = tcx * sinTheta + cy * cosTheta;
+        logger.log(
+                Level.FINE,
+                        "cx, cy, a, b, x0, y0, thetaD, eta0, sweep = " + cx + " , "
+                        + cy + " , " + a + " , " + b + " , " + x0 + " , " + y0
+                        + " , " + Math.toDegrees(thetaD) + " , "
+                        + Math.toDegrees(eta0) + " , " + Math.toDegrees(sweep));
+
+        arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+    }
+
+    /**
+     * Converts an arc to cubic Bezier segments and records them in p.
+     *
+     * @param p The target for the cubic Bezier segments
+     * @param cx The x coordinate center of the ellipse
+     * @param cy The y coordinate center of the ellipse
+     * @param a The radius of the ellipse in the horizontal direction
+     * @param b The radius of the ellipse in the vertical direction
+     * @param e1x E(eta1) x coordinate of the starting point of the arc
+     * @param e1y E(eta2) y coordinate of the starting point of the arc
+     * @param theta The angle that the ellipse bounding rectangle makes with the horizontal plane
+     * @param start The start angle of the arc on the ellipse
+     * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+     */
+    private static void arcToBezier(Path2D p, double cx, double cy, double a,
+            double b, double e1x, double e1y, double theta, double start,
+            double sweep) {
+        // Taken from equations at:
+        // http://spaceroots.org/documents/ellipse/node8.html
+        // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+        // Maximum of 45 degrees per cubic Bezier segment
+        int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+
+        double eta1 = start;
+        double cosTheta = Math.cos(theta);
+        double sinTheta = Math.sin(theta);
+        double cosEta1 = Math.cos(eta1);
+        double sinEta1 = Math.sin(eta1);
+        double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+        double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+        double anglePerSegment = sweep / numSegments;
+        for (int i = 0; i < numSegments; i++) {
+            double eta2 = eta1 + anglePerSegment;
+            double sinEta2 = Math.sin(eta2);
+            double cosEta2 = Math.cos(eta2);
+            double e2x = cx + (a * cosTheta * cosEta2)
+                    - (b * sinTheta * sinEta2);
+            double e2y = cy + (a * sinTheta * cosEta2)
+                    + (b * cosTheta * sinEta2);
+            double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+            double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+            double tanDiff2 = Math.tan((eta2 - eta1) / 2);
+            double alpha = Math.sin(eta2 - eta1)
+                    * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+            double q1x = e1x + alpha * ep1x;
+            double q1y = e1y + alpha * ep1y;
+            double q2x = e2x - alpha * ep2x;
+            double q2y = e2y - alpha * ep2y;
+
+            p.curveTo((float) q1x, (float) q1y, (float) q2x, (float) q2y,
+                    (float) e2x, (float) e2y);
+            eta1 = eta2;
+            e1x = e2x;
+            e1y = e2y;
+            ep1x = ep2x;
+            ep1y = ep2y;
+        }
+    }
+
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdOverrideInfo.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdOverrideInfo.java
new file mode 100644
index 0000000..4935027
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdOverrideInfo.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+/**
+ * Used to represent info to override the VectorDrawble's XML file content.
+ */
+public class VdOverrideInfo {
+    private int mWidth;
+    private int mHeight;
+    private int mOpacity;
+    private boolean mAutoMirrored;
+
+    public VdOverrideInfo(int width, int height, int opacity, boolean autoMirrored) {
+        mWidth = width;
+        mHeight = height;
+        mOpacity = opacity;
+        mAutoMirrored = autoMirrored;
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public void setWidth(int width) {
+        mWidth = width;
+    }
+
+    public int getOpacity() {
+        return mOpacity;
+    }
+
+    public void setOpacity(int opacity) {
+        mOpacity = opacity;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public void setHeight(int height) {
+        mHeight = height;
+    }
+
+    boolean needsOverrideWidth() {
+        return getWidth() > 0;
+    }
+
+    boolean needsOverrideHeight() {
+        return getHeight() > 0;
+    }
+
+    boolean needsOverrideOpacity() {
+        return getOpacity() < 100 && getOpacity() >= 0;
+    }
+
+    boolean needsOverrideAutoMirrored() {
+        return mAutoMirrored;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdParser.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdParser.java
new file mode 100644
index 0000000..a1a4106
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdParser.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.SdkConstants;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Parse a VectorDrawble's XML file, and generate an internal tree representation,
+ * which can be used for drawing / previewing.
+ */
+class VdParser {
+    private static Logger logger = Logger.getLogger(VdParser.class.getSimpleName());
+
+    private static final String PATH_SHIFT_X = "shift-x";
+    private static final String PATH_SHIFT_Y = "shift-y";
+
+    private static final String SHAPE_VECTOR = "vector";
+    private static final String SHAPE_PATH = "path";
+    private static final String SHAPE_GROUP = "group";
+
+    private static final String PATH_ID = "android:name";
+    private static final String PATH_DESCRIPTION = "android:pathData";
+    private static final String PATH_FILL = "android:fillColor";
+    private static final String PATH_FILL_OPACTIY = "android:fillAlpha";
+    private static final String PATH_STROKE = "android:strokeColor";
+    private static final String PATH_STROKE_OPACTIY = "android:strokeAlpha";
+
+    private static final String PATH_STROKE_WIDTH = "android:strokeWidth";
+    private static final String PATH_ROTATION = "android:rotation";
+    private static final String PATH_ROTATION_X = "android:pivotX";
+    private static final String PATH_ROTATION_Y = "android:pivotY";
+    private static final String PATH_TRIM_START = "android:trimPathStart";
+    private static final String PATH_TRIM_END = "android:trimPathEnd";
+    private static final String PATH_TRIM_OFFSET = "android:trimPathOffset";
+    private static final String PATH_STROKE_LINECAP = "android:strokeLinecap";
+    private static final String PATH_STROKE_LINEJOIN = "android:strokeLinejoin";
+    private static final String PATH_STROKE_MITERLIMIT = "android:strokeMiterlimit";
+    private static final String PATH_CLIP = "android:clipToPath";
+    private static final String LINECAP_BUTT = "butt";
+    private static final String LINECAP_ROUND = "round";
+    private static final String LINECAP_SQUARE = "square";
+    private static final String LINEJOIN_MITER = "miter";
+    private static final String LINEJOIN_ROUND = "round";
+    private static final String LINEJOIN_BEVEL = "bevel";
+
+    interface ElemParser {
+        void parse(VdTree path, Attributes attributes);
+    }
+
+    ElemParser mParseSize = new ElemParser() {
+        @Override
+        public void parse(VdTree tree, Attributes attributes) {
+            parseSize(tree, attributes);
+        }
+    };
+
+    ElemParser mParsePath = new ElemParser() {
+        @Override
+        public void parse(VdTree tree, Attributes attributes) {
+            VdPath p = parsePathAttributes(attributes);
+            tree.add(p);
+        }
+    };
+
+    ElemParser mParseGroup = new ElemParser() {
+        @Override
+        public void parse(VdTree tree, Attributes attributes) {
+            VdGroup g = parseGroupAttributes(attributes);
+            tree.add(g);
+        }
+    };
+
+    HashMap<String, ElemParser> tagSwitch = new HashMap<String, ElemParser>();
+    {
+        tagSwitch.put(SHAPE_VECTOR, mParseSize);
+        tagSwitch.put(SHAPE_PATH, mParsePath);
+        tagSwitch.put(SHAPE_GROUP, mParseGroup);
+        // TODO: add <g> tag and start to build the tree.
+    }
+
+    // Note that the incoming file is the VectorDrawable's XML file, not the SVG.
+    // TODO: Use Document to parse and make sure no big performance difference.
+    public VdTree parse(InputStream is, StringBuilder vdErrorLog) {
+        try {
+            final VdTree tree = new VdTree();
+            SAXParserFactory spf = SAXParserFactory.newInstance();
+            SAXParser sp = spf.newSAXParser();
+            XMLReader xr = sp.getXMLReader();
+
+            xr.setContentHandler(new ContentHandler() {
+                String space = " ";
+
+                    @Override
+                public void setDocumentLocator(Locator locator) {
+                }
+
+                    @Override
+                public void startDocument() throws SAXException {
+                }
+
+                    @Override
+                public void endDocument() throws SAXException {
+                }
+
+                    @Override
+                public void startPrefixMapping(String s, String s2) throws SAXException {
+                }
+
+                    @Override
+                public void endPrefixMapping(String s) throws SAXException {
+                }
+
+                    @Override
+                public void startElement(String s, String s2, String s3, Attributes attributes)
+                        throws SAXException {
+                    String name = s3;
+                    if (tagSwitch.containsKey(name)) {
+                        tagSwitch.get(name).parse(tree, attributes);
+                    }
+                    space += " ";
+                }
+
+                    @Override
+                public void endElement(String s, String s2, String s3) throws SAXException {
+                    space = space.substring(1);
+                }
+
+                    @Override
+                public void characters(char[] chars, int i, int i2) throws SAXException {
+                }
+
+                    @Override
+                public void ignorableWhitespace(char[] chars, int i, int i2) throws SAXException {
+                }
+
+                    @Override
+                public void processingInstruction(String s, String s2) throws SAXException {
+                }
+
+                    @Override
+                public void skippedEntity(String s) throws SAXException {
+                }
+            });
+            xr.parse(new InputSource(is));
+            tree.parseFinish();
+            return tree;
+        } catch (Exception e) {
+            vdErrorLog.append("Exception while parsing XML file:\n" + e.getMessage());
+            return null;
+        }
+    }
+
+    public VdParser() {
+    }
+
+    private static int nextStart(String s, int end) {
+        char c;
+
+        while (end < s.length()) {
+            c = s.charAt(end);
+            // Note that 'e' or 'E' are not valid path commands, but could be
+            // used for floating point numbers' scientific notation.
+            // Therefore, when searching for next command, we should ignore 'e'
+            // and 'E'.
+            if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
+                    && c != 'e' && c != 'E') {
+                return end;
+            }
+            end++;
+        }
+        return end;
+    }
+
+    public static VdPath.Node[] parsePath(String value) {
+        int start = 0;
+        int end = 1;
+
+        ArrayList<VdPath.Node> list = new ArrayList<VdPath.Node>();
+        while (end < value.length()) {
+            end = nextStart(value, end);
+            String s = value.substring(start, end);
+            float[] val = getFloats(s);
+
+            addNode(list, s.charAt(0), val);
+
+            start = end;
+            end++;
+        }
+        if ((end - start) == 1 && start < value.length()) {
+
+            addNode(list, value.charAt(start), new float[0]);
+        }
+        return list.toArray(new VdPath.Node[list.size()]);
+    }
+
+    private static class ExtractFloatResult {
+        // We need to return the position of the next separator and whether the
+        // next float starts with a '-' or a '.'.
+        int mEndPosition;
+        boolean mEndWithNegOrDot;
+    }
+
+    /**
+     * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
+     * end (exclusive). The original order of elements is preserved.
+     * If {@code end} is greater than {@code original.length}, the result is padded
+     * with the value {@code 0.0f}.
+     *
+     * @param original the original array
+     * @param start the start index, inclusive
+     * @param end the end index, exclusive
+     * @return the new array
+     * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
+     * @throws IllegalArgumentException if {@code start > end}
+     * @throws NullPointerException if {@code original == null}
+     */
+    private static float[] copyOfRange(float[] original, int start, int end) {
+        if (start > end) {
+            throw new IllegalArgumentException();
+        }
+        int originalLength = original.length;
+        if (start < 0 || start > originalLength) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        int resultLength = end - start;
+        int copyLength = Math.min(resultLength, originalLength - start);
+        float[] result = new float[resultLength];
+        System.arraycopy(original, start, result, 0, copyLength);
+        return result;
+    }
+
+    /**
+     * Calculate the position of the next comma or space or negative sign
+     * @param s the string to search
+     * @param start the position to start searching
+     * @param result the result of the extraction, including the position of the
+     * the starting position of next number, whether it is ending with a '-'.
+     */
+    private static void extract(String s, int start, ExtractFloatResult result) {
+        // Now looking for ' ', ',', '.' or '-' from the start.
+        int currentIndex = start;
+        boolean foundSeparator = false;
+        result.mEndWithNegOrDot = false;
+        boolean secondDot = false;
+        boolean isExponential = false;
+        for (; currentIndex < s.length(); currentIndex++) {
+            boolean isPrevExponential = isExponential;
+            isExponential = false;
+            char currentChar = s.charAt(currentIndex);
+            switch (currentChar) {
+                case ' ':
+                case ',':
+                    foundSeparator = true;
+                    break;
+                case '-':
+                    // The negative sign following a 'e' or 'E' is not a separator.
+                    if (currentIndex != start && !isPrevExponential) {
+                        foundSeparator = true;
+                        result.mEndWithNegOrDot = true;
+                    }
+                    break;
+                case '.':
+                    if (!secondDot) {
+                        secondDot = true;
+                    } else {
+                        // This is the second dot, and it is considered as a separator.
+                        foundSeparator = true;
+                        result.mEndWithNegOrDot = true;
+                    }
+                    break;
+                case 'e':
+                case 'E':
+                    isExponential = true;
+                    break;
+            }
+            if (foundSeparator) {
+                break;
+            }
+        }
+        // When there is nothing found, then we put the end position to the end
+        // of the string.
+        result.mEndPosition = currentIndex;
+    }
+
+    /**
+     * parse the floats in the string this is an optimized version of parseFloat(s.split(",|\\s"));
+     *
+     * @param s the string containing a command and list of floats
+     * @return array of floats
+     */
+    private static float[] getFloats(String s) {
+        if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
+            return new float[0];
+        }
+        try {
+            float[] results = new float[s.length()];
+            int count = 0;
+            int startPosition = 1;
+            int endPosition = 0;
+
+            ExtractFloatResult result = new ExtractFloatResult();
+            int totalLength = s.length();
+
+            // The startPosition should always be the first character of the
+            // current number, and endPosition is the character after the current
+            // number.
+            while (startPosition < totalLength) {
+                extract(s, startPosition, result);
+                endPosition = result.mEndPosition;
+
+                if (startPosition < endPosition) {
+                    results[count++] = Float.parseFloat(
+                            s.substring(startPosition, endPosition));
+                }
+
+                if (result.mEndWithNegOrDot) {
+                    // Keep the '-' or '.' sign with next number.
+                    startPosition = endPosition;
+                } else {
+                    startPosition = endPosition + 1;
+                }
+            }
+            return copyOfRange(results, 0, count);
+        } catch (NumberFormatException e) {
+            throw new RuntimeException("error in parsing \"" + s + "\"", e);
+        }
+    }
+    // End of copy from PathParser.java
+    ////////////////////////////////////////////////////////////////
+    private static void addNode(ArrayList<VdPath.Node> list, char cmd, float[] val) {
+        list.add(new VdPath.Node(cmd, val));
+    }
+
+    public VdTree parse(URL r, StringBuilder vdErrorLog) throws Exception {
+        return parse(r.openStream(), vdErrorLog);
+    }
+
+    private void parseSize(VdTree vdTree, Attributes attributes) {
+
+        Pattern pattern = Pattern.compile("^\\s*(\\d+(\\.\\d+)*)\\s*([a-zA-Z]+)\\s*$");
+        HashMap<String, Integer> m = new HashMap<String, Integer>();
+        m.put(SdkConstants.UNIT_PX, 1);
+        m.put(SdkConstants.UNIT_DIP, 1);
+        m.put(SdkConstants.UNIT_DP, 1);
+        m.put(SdkConstants.UNIT_SP, 1);
+        m.put(SdkConstants.UNIT_PT, 1);
+        m.put(SdkConstants.UNIT_IN, 1);
+        m.put(SdkConstants.UNIT_MM, 1);
+        int len = attributes.getLength();
+
+        for (int i = 0; i < len; i++) {
+            String name = attributes.getQName(i);
+            String value = attributes.getValue(i);
+            Matcher matcher = pattern.matcher(value);
+            float size = 0;
+            if (matcher.matches()) {
+                float v = Float.parseFloat(matcher.group(1));
+                String unit = matcher.group(3).toLowerCase(Locale.getDefault());
+                size = v;
+            }
+            // -- Extract dimension units.
+
+            if ("android:width".equals(name)) {
+                vdTree.mBaseWidth = size;
+            } else if ("android:height".equals(name)) {
+                vdTree.mBaseHeight = size;
+            } else if ("android:viewportWidth".equals(name)) {
+                vdTree.mPortWidth = Float.parseFloat(value);
+            } else if ("android:viewportHeight".equals(name)) {
+                vdTree.mPortHeight = Float.parseFloat(value);
+            } else if ("android:alpha".equals(name)) {
+                vdTree.mRootAlpha = Float.parseFloat(value);
+            } else {
+                continue;
+            }
+
+        }
+    }
+
+    private VdPath parsePathAttributes(Attributes attributes) {
+        int len = attributes.getLength();
+        VdPath vgPath = new VdPath();
+
+        for (int i = 0; i < len; i++) {
+            String name = attributes.getQName(i);
+            String value = attributes.getValue(i);
+            logger.log(Level.FINE, "name " + name + "value " + value);
+            setNameValue(vgPath, name, value);
+        }
+        return vgPath;
+    }
+
+    private VdGroup parseGroupAttributes(Attributes attributes) {
+        int len = attributes.getLength();
+        VdGroup vgGroup = new VdGroup();
+
+        for (int i = 0; i < len; i++) {
+            String name = attributes.getQName(i);
+            String value = attributes.getValue(i);
+            logger.log(Level.FINE, "name " + name + "value " + value);
+        }
+        return vgGroup;
+    }
+
+    public void setNameValue(VdPath vgPath, String name, String value) {
+        if (PATH_DESCRIPTION.equals(name)) {
+            vgPath.mNode = parsePath(value);
+        } else if (PATH_ID.equals(name)) {
+            vgPath.mName = value;
+        } else if (PATH_FILL.equals(name)) {
+            vgPath.mFillColor = calculateColor(value);
+            if (!Float.isNaN(vgPath.mFillOpacity)) {
+                vgPath.mFillColor &= 0x00FFFFFF;
+                vgPath.mFillColor |= ((int) (0xFF * vgPath.mFillOpacity)) << 24;
+            }
+        } else if (PATH_STROKE.equals(name)) {
+            vgPath.mStrokeColor = calculateColor(value);
+            if (!Float.isNaN(vgPath.mStrokeOpacity)) {
+                vgPath.mStrokeColor &= 0x00FFFFFF;
+                vgPath.mStrokeColor |= ((int) (0xFF * vgPath.mStrokeOpacity)) << 24;
+            }
+        } else if (PATH_FILL_OPACTIY.equals(name)) {
+            vgPath.mFillOpacity = Float.parseFloat(value);
+            vgPath.mFillColor &= 0x00FFFFFF;
+            vgPath.mFillColor |= ((int) (0xFF * vgPath.mFillOpacity)) << 24;
+        } else if (PATH_STROKE_OPACTIY.equals(name)) {
+            vgPath.mStrokeOpacity = Float.parseFloat(value);
+            vgPath.mStrokeColor &= 0x00FFFFFF;
+            vgPath.mStrokeColor |= ((int) (0xFF * vgPath.mStrokeOpacity)) << 24;
+        } else if (PATH_STROKE_WIDTH.equals(name)) {
+            vgPath.mStrokeWidth = Float.parseFloat(value);
+        } else if (PATH_ROTATION.equals(name)) {
+            vgPath.mRotate = Float.parseFloat(value);
+        } else if (PATH_SHIFT_X.equals(name)) {
+            vgPath.mShiftX = Float.parseFloat(value);
+        } else if (PATH_SHIFT_Y.equals(name)) {
+            vgPath.mShiftY = Float.parseFloat(value);
+        } else if (PATH_ROTATION_Y.equals(name)) {
+            vgPath.mRotateY = Float.parseFloat(value);
+        } else if (PATH_ROTATION_X.equals(name)) {
+            vgPath.mRotateX = Float.parseFloat(value);
+        } else if (PATH_CLIP.equals(name)) {
+
+            vgPath.mClip = Boolean.parseBoolean(value);
+
+        } else if (PATH_TRIM_START.equals(name)) {
+            vgPath.mTrimPathStart = Float.parseFloat(value);
+        } else if (PATH_TRIM_END.equals(name)) {
+            vgPath.mTrimPathEnd = Float.parseFloat(value);
+        } else if (PATH_TRIM_OFFSET.equals(name)) {
+            vgPath.mTrimPathOffset = Float.parseFloat(value);
+        } else if (PATH_STROKE_LINECAP.equals(name)) {
+            if (LINECAP_BUTT.equals(value)) {
+                vgPath.mStrokeLineCap = 0;
+            } else if (LINECAP_ROUND.equals(value)) {
+                vgPath.mStrokeLineCap = 1;
+            } else if (LINECAP_SQUARE.equals(value)) {
+                vgPath.mStrokeLineCap = 2;
+            }
+        } else if (PATH_STROKE_LINEJOIN.equals(name)) {
+            if (LINEJOIN_MITER.equals(value)) {
+                vgPath.mStrokeLineJoin = 0;
+            } else if (LINEJOIN_ROUND.equals(value)) {
+                vgPath.mStrokeLineJoin = 1;
+            } else if (LINEJOIN_BEVEL.equals(value)) {
+                vgPath.mStrokeLineJoin = 2;
+            }
+        } else if (PATH_STROKE_MITERLIMIT.equals(name)) {
+            vgPath.mStrokeMiterlimit = Float.parseFloat(value);
+        } else {
+            logger.log(Level.FINE, ">>>>>> DID NOT UNDERSTAND ! \"" + name + "\" <<<<");
+        }
+
+    }
+
+    private int calculateColor(String value) {
+        int len = value.length();
+        int ret;
+        int k = 0;
+        switch (len) {
+            case 7: // #RRGGBB
+                ret = (int) Long.parseLong(value.substring(1), 16);
+                ret |= 0xFF000000;
+                break;
+            case 9: // #AARRGGBB
+                ret = (int) Long.parseLong(value.substring(1), 16);
+                break;
+            case 4: // #RGB
+                ret = (int) Long.parseLong(value.substring(1), 16);
+
+                k |= ((ret >> 8) & 0xF) * 0x110000;
+                k |= ((ret >> 4) & 0xF) * 0x1100;
+                k |= ((ret) & 0xF) * 0x11;
+                ret = k | 0xFF000000;
+                break;
+            case 5: // #ARGB
+                ret = (int) Long.parseLong(value.substring(1), 16);
+                k |= ((ret >> 16) & 0xF) * 0x11000000;
+                k |= ((ret >> 8) & 0xF) * 0x110000;
+                k |= ((ret >> 4) & 0xF) * 0x1100;
+                k |= ((ret) & 0xF) * 0x11;
+                break;
+            default:
+                return 0xFF000000;
+        }
+
+        logger.log(Level.FINE, "color = " + value + " = " + Integer.toHexString(ret));
+        return ret;
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPath.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPath.java
new file mode 100644
index 0000000..6315fff
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPath.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import java.awt.geom.Path2D;
+import java.util.Arrays;
+
+/**
+ * Used to represent one VectorDrawble's path element.
+ */
+class VdPath extends VdElement{
+    Node[] mNode = null;
+    int mStrokeColor = 0;
+    int mFillColor = 0;
+    float mStrokeWidth = 0;
+    float mRotate = 0;
+    float mShiftX = 0;
+    float mShiftY = 0;
+    float mRotateX = 0;
+    float mRotateY = 0;
+    float trimPathStart = 0;
+    float trimPathEnd = 1;
+    float trimPathOffset = 0;
+    int mStrokeLineCap = -1;
+    int mStrokeLineJoin = -1;
+    float mStrokeMiterlimit = -1;
+    boolean mClip = false;
+    float mStrokeOpacity = Float.NaN;
+    float mFillOpacity = Float.NaN;
+    float mTrimPathStart = 0;
+    float mTrimPathEnd = 1;
+    float mTrimPathOffset = 0;
+
+    public void toPath(Path2D path) {
+        path.reset();
+        if (mNode != null) {
+            VdNodeRender.creatPath(mNode, path);
+        }
+    }
+
+    public static class Node {
+        char type;
+        float[] params;
+
+        public Node(char type, float[] params) {
+            this.type = type;
+            this.params = params;
+        }
+
+        public Node(Node n) {
+            this.type = n.type;
+            this.params = Arrays.copyOf(n.params, n.params.length);
+        }
+
+        public static String NodeListToString(Node[] nodes) {
+            String s = "";
+            for (int i = 0; i < nodes.length; i++) {
+                Node n = nodes[i];
+                s += n.type;
+                int len = n.params.length;
+                for (int j = 0; j < len; j++) {
+                    if (j > 0) {
+                        s += ((j & 1) == 1) ? "," : " ";
+                    }
+                    // To avoid trailing zeros like 17.0, use this trick
+                    float value = n.params[j];
+                    if (value == (long) value) {
+                        s += String.valueOf((long) value);
+                    } else {
+                        s += String.valueOf(value);
+                    }
+
+                }
+            }
+            return s;
+        }
+
+        public static void transform(float a,
+                float b,
+                float c,
+                float d,
+                float e,
+                float f,
+                Node[] nodes) {
+            float[] pre = new float[2];
+            for (int i = 0; i < nodes.length; i++) {
+                nodes[i].transform(a, b, c, d, e, f, pre);
+            }
+        }
+
+        public void transform(float a,
+                float b,
+                float c,
+                float d,
+                float e,
+                float f,
+                float[] pre) {
+            int incr = 0;
+            float[] tempParams;
+            float[] origParams;
+            switch (type) {
+
+                case 'z':
+                case 'Z':
+                    return;
+                case 'M':
+                case 'L':
+                case 'T':
+                    incr = 2;
+                    pre[0] = params[params.length - 2];
+                    pre[1] = params[params.length - 1];
+                    for (int i = 0; i < params.length; i += incr) {
+                        matrix(a, b, c, d, e, f, i, i + 1);
+                    }
+                    break;
+                case 'm':
+                case 'l':
+                case 't':
+                    incr = 2;
+                    pre[0] += params[params.length - 2];
+                    pre[1] += params[params.length - 1];
+                    for (int i = 0; i < params.length; i += incr) {
+                        matrix(a, b, c, d, 0, 0, i, i + 1);
+                    }
+                    break;
+                case 'h':
+                    type = 'l';
+                    pre[0] += params[params.length - 1];
+
+                    tempParams = new float[params.length * 2];
+                    origParams = params;
+                    params = tempParams;
+                    for (int i = 0; i < params.length; i += 2) {
+                        params[i] = origParams[i / 2];
+                        params[i + 1] = 0;
+                        matrix(a, b, c, d, 0, 0, i, i + 1);
+                    }
+
+                    break;
+                case 'H':
+                    type = 'L';
+                    pre[0] = params[params.length - 1];
+                    tempParams = new float[params.length * 2];
+                    origParams = params;
+                    params = tempParams;
+                    for (int i = 0; i < params.length; i += 2) {
+                        params[i] = origParams[i / 2];
+                        params[i + 1] = pre[1];
+                        matrix(a, b, c, d, e, f, i, i + 1);
+                    }
+                    break;
+                case 'v':
+                    pre[1] += params[params.length - 1];
+                    type = 'l';
+                    tempParams = new float[params.length * 2];
+                    origParams = params;
+                    params = tempParams;
+                    for (int i = 0; i < params.length; i += 2) {
+                        params[i] = 0;
+                        params[i + 1] = origParams[i / 2];
+                        matrix(a, b, c, d, 0, 0, i, i + 1);
+                    }
+                    break;
+                case 'V':
+                    type = 'L';
+                    pre[1] = params[params.length - 1];
+                    tempParams = new float[params.length * 2];
+                    origParams = params;
+                    params = tempParams;
+                    for (int i = 0; i < params.length; i += 2) {
+                        params[i] = pre[0];
+                        params[i + 1] = origParams[i / 2];
+                        matrix(a, b, c, d, e, f, i, i + 1);
+                    }
+                    break;
+                case 'C':
+                case 'S':
+                case 'Q':
+                    pre[0] = params[params.length - 2];
+                    pre[1] = params[params.length - 1];
+                    for (int i = 0; i < params.length; i += 2) {
+                        matrix(a, b, c, d, e, f, i, i + 1);
+                    }
+                    break;
+                case 's':
+                case 'q':
+                case 'c':
+                    pre[0] += params[params.length - 2];
+                    pre[1] += params[params.length - 1];
+                    for (int i = 0; i < params.length; i += 2) {
+                        matrix(a, b, c, d, 0, 0, i, i + 1);
+                    }
+                    break;
+                case 'a':
+                    incr = 7;
+                    pre[0] += params[params.length - 2];
+                    pre[1] += params[params.length - 1];
+                    for (int i = 0; i < params.length; i += incr) {
+                        matrix(a, b, c, d, 0, 0, i, i + 1);
+                        double ang = Math.toRadians(params[i + 2]);
+                        params[i + 2] = (float) Math.toDegrees(ang + Math.atan2(b, d));
+                        matrix(a, b, c, d, 0, 0, i + 5, i + 6);
+                    }
+                    break;
+                case 'A':
+                    incr = 7;
+                    pre[0] = params[params.length - 2];
+                    pre[1] = params[params.length - 1];
+                    for (int i = 0; i < params.length; i += incr) {
+                        matrix(a, b, c, d, e, f, i, i + 1);
+                        double ang = Math.toRadians(params[i + 2]);
+                        params[i + 2] = (float) Math.toDegrees(ang + Math.atan2(b, d));
+                        matrix(a, b, c, d, e, f, i + 5, i + 6);
+                    }
+                    break;
+
+            }
+        }
+
+        void matrix(float a,
+                float b,
+                float c,
+                float d,
+                float e,
+                float f,
+                int offx,
+                int offy) {
+            float inx = (offx < 0) ? 1 : params[offx];
+            float iny = (offy < 0) ? 1 : params[offy];
+            float x = inx * a + iny * c + e;
+            float y = inx * b + iny * d + f;
+            if (offx >= 0) {
+                params[offx] = x;
+            }
+            if (offy >= 0) {
+                params[offy] = y;
+            }
+        }
+    }
+
+    public VdPath() {
+        mName = this.toString(); // to ensure paths have unique names
+    }
+
+    /**
+     * TODO: support rotation attribute for stroke width
+     */
+    public void transform(float a, float b, float c, float d, float e, float f) {
+        mStrokeWidth *= Math.hypot(a + b, c + d);
+        Node.transform(a, b, c, d, e, f, mNode);
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java
new file mode 100644
index 0000000..d5721a9
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.util.AssetUtil;
+import com.google.common.base.Charsets;
+import com.sun.org.apache.xml.internal.serialize.OutputFormat;
+import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.awt.image.BufferedImage;
+import java.io.*;
+
+/**
+ * Generate a Image based on the VectorDrawable's XML content.
+ *
+ * <p>This class also contains a main method, which can be used to preview a vector drawable file.
+ */
+public class VdPreview {
+
+    private static final String ANDROID_ALPHA = "android:alpha";
+    private static final String ANDROID_AUTO_MIRRORED = "android:autoMirrored";
+    private static final String ANDROID_HEIGHT = "android:height";
+    private static final String ANDROID_WIDTH = "android:width";
+    public static final int MAX_PREVIEW_IMAGE_SIZE = 4096;
+    public static final int MIN_PREVIEW_IMAGE_SIZE = 1;
+
+    /**
+     * This encapsulates the information used to determine the preview image size.
+     * The reason we have different ways here is that both Studio UI and build process need
+     * to use this common code path to generate images for vectordrawable.
+     * When {@value mUseWidth} is true, use {@code mImageMaxDimension} as the maximum
+     * dimension value while keeping the aspect ratio.
+     * Otherwise, use {@code mImageScale} to scale the image based on the XML's size information.
+     */
+    public static class TargetSize {
+        private boolean mUseWidth;
+
+        private int mImageMaxDimension;
+        private float mImageScale;
+
+        private TargetSize(boolean useWidth, int imageWidth, float imageScale) {
+            mUseWidth = useWidth;
+            mImageMaxDimension = imageWidth;
+            mImageScale = imageScale;
+        }
+
+        public static TargetSize createSizeFromWidth(int imageWidth) {
+            return new TargetSize(true, imageWidth, 0.0f);
+        }
+
+        public static TargetSize createSizeFromScale(float imageScale) {
+            return new TargetSize(false, 0, imageScale);
+        }
+    }
+
+    /**
+     * Since we allow overriding the vector drawable's size, we also need to keep
+     * the original size and aspect ratio.
+     */
+    public static class SourceSize {
+        public int getHeight() {
+            return mSourceHeight;
+        }
+
+        public int getWidth() {
+            return mSourceWidth;
+        }
+
+        private int mSourceWidth;
+        private int mSourceHeight;
+    }
+
+
+    /**
+     * @return a format object for XML formatting.
+     */
+    @NonNull
+    private static OutputFormat getPrettyPrintFormat() {
+        OutputFormat format = new OutputFormat();
+        format.setLineWidth(120);
+        format.setIndenting(true);
+        format.setIndent(4);
+        format.setEncoding("UTF-8");
+        format.setOmitComments(true);
+        return format;
+    }
+
+    /**
+     * Get the vector drawable's original size.
+     */
+    public static SourceSize getVdOriginalSize(@NonNull Document document) {
+        Element root = document.getDocumentElement();
+        SourceSize srcSize = new SourceSize();
+        // Update attributes, note that attributes as width and height are required,
+        // while others are optional.
+        NamedNodeMap attr = root.getAttributes();
+        Node nodeAttr = attr.getNamedItem(ANDROID_WIDTH);
+        assert nodeAttr != null;
+        srcSize.mSourceWidth = parseDimension(0, nodeAttr, false);
+
+        nodeAttr = attr.getNamedItem(ANDROID_HEIGHT);
+        assert nodeAttr != null;
+        srcSize.mSourceHeight = parseDimension(0, nodeAttr, false);
+        return srcSize;
+    }
+
+    /**
+     * The UI can override some properties of the Vector drawable.
+     * In order to override in an uniform way, we re-parse the XML file
+     * and pick the appropriate attributes to override.
+     *
+     * @param document the parsed document of original VectorDrawable's XML file.
+     * @param info incoming override information for VectorDrawable.
+     * @param errorLog log for the parsing errors and warnings.
+     * @param srcSize as an output, store the original size of the VectorDrawable
+     * @return the overridden XML file in one string. If exception happens
+     * or no attributes needs to be overriden, return null.
+     */
+    @Nullable
+    public static String overrideXmlContent(@NonNull Document document,
+                                            @NonNull VdOverrideInfo info,
+                                            @Nullable StringBuilder errorLog) {
+        boolean isXmlFileContentChanged = false;
+        Element root = document.getDocumentElement();
+
+        // Update attributes, note that attributes as width and height are required,
+        // while others are optional.
+        NamedNodeMap attr = root.getAttributes();
+        if (info.needsOverrideWidth()) {
+            Node nodeAttr = attr.getNamedItem(ANDROID_WIDTH);
+            int overrideValue = info.getWidth();
+            int originalValue = parseDimension(overrideValue, nodeAttr, true);
+            if (originalValue != overrideValue) {
+                isXmlFileContentChanged = true;
+            }
+        }
+        if (info.needsOverrideHeight()) {
+            Node nodeAttr = attr.getNamedItem(ANDROID_HEIGHT);
+            int overrideValue = info.getHeight();
+            int originalValue = parseDimension(overrideValue, nodeAttr, true);
+            if (originalValue != overrideValue) {
+                isXmlFileContentChanged = true;
+            }
+        }
+        if (info.needsOverrideOpacity()) {
+            Node nodeAttr = attr.getNamedItem(ANDROID_ALPHA);
+            String opacityValue = String.format("%.2f", info.getOpacity() / 100.0f);
+            if (nodeAttr != null) {
+                nodeAttr.setTextContent(opacityValue);
+            }
+            else {
+                root.setAttribute(ANDROID_ALPHA, opacityValue);
+            }
+            isXmlFileContentChanged = true;
+        }
+        // When auto mirror is set to true, then we always need to set it.
+        // Because SVG has no such attribute at all.
+        if (info.needsOverrideAutoMirrored()) {
+            Node nodeAttr = attr.getNamedItem(ANDROID_AUTO_MIRRORED);
+            if (nodeAttr != null) {
+                nodeAttr.setTextContent("true");
+            }
+            else {
+                root.setAttribute(ANDROID_AUTO_MIRRORED, "true");
+            }
+            isXmlFileContentChanged = true;
+        }
+
+        if (isXmlFileContentChanged) {
+            // Prettify the XML string from the document.
+            StringWriter stringOut = new StringWriter();
+            XMLSerializer serial = new XMLSerializer(stringOut, getPrettyPrintFormat());
+            try {
+                serial.serialize(document);
+            }
+            catch (IOException e) {
+                if (errorLog != null) {
+                    errorLog.append("Exception while parsing XML file:\n").append(e.getMessage());
+                }
+            }
+            return stringOut.toString();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Query the dimension info and override it if needed.
+     *
+     * @param overrideValue the dimension value to override with.
+     * @param nodeAttr the node who contains dimension info.
+     * @param override if true then override the dimension.
+     * @return the original dimension value.
+     */
+    private static int parseDimension(int overrideValue, Node nodeAttr, boolean override) {
+        assert nodeAttr != null;
+        String content = nodeAttr.getTextContent();
+        assert content.endsWith("dp");
+        int originalValue = Integer.parseInt(content.substring(0, content.length() - 2));
+
+        if (override) {
+            nodeAttr.setTextContent(overrideValue + "dp");
+        }
+        return originalValue;
+    }
+
+    /**
+     * This generates an image according to the VectorDrawable's content {@code xmlFileContent}.
+     * At the same time, {@vdErrorLog} captures all the errors found during parsing.
+     * The size of image is determined by the {@code size}.
+     *
+     * @param targetSize the size of result image.
+     * @param xmlFileContent  VectorDrawable's XML file's content.
+     * @param vdErrorLog      log for the parsing errors and warnings.
+     * @return an preview image according to the VectorDrawable's XML
+     */
+    @Nullable
+    public static BufferedImage getPreviewFromVectorXml(@NonNull TargetSize targetSize,
+                                                        @Nullable String xmlFileContent,
+                                                        @Nullable StringBuilder vdErrorLog) {
+        if (xmlFileContent == null || xmlFileContent.isEmpty()) {
+            return null;
+        }
+        VdParser p = new VdParser();
+        VdTree vdTree;
+
+        InputStream inputStream = new ByteArrayInputStream(
+                xmlFileContent.getBytes(Charsets.UTF_8));
+        vdTree = p.parse(inputStream, vdErrorLog);
+        if (vdTree == null) {
+            return null;
+        }
+
+        // If the forceImageSize is set (>0), then we honor that.
+        // Otherwise, we will ask the vectorDrawable for the prefer size, then apply the imageScale.
+        float vdWidth = vdTree.getBaseWidth();
+        float vdHeight = vdTree.getBaseHeight();
+        float imageWidth;
+        float imageHeight;
+        int forceImageSize = targetSize.mImageMaxDimension;
+        float imageScale = targetSize.mImageScale;
+
+        if (forceImageSize > 0) {
+            // The goal here is to generate an image within certain size, while keeping the
+            // aspect ration as much as we can.
+            // If it is scaling too much to fit in, we log an error.
+            float maxVdSize = Math.max(vdWidth, vdHeight);
+            float ratioToForceImageSize = forceImageSize / maxVdSize;
+            float scaledWidth = ratioToForceImageSize * vdWidth;
+            float scaledHeight = ratioToForceImageSize * vdHeight;
+            imageWidth = Math.max(MIN_PREVIEW_IMAGE_SIZE, Math.min(MAX_PREVIEW_IMAGE_SIZE, scaledWidth));
+            imageHeight = Math.max(MIN_PREVIEW_IMAGE_SIZE, Math.min(MAX_PREVIEW_IMAGE_SIZE, scaledHeight));
+            if (scaledWidth != imageWidth || scaledHeight != imageHeight) {
+                vdErrorLog.append("Invalid image size, can't fit in a square whose size is" + forceImageSize);
+            }
+        } else {
+            imageWidth = vdWidth * imageScale;
+            imageHeight = vdHeight * imageScale;
+        }
+
+        // Create the image according to the vectorDrawable's aspect ratio.
+
+        BufferedImage image = AssetUtil.newArgbBufferedImage((int)imageWidth, (int)imageHeight);
+        vdTree.drawIntoImage(image);
+        return image;
+    }
+
+    public static void main(String[] args) {
+        System.out.println("Hello from sdk-common-lib.");
+    }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdTree.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdTree.java
new file mode 100644
index 0000000..883b821
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdTree.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.ide.common.util.AssetUtil;
+
+import java.awt.*;
+import java.awt.geom.Path2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Used to represent the whole VectorDrawable XML file's tree.
+ */
+class VdTree {
+    private static Logger logger = Logger.getLogger(VdTree.class.getSimpleName());
+
+    VdGroup mCurrentGroup = new VdGroup();
+    ArrayList<VdElement> mChildren;
+
+    float mBaseWidth = 1;
+    float mBaseHeight = 1;
+    float mPortWidth = 1;
+    float mPortHeight = 1;
+    float mRootAlpha = 1;
+
+    /**
+     * Ensure there is at least one animation for every path in group (linking
+     * them by names) Build the "current" path based on the first group
+     */
+    void parseFinish() {
+        mChildren = mCurrentGroup.getChildren();
+    }
+
+    void add(VdElement pathOrGroup) {
+        mCurrentGroup.add(pathOrGroup);
+    }
+
+    float getBaseWidth(){
+        return mBaseWidth;
+    }
+
+    float getBaseHeight(){
+        return mBaseHeight;
+    }
+
+    private void drawInternal(Graphics g, int w, int h) {
+        float scaleX = w / mPortWidth;
+        float scaleY = h / mPortHeight;
+        float minScale = Math.min(scaleX, scaleY);
+
+        if (mChildren == null) {
+            logger.log(Level.FINE, "no pathes");
+            return;
+        }
+        ((Graphics2D) g).scale(scaleX, scaleY);
+
+        Rectangle bounds = null;
+        for (int i = 0; i < mChildren.size(); i++) {
+            // TODO: do things differently when it is a path or group!!
+            VdPath path = (VdPath) mChildren.get(i);
+            logger.log(Level.FINE, "mCurrentPaths[" + i + "]=" + path.getName() +
+                                   Integer.toHexString(path.mFillColor));
+            if (mChildren.get(i) != null) {
+                Rectangle r = drawPath(path, g, w, h, minScale);
+                if (bounds == null) {
+                    bounds = r;
+                } else {
+                    bounds.add(r);
+                }
+            }
+        }
+        logger.log(Level.FINE, "Rectangle " + bounds);
+        logger.log(Level.FINE, "Port  " + mPortWidth + "," + mPortHeight);
+        double right = mPortWidth - bounds.getMaxX();
+        double bot = mPortHeight - bounds.getMaxY();
+        logger.log(Level.FINE, "x " + bounds.getMinX() + ", " + right);
+        logger.log(Level.FINE, "y " + bounds.getMinY() + ", " + bot);
+    }
+
+    private Rectangle drawPath(VdPath path, Graphics canvas, int w, int h, float scale) {
+
+        Path2D path2d = new Path2D.Double();
+        Graphics2D g = (Graphics2D) canvas;
+        path.toPath(path2d);
+
+        // TODO: Use AffineTransform to apply group's transformation info.
+        double theta = Math.toRadians(path.mRotate);
+        g.rotate(theta, path.mRotateX, path.mRotateY);
+        if (path.mClip) {
+            logger.log(Level.FINE, "CLIP");
+
+            g.setColor(Color.RED);
+            g.fill(path2d);
+
+        }
+        if (path.mFillColor != 0) {
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g.setColor(new Color(path.mFillColor, true));
+            g.fill(path2d);
+        }
+        if (path.mStrokeColor != 0) {
+            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g.setStroke(new BasicStroke(path.mStrokeWidth));
+            g.setColor(new Color(path.mStrokeColor, true));
+            g.draw(path2d);
+        }
+
+        g.rotate(-theta, path.mRotateX, path.mRotateY);
+        return path2d.getBounds();
+    }
+
+    /**
+     * Draw the VdTree into an image.
+     * If the root alpha is less than 1.0, then draw into a temporary image,
+     * then draw into the result image applying alpha blending.
+     */
+    public void drawIntoImage(BufferedImage image) {
+        Graphics2D gFinal = (Graphics2D) image.getGraphics();
+        int width = image.getWidth();
+        int height = image.getHeight();
+        gFinal.setColor(new Color(255, 255, 255, 0));
+        gFinal.fillRect(0, 0, width, height);
+
+        float rootAlpha = mRootAlpha;
+        if (rootAlpha < 1.0) {
+            BufferedImage alphaImage = AssetUtil.newArgbBufferedImage(width, height);
+            Graphics2D gTemp = (Graphics2D)alphaImage.getGraphics();
+            drawInternal(gTemp, width, height);
+            gFinal.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, rootAlpha));
+            gFinal.drawImage(alphaImage, 0, 0, null);
+            gTemp.dispose();
+        } else {
+            drawInternal(gFinal, width, height);
+        }
+        gFinal.dispose();
+    }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java b/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java
index ea34226..257cdfe 100644
--- a/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java
@@ -531,7 +531,7 @@
                 if (attributeName.equals(attributes.getLocalName(i)) &&
                         ((hasNamespace &&
                                 SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
-                                (hasNamespace == false && attributes.getURI(i).length() == 0))) {
+                                (hasNamespace == false && attributes.getURI(i).isEmpty()))) {
                     return attributes.getValue(i);
                 }
             }
@@ -555,7 +555,7 @@
                 if (attributeName.equals(attributes.getLocalName(i)) &&
                         ((hasNamespace &&
                                 SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
-                                (hasNamespace == false && attributes.getURI(i).length() == 0))) {
+                                (hasNamespace == false && attributes.getURI(i).isEmpty()))) {
                     String attr = attributes.getValue(i);
                     if (attr != null) {
                         return Boolean.valueOf(attr);
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/MessageJsonSerializerTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/MessageJsonSerializerTest.java
new file mode 100644
index 0000000..fdb803e
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/MessageJsonSerializerTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class MessageJsonSerializerTest {
+
+    private static GsonBuilder sGsonBuilder = new GsonBuilder();
+
+    static {
+        MessageJsonSerializer.registerTypeAdapters(sGsonBuilder);
+    }
+
+    @RunWith(Parameterized.class)
+    public static class DeserializeTest {
+
+        private static Gson sGson;
+
+        @Parameterized.Parameter(value = 0)
+        public Message message;
+
+        @Parameterized.Parameter(value = 1)
+        public String serializedMessage;
+
+        @Parameterized.Parameters(name = "fromJson(\"{1}\") should give {0}")
+        public static Collection<Object[]> data() {
+            return Arrays.asList(new Object[][]{
+                    {new Message(
+                                    Message.Kind.ERROR,
+                                    "some error text",
+                                    new SourceFilePosition(
+                                            new SourceFile(new File("/path/file.java")),
+                                            new SourcePosition(1, 3, 5))),
+                            "{"
+                                    + "\"kind\":\"Error\", "
+                                    + "\"text\":\"some error text\","
+                                    + "\"sources\": {"
+                                    + "\"file\":\"/path/file.java\","
+                                    + "\"position\":{"
+                                    + "\"startLine\":1,"
+                                    + "\"startColumn\":3,"
+                                    + "\"startOffset\":5"
+                                    + "}"
+                                    + "}}"
+                    }, {
+                            new Message(
+                                    Message.Kind.ERROR,
+                                    "errorText",
+                                    new SourceFilePosition(
+                                            new File("error/source"),
+                                            new SourcePosition(1,2,3,4,5,6))
+                            ),
+                            "{\"kind\":\"ERROR\",\"text\":\"errorText\",\"sourcePath\":\"error/source\","
+                            + "\"position\":{\"startLine\":1,\"startColumn\":2,\"startOffset\":3,"
+                            + "\"endLine\":4,\"endColumn\":5,\"endOffset\":6},\"original\":\"\"}\n"
+            }, {new Message(Message.Kind.SIMPLE, "something else", new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN)),
+                    "{\"kind\":\"SIMPLE\","
+                            + "\"text\":\"something else\",\"position\":{},\"original\":\"something else\"}"
+            }, {
+                    new Message(
+                            Message.Kind.SIMPLE,
+                            "Warning: AndroidManifest.xml already defines debuggable (in http://"
+                                    + "schemas.android.com/apk/res/android); using existing value "
+                                    + "in manifest.",
+                            SourceFilePosition.UNKNOWN),
+                    "{\"kind\":\"simple\",\"text\":\"Warning: AndroidManifest.xml already defines "
+                            + "debuggable (in http://schemas.android.com/apk/res/android); using "
+                            + "existing value in manifest.\",\"sources\":\"\"}"
+            }, {
+                    new Message(
+                            Message.Kind.UNKNOWN,
+                            "Text.",
+                            SourceFilePosition.UNKNOWN),
+                    "{\"text\":\"Text.\"}",
+
+            }});
+        }
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = sGsonBuilder.create();
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+
+        @Test
+        public void check() {
+            assertEquals(message, sGson.fromJson(serializedMessage, Message.class));
+        }
+
+
+    }
+
+    @RunWith(Parameterized.class)
+    public static class RoundTripTest {
+
+        private static Gson sGson;
+
+        @Parameterized.Parameter
+        public Message message;
+
+
+        @Parameterized.Parameters(name = "{0}")
+        public static Collection<Object[]> data() {
+            return Arrays.asList(new Object[][]{{
+                    new Message(
+                            Message.Kind.ERROR,
+                            "some error text",
+                            "original error text",
+                            new SourceFilePosition(
+                                    new SourceFile(new File("/path/file.java")),
+                                    new SourcePosition(1, 3, 5)))
+                    }, {
+                    new Message(
+                            Message.Kind.SIMPLE,
+                            "something else",
+                            new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN))
+                    }, {
+                    new Message(
+                            Message.Kind.SIMPLE,
+                            "Warning: AndroidManifest.xml already defines debuggable (in http://"
+                                    + "schemas.android.com/apk/res/android); using existing value "
+                                    + "in manifest.",
+                            SourceFilePosition.UNKNOWN)
+            }});
+        }
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = sGsonBuilder.create();
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+
+        @Test
+        public void check() {
+            assertEquals(message, sGson.fromJson(sGson.toJson(message), Message.class));
+        }
+
+
+    }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/ParsingProcessOutputHandlerTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/ParsingProcessOutputHandlerTest.java
new file mode 100644
index 0000000..c065001
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/ParsingProcessOutputHandlerTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.blame.parser.ParsingFailedException;
+import com.android.ide.common.blame.parser.PatternAwareOutputParser;
+import com.android.ide.common.blame.parser.ToolOutputParser;
+import com.android.ide.common.blame.parser.util.OutputLineReader;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.ide.common.res2.RecordingLogger;
+import com.android.utils.ILogger;
+import com.google.common.base.Charsets;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class ParsingProcessOutputHandlerTest {
+
+    private static ParsingProcessOutputHandler sParsingProcessOutputHandler;
+
+    private static RecordingLogger sLogger;
+
+    private static PatternAwareOutputParser sFakePatternParser;
+
+    private static MessageReceiver sMessageReceiver;
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        sLogger = new RecordingLogger();
+        sFakePatternParser = new FakePatternAwareOutputParser();
+
+        sMessageReceiver = Mockito.mock(MessageReceiver.class);
+        sParsingProcessOutputHandler = new ParsingProcessOutputHandler(
+                new ToolOutputParser(sFakePatternParser, sLogger),
+                sMessageReceiver);
+    }
+
+    @Test
+    public void testRewriteMessages() throws IOException, ProcessException {
+
+        String original = "error example\ntwo line error\nnext line\nsomething else";
+
+        sParsingProcessOutputHandler.handleOutput(processOutputFromErrorString(original));
+
+        Mockito.verify(sMessageReceiver).receiveMessage(new Message(
+                Message.Kind.ERROR,
+                "errorText",
+                "originalText",
+                new SourceFilePosition(
+                        new SourceFile(FakePatternAwareOutputParser.ERROR_EXAMPLE_FILE),
+                        new SourcePosition(1, 2, 3, 4, 5, 6)
+                )));
+
+        Mockito.verify(sMessageReceiver).receiveMessage(new Message(
+                Message.Kind.WARNING,
+                "two line warning",
+                new SourceFilePosition(
+                        new SourceFile(FakePatternAwareOutputParser.TWO_LINE_ERROR_FILE),
+                        new SourcePosition(1, 2, -1)
+                )));
+
+
+        Mockito.verify(sMessageReceiver).receiveMessage(new Message(
+                Message.Kind.SIMPLE,
+                "something else",
+                SourceFilePosition.UNKNOWN));
+        Mockito.verifyNoMoreInteractions(sMessageReceiver);
+
+        String expected = "AGPBI: {"
+                + "\"kind\":\"error\","
+                + "\"text\":\"errorText\","
+                + "\"sources\":[{"
+                + "\"file\":\"" + FakePatternAwareOutputParser.ERROR_EXAMPLE_FILE.getAbsolutePath()
+                + "\",\"position\":{\"startLine\":1,\"startColumn\":2,\"startOffset\":3,"
+                + "\"endLine\":4,\"endColumn\":5,\"endOffset\":6}}],"
+                + "\"original\":\"original_text\"}\n"
+                + "AGPBI: {\"kind\":\"warning\","
+                + "\"text\":\"two line warning\","
+                + "\"sources\":[{\"file\":\"" +
+                FakePatternAwareOutputParser.TWO_LINE_ERROR_FILE.getAbsolutePath() + "\","
+                + "\"position\":{\"startLine\":1,\"startColumn\":2}}]}\n"
+                + "AGPBI: {\"kind\":\"simple\","
+                + "\"text\":\"something else\","
+                + "\"sources\":[{}]}";
+    }
+
+    @Test
+    public void parseException() throws IOException, ProcessException {
+        String original = "two line error";
+
+        sParsingProcessOutputHandler.handleOutput(processOutputFromErrorString(original));
+
+        Mockito.verifyNoMoreInteractions(sMessageReceiver);
+    }
+
+    private static ProcessOutput processOutputFromErrorString(String original) throws IOException {
+        ProcessOutput processOutput = sParsingProcessOutputHandler.createOutput();
+        processOutput.getErrorOutput().write(original.getBytes(Charsets.UTF_8));
+        return processOutput;
+    }
+
+    private static class FakePatternAwareOutputParser implements PatternAwareOutputParser {
+        static final File ERROR_EXAMPLE_FILE = new File("error/source");
+        static final File TWO_LINE_ERROR_FILE = new File("error/source/2");
+        @Override
+        public boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
+                @NonNull List<Message> messages, @NonNull ILogger logger)
+                throws ParsingFailedException {
+            if (line.equals("two line error")) {
+                String nextLine = reader.readLine();
+                if ("next line".equals(nextLine)) {
+                    messages.add(new Message(
+                            Message.Kind.WARNING,
+                            "two line warning",
+                            "two line warning",
+                            new SourceFilePosition(
+                                    TWO_LINE_ERROR_FILE,
+                                    new SourcePosition(1, 2, -1))));
+                } else {
+                    throw new ParsingFailedException();
+                }
+                return true;
+            }
+            if (line.equals("error example")) {
+                messages.add(
+                        new Message(
+                                Message.Kind.ERROR,
+                                "errorText",
+                                "original_text",
+                                new SourceFilePosition(
+                                        ERROR_EXAMPLE_FILE,
+                                        new SourcePosition(1, 2, 3, 4, 5, 6))));
+                return true;
+            }
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/SourceFileJsonTypeAdapterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFileJsonTypeAdapterTest.java
new file mode 100644
index 0000000..4f79e2a
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFileJsonTypeAdapterTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class SourceFileJsonTypeAdapterTest {
+
+    private static GsonBuilder sGsonBuilder = new GsonBuilder()
+            .registerTypeAdapter(SourceFile.class, new SourceFileJsonTypeAdapter());
+
+    @RunWith(Parameterized.class)
+    public static class RoundTripTest {
+
+        @Parameterized.Parameters(name = "{0}")
+        public static Collection<Object[]> data() {
+            return Arrays.asList(new Object[][]{
+                    {new SourceFile(new File("/path/to/a/file.java"))},
+                    {new SourceFile(new File("/path/to/a/file.java"), "Description")},
+                    {new SourceFile("Description")},
+                    {SourceFile.UNKNOWN},
+            });
+        }
+
+        @Parameterized.Parameter
+        public SourceFile mSourceFile;
+
+        private static Gson sGson;
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = sGsonBuilder.create();
+        }
+
+        @Test
+        public void test() {
+            assertEquals(mSourceFile, sGson.fromJson(sGson.toJson(mSourceFile), SourceFile.class));
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class DeserializeTest {
+
+        @Parameterized.Parameters(name = "fromJson(\"{1}\") should be {0}")
+        public static Collection<Object[]> data() {
+            return Arrays.asList(new Object[][]{
+                    {
+                            new SourceFile(new File("/path/file.java")),
+                            "\"/path/file.java\""
+                    },
+                    {
+                            new SourceFile(new File("/path/f.java")),
+                            "{\"path\":\"/path/f.java\"}"
+                    },
+                    {
+                            new SourceFile(new File("/path/file.java"), "Description"),
+                            "{\"description\":\"Description\", \"path\":\"/path/file.java\"}"
+                    },
+                    {
+                            new SourceFile("Description"),
+                            "{\"description\":\"Description\"}"
+                    },
+                    {
+                            SourceFile.UNKNOWN,
+                            "{}"
+                    },
+                    {
+                            SourceFile.UNKNOWN,
+                            "\"\""
+                    },
+                    {
+                            SourceFile.UNKNOWN,
+                            "{\"foo\":\"\"}"
+                    },
+                    {
+                            SourceFile.UNKNOWN,
+                            "{\"foo\": {\"bar\" : \":)\"}}"
+                    },
+            });
+        }
+
+        @Parameterized.Parameter(value = 0)
+        public SourceFile mSourceFile;
+
+        @Parameterized.Parameter(value = 1)
+        public String jsonString;
+
+
+        private static Gson sGson;
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = sGsonBuilder.create();
+        }
+
+        @Test
+        public void test() {
+            assertEquals(mSourceFile, sGson.fromJson(jsonString, SourceFile.class));
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+    }
+
+
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/SourceFilePositionJsonSerializerTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFilePositionJsonSerializerTest.java
new file mode 100644
index 0000000..e775370
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFilePositionJsonSerializerTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class SourceFilePositionJsonSerializerTest {
+
+    private static GsonBuilder sGsonBuilder = new GsonBuilder();
+
+    static {
+        MessageJsonSerializer.registerTypeAdapters(sGsonBuilder);
+    }
+
+    private static Object[][] allPairings(Object[] x, Object[] y) {
+        int totalSize = x.length * y.length;
+
+        final Object[][] matrix = new Object[totalSize][];
+        for (int i = 0; i < totalSize; i++) {
+            matrix[i] = new Object[2];
+            matrix[i][0] = x[i / y.length];
+            matrix[i][1] = y[i % y.length];
+        }
+
+        return matrix;
+
+    }
+
+    @RunWith(Parameterized.class)
+    public static class RoundTripTest {
+
+        private static Gson sGson;
+
+        @Parameterized.Parameter
+        public SourceFile mSourceFile;
+
+        @Parameterized.Parameter(value = 1)
+        public SourcePosition mSourcePosition;
+
+        @Parameterized.Parameters(name = "SourceFilePosition({0}, {1})")
+        public static Collection<Object[]> data() {
+
+            return Arrays.asList(allPairings(new SourceFile[]{
+                            new SourceFile(new File("/path/to/a/file.java")),
+                            new SourceFile(new File("/path/to/a/file.java"), "Description"),
+                            new SourceFile("Description"),
+                            SourceFile.UNKNOWN},
+                    SourcePositionJsonTypeAdapterTest.mExamples));
+        }
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = sGsonBuilder.create();
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+
+        @Test
+        public void test() {
+            SourceFilePosition item = new SourceFilePosition(mSourceFile, mSourcePosition);
+            assertEquals(item, sGson.fromJson(sGson.toJson(item), SourceFilePosition.class));
+        }
+    }
+
+    @RunWith(Parameterized.class)
+    public static class DeserializeTest {
+
+        private static Gson sGson;
+
+        @Parameterized.Parameter(value = 0)
+        public SourceFilePosition mSourceFilePosition;
+
+        @Parameterized.Parameter(value = 1)
+        public String jsonString;
+
+        @Parameterized.Parameters(name = "fromJson( {1} ) → {0}")
+        public static Collection<Object[]> data() {
+            return Arrays.asList(new Object[][]{
+                    {new SourceFilePosition(
+                            new SourceFile(new File("/path/file.java")),
+                            SourcePosition.UNKNOWN),
+                            "{\"file\":\"/path/file.java\"}"},
+                    {new SourceFilePosition(
+                            new SourceFile(new File("/path/file.java"), "Description"),
+                            new SourcePosition(1, 3, 4)),
+                            "{file:{\"description\":\"Description\",\"path\":\"/path/file.java\"},"
+                                    + "position:{startLine:1, startColumn:3,startOffset:4}}"},
+                    {new SourceFilePosition(
+                            new SourceFile("Description"),
+                            new SourcePosition(11, 22, 33, 66, 77, 88)),
+                            "{\"file\":{\"description\":\"Description\"}, " + "position: {"
+                                    + "\"startLine\":11,\"startColumn\":22,\"startOffset\":33,"
+                                    + "\"endLine\":66,\"endColumn\":77,\"endOffset\":88" + "}}"},
+                    {new SourceFilePosition(
+                            SourceFile.UNKNOWN,
+                            new SourcePosition(11, 22, 33, 66, 77, 88)),
+                            "{ position: {\"startLine\":11,\"startColumn\":22,\"startOffset\":33,"
+                                    + "\"endLine\":66,\"endColumn\":77,\"endOffset\":88" + "}, "
+                                    + "\"invalid_something\":[\"ignored tree\"]}"},
+                    {new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN), "{}"},});
+        }
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = sGsonBuilder.create();
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+
+        @Test
+        public void test() {
+            assertEquals(mSourceFilePosition, sGson.fromJson(jsonString, SourceFilePosition.class));
+        }
+    }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/SourcePositionJsonTypeAdapterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/SourcePositionJsonTypeAdapterTest.java
new file mode 100644
index 0000000..d58143d
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/SourcePositionJsonTypeAdapterTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+
+@RunWith(Enclosed.class)
+public class SourcePositionJsonTypeAdapterTest {
+
+    /* package */ static SourcePosition[] mExamples = new SourcePosition[]{
+            new SourcePosition(-1, -1, -1),
+            new SourcePosition(11, 22, 34),
+            new SourcePosition(11, 22, -1),
+            new SourcePosition(11, -1, 34),
+            new SourcePosition(-1, 22, 34),
+            new SourcePosition(11, 22, 33, 66, 77, 88),
+            new SourcePosition(11, 22, -1, 66, 77, -1),
+            new SourcePosition(11, -1, -1, 66, -1, -1),
+            new SourcePosition(11, -1, -1, 11, -1, -1),
+            new SourcePosition(11, 22, 33, 66, 77, 88),
+            new SourcePosition(-1, -1, 33, -1, -1, 88),
+            new SourcePosition(-1, -1, 33, -1, -1, 33),
+            new SourcePosition(11, 22, 33, 11, 22, 33)};
+
+    @RunWith(Parameterized.class)
+    public static class RoundTripTest {
+
+        private static Gson sGson;
+
+        @Parameterized.Parameter
+        public SourcePosition mSourcePosition;
+
+        @Parameterized.Parameter(value = 1)
+        public String mIgnoredName;
+
+        @Parameterized.Parameters(name = "{0}   new SourcePosition({1})")
+        public static Iterable<Object[]> data() {
+            return Iterables.transform(Arrays.asList(mExamples),
+                    new Function<SourcePosition, Object[]>() {
+                        @Override
+                        public Object[] apply(SourcePosition input) {
+                            return new Object[]{input, Joiner.on(", ").join(input.getStartLine(),
+                                    input.getStartColumn(),
+                                    input.getStartOffset(), input.getEndLine(),
+                                    input.getEndColumn(), input.getEndOffset())};
+                        }
+                    });
+        }
+
+        @Test
+        public void roundTrip() {
+            String json = sGson.toJson(mSourcePosition);
+            SourcePosition m2 = sGson.fromJson(json, SourcePosition.class);
+            assertEquals(mSourcePosition, m2);
+        }
+
+        @BeforeClass
+        public static void initGson() {
+            sGson = newGson();
+        }
+
+        @AfterClass
+        public static void removeGson() {
+            sGson = null;
+        }
+    }
+
+    public static class DeserializeTest {
+
+        private Gson mGson = newGson();
+
+        @Test
+        public void testSimpleDeserialize() {
+            String json2 = "{\"startLine\":245}";
+            SourcePosition range2 =
+                    mGson.fromJson(json2, SourcePosition.class);
+            assertEquals(new SourcePosition(245, -1, -1), range2);
+        }
+
+        @Test
+        public void testDeserialize() {
+            String json
+                    = "{\"startLine\":11,\"startColumn\":22,\"startOffset\":33,"
+                    + "\"endLine\":66,\"endColumn\":77,\"endOffset\":88, "
+                    + "\"invalid\":[\"ignored\"]}";
+            SourcePosition range =
+                    mGson.fromJson(json, SourcePosition.class);
+            assertEquals(range, new SourcePosition(11, 22, 33, 66, 77, 88));
+        }
+    }
+
+    private static Gson newGson() {
+        return new GsonBuilder()
+                .registerTypeAdapter(
+                        SourcePosition.class,
+                        new SourcePositionJsonTypeAdapter())
+                .create();
+    }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java
index 8058d3f..de9cb54 100644
--- a/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java
@@ -23,17 +23,17 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
 import com.android.ide.common.blame.parser.aapt.AaptOutputParser;
 import com.android.ide.common.blame.parser.aapt.AbstractAaptOutputParser;
-import com.android.utils.SdkUtils;
 import com.android.utils.StdLogger;
 import com.android.utils.StringHelper;
 import com.google.common.base.Charsets;
 import com.google.common.io.Closeables;
 import com.google.common.io.Files;
 
-import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import java.io.BufferedWriter;
@@ -48,9 +48,6 @@
  * Tests for {@link ToolOutputParser}.
  */
 public class AaptOutputParserTest extends TestCase {
-
-    private static final String NEWLINE = SdkUtils.getLineSeparator();
-
     private File sourceFile;
 
     private String sourceFilePath;
@@ -58,7 +55,7 @@
     private ToolOutputParser parser;
 
     @Nullable
-    private static String getSystemIndependentSourcePath(@NonNull GradleMessage message) {
+    private static String getSystemIndependentSourcePath(@NonNull Message message) {
         String sourcePath = message.getSourcePath();
         return sourcePath == null ? null : sourcePath.replace('\\', '/');
     }
@@ -69,21 +66,21 @@
     }
 
     @NonNull
-    private static String toString(@NonNull List<GradleMessage> messages) {
+    private static String toString(@NonNull List<Message> messages) {
         StringBuilder sb = new StringBuilder();
         for (int i = 0, n = messages.size(); i < n; i++) {
-            GradleMessage message = messages.get(i);
+            Message message = messages.get(i);
             sb.append(Integer.toString(i)).append(':').append(' ');
             sb.append(
                     StringHelper.capitalize(message.getKind().toString().toLowerCase(Locale.US)))
                     .append(':'); // INFO => Info
             sb.append(message.getText());
-            if (message.getSourcePath() != null) {
+            if (!message.getSourceFilePositions().isEmpty() &&
+                    !message.getSourceFilePositions().get(0).getPosition().equals(
+                            SourcePosition.UNKNOWN)) {
                 sb.append('\n');
                 sb.append('\t');
-                sb.append(message.getSourcePath());
-                sb.append(':').append(Long.toString(message.getLineNumber()));
-                sb.append(':').append(Long.toString(message.getColumn()));
+                sb.append(message.getSourceFilePositions().get(0).toString());
             }
             sb.append('\n');
         }
@@ -91,14 +88,6 @@
         return sb.toString();
     }
 
-    public static void assertEquals(String expected, String actual) {
-        if (expected != null || actual != null) {
-            if (expected == null || !expected.equals(actual)) {
-                throw new ComparisonFailure(null, expected, actual);
-            }
-        }
-    }
-
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -116,11 +105,11 @@
 
     public void testParseDisplayingUnhandledMessages() {
         String output = " **--- HELLO WORLD ---**";
-        List<GradleMessage> gradleMessages = parser.parseToolOutput(output);
+        List<Message> gradleMessages = parser.parseToolOutput(output);
         assertEquals(1, gradleMessages.size());
-        GradleMessage message = gradleMessages.get(0);
+        Message message = gradleMessages.get(0);
         assertEquals(output, message.getText());
-        assertEquals(GradleMessage.Kind.SIMPLE, message.getKind());
+        assertEquals(Message.Kind.SIMPLE, message.getKind());
     }
 
     public void testParseAaptOutputWithRange1() throws IOException {
@@ -132,7 +121,7 @@
         String messageText = "No resource found that matches the given name (at 'label' with value "
                 + "'@string/app_name2').";
         String err = sourceFilePath + ":4: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 4, 61);
     }
 
@@ -149,7 +138,7 @@
         String messageText = "No resource found that matches the given name (at 'label' with value "
                 + "'@string/app_name2').";
         String err = sourceFilePath + ":4: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 5, 8);
     }
 
@@ -166,7 +155,7 @@
                 "    <item name='android:gravity'>left</item>");
         String messageText = "Resource entry repeatedStyle1 already has bag item android:gravity.";
         String err = sourceFilePath + ":6: error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 6, 17);
     }
 
@@ -181,7 +170,7 @@
                 "    <item name='android:gravity'>left</item>");
         String messageText = "Originally defined here.";
         String err = sourceFilePath + ":3: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 3, 5);
     }
 
@@ -194,7 +183,7 @@
                 "    <item name='nonexistent'>left</item>");
         String messageText = "No resource found that matches the given name: attr 'nonexistent'.";
         String err = sourceFilePath + ":3: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 3, 17);
     }
 
@@ -205,7 +194,7 @@
                 "  <style>");
         String messageText = "A 'name' attribute is required for <style>";
         String err = sourceFilePath + ":2: error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 2, 3);
     }
 
@@ -215,7 +204,7 @@
                 "  <item>");
         String messageText = "A 'type' attribute is required for <item>";
         String err = sourceFilePath + ":2: error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 2, 3);
     }
 
@@ -225,7 +214,7 @@
                 "  <item>");
         String messageText = "A 'name' attribute is required for <item>";
         String err = sourceFilePath + ":2: error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 2, 3);
     }
 
@@ -236,7 +225,7 @@
                 "        <item name='android:layout_width'></item>");
         String messageText = "String types not allowed (at 'android:layout_width' with value '').";
         String err = sourceFilePath + ":3: error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 3, 21);
     }
 
@@ -253,7 +242,7 @@
                 "        android:layout_marginLeft=''");
         String messageText = "String types not allowed (at 'layout_marginTop' with value '').";
         String err = sourceFilePath + ":5: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 8, 34);
     }
 
@@ -270,7 +259,7 @@
                 "        android:layout_marginLeft=''");
         String messageText = "String types not allowed (at 'layout_marginLeft' with value '').";
         String err = sourceFilePath + ":5: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 9, 35);
     }
 
@@ -284,7 +273,7 @@
                 "        android:id=''");
         String messageText = "String types not allowed (at 'id' with value '').";
         String err = sourceFilePath + ":5: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertHasCorrectErrorMessage(messages, messageText, 6, 20);
     }
 
@@ -310,33 +299,28 @@
             Closeables.close(out, true /* swallowIOException */);
         }
     }
-    //    String homePath = PathManager.getHomePath();
-    //    assertNotNull(homePath);
-    //    // The relative paths in the output file below is relative to the sdk-common directory in tools/base
-    //    // (it's from one of the unit tests there)
-    //    File rootDir = new File(homePath, ".." + File.separator + "base" + File.separator + "sdk-common");
-    //    if (!rootDir.isDirectory()) {
-    //        // Also check for an IDEA project structure where sdk-common is located in this location:
-    //        rootDir = new File(homePath, "android" + File.separator + "tools-base" + File.separator + "sdk-common");
-    //        if (!rootDir.isDirectory()) {
-    //            return false;
-    //        }
-    //    }
-    //    AbstractAaptOutputParser.ourRootDir = rootDir;
-    //    return true;
-    //}
 
-    private void assertHasCorrectErrorMessage(@NonNull Collection<GradleMessage> messages,
+    /**
+     * Assert the error message is correct.
+     *
+     * @param messages       a collection of Messages
+     * @param expectedText   the text the single gradle message should have.
+     * @param expectedLine   the 1-based line
+     * @param expectedColumn the 1-based column.
+     */
+    private void assertHasCorrectErrorMessage(@NonNull Collection<Message> messages,
             @NonNull String expectedText,
-            long expectedLine,
-            long expectedColumn) {
+            int expectedLine,
+            int expectedColumn) {
         assertEquals("[message count]", 1, messages.size());
-        GradleMessage message = messages.iterator().next();
-        assertEquals("[file path]", sourceFilePath, message.getSourcePath());
-        assertEquals("[message severity]", GradleMessage.Kind.ERROR, message.getKind());
+        Message message = messages.iterator().next();
+        assertEquals("[source file position count]", 1, message.getSourceFilePositions().size());
+        SourceFilePosition position = message.getSourceFilePositions().get(0);
+        assertEquals("[file path]", sourceFilePath, position.getFile().toString());
+        assertEquals("[message severity]", Message.Kind.ERROR, message.getKind());
         assertEquals("[message text]", expectedText, message.getText());
-        assertEquals("[position line]", expectedLine, message.getLineNumber());
-        assertEquals("[position column]", expectedColumn, message.getColumn());
+        assertEquals("[position line]", expectedLine, position.getPosition().getStartLine() + 1);
+        assertEquals("[position column]", expectedColumn, position.getPosition().getStartColumn() + 1);
     }
 
     public void testRedirectValueLinksOutput() throws Exception {
@@ -445,23 +429,24 @@
         String messageText
                 = "String types not allowed (at 'drawable_ref' with value '@drawable/stat_notify_sync_anim0').";
         String err = sourceFilePath + ":46: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertEquals(1, messages.size());
 
         assertEquals("[message count]", 1, messages.size());
-        GradleMessage message = messages.iterator().next();
+        Message message = messages.iterator().next();
 
         assertNotNull(message);
 
         // NOT sourceFilePath; should be translated back from source comment
-        assertEquals("[file path]",
-                "src/test/resources/testData/resources/baseSet/values/values.xml",
-                getSystemIndependentSourcePath(message));
+        assertEquals(new File ("src/test/resources/testData/resources/baseSet/values/values.xml").getAbsolutePath(),
+                     getSystemIndependentSourcePath(message));
 
-        assertEquals("[message severity]", GradleMessage.Kind.ERROR, message.getKind());
+        assertEquals("[message severity]", Message.Kind.ERROR, message.getKind());
         assertEquals("[message text]", messageText, message.getText());
-        assertEquals("[position line]", 9, message.getLineNumber());
-        assertEquals("[position column]", 35, message.getColumn());
+        assertEquals(1, message.getSourceFilePositions().size());
+        SourcePosition pos = message.getSourceFilePositions().get(0).getPosition();
+        assertEquals("[position line]", 9, pos.getStartLine() + 1);
+        assertEquals("[position column]", 35, pos.getStartColumn() + 1);
     }
 
     public void testRedirectFileLinksOutput() throws Exception {
@@ -498,21 +483,18 @@
 
         String messageText = "Random error message here";
         String err = sourceFilePath + ":4: error: Error: " + messageText;
-        Collection<GradleMessage> messages = parser.parseToolOutput(err);
-        assertEquals(1, messages.size());
-
+        Collection<Message> messages = parser.parseToolOutput(err);
         assertEquals("[message count]", 1, messages.size());
-        GradleMessage message = messages.iterator().next();
+        Message message = messages.iterator().next();
         assertNotNull(message);
 
         // NOT sourceFilePath; should be translated back from source comment
-        String expected
-                = "src/test/resources/testData/resources/incMergeData/filesVsValues/main/layout/main.xml";
+        String expected = new File("src/test/resources/testData/resources/incMergeData/filesVsValues/main/layout/main.xml")
+          .getAbsolutePath();
         assertEquals("[file path]", expected, getSystemIndependentSourcePath(message));
-
-        assertEquals("[message severity]", GradleMessage.Kind.ERROR, message.getKind());
+        assertEquals("[message severity]", Message.Kind.ERROR, message.getKind());
         assertEquals("[message text]", messageText, message.getText());
-        assertEquals("[position line]", 4, message.getLineNumber());
+        assertEquals("[position line]", 4, message.getSourceFilePositions().get(0).getPosition().getStartLine() + 1);
         //assertEquals("[position column]", 35, message.getColumn());
 
         // TODO: Test encoding issues (e.g. & in path where the XML source comment would be &amp; instead)
@@ -596,7 +578,7 @@
                         "5: Simple::BlankProject1:processDebugManifest UP-TO-DATE\n" +
                         "6: Simple::BlankProject1:processDebugResources\n" +
                         "7: Error:Integer types not allowed (at 'new_name' with value '50').\n" +
-                        "\t" + source.getPath() + ":5:28\n" +
+                        "\t" + source.getPath() + ":5:28-30\n" +
                         "8: Simple::BlankProject1:processDebugResources FAILED\n"; /* +
                         "9: Error:Error while executing aapt command\n" +
                         "10: Error:Integer types not allowed (at 'new_name' with value '50').\n" +
@@ -675,7 +657,7 @@
                         "5: Simple::BlankProject1:processDebugManifest UP-TO-DATE\n" +
                         "6: Simple::BlankProject1:processDebugResources\n" +
                         "7: Error:Integer types not allowed (at 'new_name' with value '50').\n" +
-                        "\t" + source.getPath() + ":5:28\n" +
+                        "\t" + source.getPath() + ":5:28-30\n" +
                         "8: Simple::BlankProject1:processDebugResources FAILED\n";
         String actual = toString(parser.parseToolOutput(output));
 
@@ -771,7 +753,7 @@
                         "11: Simple::BlankProject1:processDebugResources\n" +
                         "12: Error:No resource identifier found for attribute 'slayout_alignParentTop' in package 'android'\n"
                         +
-                        "\t" + source.getPath() + ":12:-1\n" +
+                        "\t" + source.getPath() + ":12\n" +
                         "13: Simple::BlankProject1:processDebugResources FAILED\n",
                 toString(parser.parseToolOutput(output)));
 
@@ -804,28 +786,9 @@
                         "6: Simple::AudioPlayer:processDebugManifest UP-TO-DATE\n" +
                         "7: Simple::AudioPlayer:processDebugResources\n" +
                         "8: Error:Error parsing XML: mismatched tag\n" +
-                        "\t" + sourceFilePath + ":101:-1\n" +
+                        "\t" + sourceFilePath + ":101\n" +
                         "9: Simple::AudioPlayer:processDebugResources FAILED\n",
                 toString(parser.parseToolOutput(output)));
         sourceFile.delete();
     }
-
-    private static class ComparisonFailure extends AssertionFailedError {
-
-        private final String fExpected;
-
-        private final String fActual;
-
-        public ComparisonFailure(String message, String expected, String actual) {
-            super(message);
-            this.fExpected = expected;
-            this.fActual = actual;
-        }
-
-        @Override
-        public String getMessage() {
-            return "expected:<" + fExpected + "> but was:<" + fActual + ">";
-
-        }
-    }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/GradleMessageRewriterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/GradleMessageRewriterTest.java
deleted file mode 100644
index 141f761..0000000
--- a/sdk-common/src/test/java/com/android/ide/common/blame/parser/GradleMessageRewriterTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame.parser;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.output.GradleMessage;
-import com.android.ide.common.blame.output.GradleMessageRewriter;
-import com.android.ide.common.blame.parser.util.OutputLineReader;
-import com.android.ide.common.res2.RecordingLogger;
-import com.android.utils.ILogger;
-
-import junit.framework.TestCase;
-
-import java.util.List;
-
-public class GradleMessageRewriterTest extends TestCase {
-
-    private GradleMessageRewriter mGradleMessageRewriter;
-
-    private ILogger mLogger;
-
-    private PatternAwareOutputParser mFakePatternParser;
-
-    @Override
-    public void setUp() throws Exception {
-        mLogger = new RecordingLogger();
-        mFakePatternParser = new FakePatternAwareOutputParser();
-
-        mGradleMessageRewriter = new GradleMessageRewriter(
-                new ToolOutputParser(mFakePatternParser, mLogger),
-                GradleMessageRewriter.ErrorFormatMode.MACHINE_PARSABLE);
-    }
-
-    public void testRewriteGradleMessages() {
-        String original = "error example\ntwo line error\nnext line\nsomething else";
-        String rewriten = mGradleMessageRewriter.rewriteMessages(original);
-        String expected = "AGPBI: {\"kind\":\"ERROR\",\"text\":\"errorText\",\"sourcePath\":\"error/source\","
-                + "\"position\":{\"startLine\":1,\"startColumn\":2,\"startOffset\":3,"
-                + "\"endLine\":4,\"endColumn\":5,\"endOffset\":6},\"original\":\"\"}\n"
-                + "AGPBI: {\"kind\":\"WARNING\",\"text\":"
-                + "\"two line warning\",\"sourcePath\":\"sourcePath\","
-                + "\"position\":{\"startLine\":1,\"startColumn\":2},\"original\":\"\"}\n"
-                + "AGPBI: {\"kind\":\"SIMPLE\","
-                + "\"text\":\"something else\",\"position\":{},\"original\":\"something else\"}";
-        assertEquals(expected.trim(), rewriten.trim());
-    }
-
-    public void testParseException() {
-        String original = "two line error";
-        String rewriten = mGradleMessageRewriter.rewriteMessages(original);
-        assertEquals(original, rewriten);
-    }
-
-    private class FakePatternAwareOutputParser implements PatternAwareOutputParser {
-        @Override
-        public boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
-                @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
-                throws ParsingFailedException {
-            if (line.equals("two line error")) {
-                String nextLine = reader.readLine();
-                if ("next line".equals(nextLine)) {
-                    messages.add(new GradleMessage(GradleMessage.Kind.WARNING, "two line warning",
-                            "sourcePath", new SourcePosition(1, 2, -1), ""));
-                } else {
-                    throw new ParsingFailedException();
-                }
-                return true;
-            }
-            if (line.equals("error example")) {
-                messages.add(
-                        new GradleMessage(
-                                GradleMessage.Kind.ERROR,
-                                "errorText",
-                                "error/source",
-                                new SourcePosition(1, 2, 3, 4, 5, 6),
-                                ""));
-                return true;
-            }
-            return false;
-        }
-    }
-}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/LegacyNdkOutputParserTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/LegacyNdkOutputParserTest.java
new file mode 100644
index 0000000..43e5bcb
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/parser/LegacyNdkOutputParserTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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.ide.common.blame.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.ide.common.blame.Message;
+import com.android.utils.StdLogger;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Tests for {@link LegacyNdkOutputParser}.
+ */
+public class LegacyNdkOutputParserTest {
+    @Rule
+    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    private ToolOutputParser mParser;
+
+    private File mSourceFile;
+
+    @Before
+    public void setUp() throws IOException {
+        mParser = new ToolOutputParser(new LegacyNdkOutputParser(),
+                new StdLogger(StdLogger.Level.VERBOSE));
+        mSourceFile = mTemporaryFolder.newFile();
+    }
+
+    @Test
+    public void testParseUnresolvedInclude() {
+        String path = mSourceFile.getAbsolutePath();
+        String err = path +
+                ":35:18: fatal error: fake.h: No such file or directory\n" +
+                " #include \"fake.h\"\n" +
+                "                  ^";
+        List<Message> messages = mParser.parseToolOutput(err);
+        assertEquals("[message count]", 1, messages.size());
+        Message message = messages.iterator().next();
+        assertNotNull(message);
+
+        assertEquals("fake.h: No such file or directory", message.getText());
+        assertEquals(Message.Kind.ERROR, message.getKind());
+        assertEquals(path, message.getSourcePath());
+        assertEquals(35, message.getLineNumber());
+        assertEquals(18, message.getColumn());
+    }
+
+    @Test
+    public void testParseUnknownMessage() {
+        String path = mSourceFile.getAbsolutePath();
+        String err = "In file included from " + path + ":35:18,";
+        List<Message> messages = mParser.parseToolOutput(err);
+        assertEquals("[message count]", 1, messages.size());
+        Message message = messages.iterator().next();
+        assertNotNull(message);
+
+        assertEquals("(Unknown) In file included", message.getText());
+        assertEquals(Message.Kind.INFO, message.getKind());
+        assertEquals(path, message.getSourcePath());
+        assertEquals(35, message.getLineNumber());
+        assertEquals(18, message.getColumn());
+    }
+
+    @Test
+    public void testParseUnknownMessage2() {
+        String path = mSourceFile.getAbsolutePath();
+        String err = "                 from " + path + ":35:18:";
+        List<Message> messages = mParser.parseToolOutput(err);
+        assertEquals("[message count]", 1, messages.size());
+        Message message = messages.iterator().next();
+        assertNotNull(message);
+
+        assertEquals("(Unknown)", message.getText());
+        assertEquals(Message.Kind.INFO, message.getKind());
+        assertEquals(path, message.getSourcePath());
+        assertEquals(35, message.getLineNumber());
+        assertEquals(18, message.getColumn());
+    }
+}
\ No newline at end of file
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/SourcePositionJsonTypeAdapterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/SourcePositionJsonTypeAdapterTest.java
deleted file mode 100644
index cde1e47..0000000
--- a/sdk-common/src/test/java/com/android/ide/common/blame/parser/SourcePositionJsonTypeAdapterTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 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.ide.common.blame.parser;
-
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.SourcePositionJsonTypeAdapter;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-import junit.framework.TestCase;
-
-public class SourcePositionJsonTypeAdapterTest extends TestCase {
-
-    private Gson gsonSerializer;
-
-    private Gson gsonDeserializer;
-
-    private SourcePosition[] mExamples = new SourcePosition[]{
-            new SourcePosition(-1, -1, -1),
-            new SourcePosition(11, 22, 34),
-            new SourcePosition(11, 22, -1),
-            new SourcePosition(11, -1, 34),
-            new SourcePosition(-1, 22, 34),
-            new SourcePosition(11, 22, 33, 66, 77, 88),
-            new SourcePosition(11, 22, -1, 66, 77, -1),
-            new SourcePosition(11, -1, -1, 66, -1, -1),
-            new SourcePosition(11, -1, -1, 11, -1, -1),
-            new SourcePosition(11, 22, 33, 66, 77, 88),
-            new SourcePosition(-1, -1, 33, -1, -1, 88),
-            new SourcePosition(-1, -1, 33, -1, -1, 33),
-            new SourcePosition(11, 22, 33, 11, 22, 33)};
-
-    @Override
-    public void setUp() {
-        gsonSerializer = new GsonBuilder()
-                .registerTypeAdapter(
-                        SourcePosition.class,
-                        new SourcePositionJsonTypeAdapter())
-                .create();
-        gsonDeserializer = new GsonBuilder()
-                .registerTypeAdapter(
-                        SourcePosition.class,
-                        new SourcePositionJsonTypeAdapter())
-                .create();
-    }
-
-
-    public void testSerializeDeserializeRoundtrip() {
-        for (SourcePosition range : mExamples) {
-            testRoundTripExample(range);
-        }
-    }
-
-    private void testRoundTripExample(SourcePosition m1) {
-        String json = gsonSerializer.toJson(m1);
-        SourcePosition m2 =
-                gsonDeserializer.fromJson(json, SourcePosition.class);
-        assertEquals(m1, m2);
-    }
-
-
-    public void testSimpleDeserialize() {
-        String json2 = "{\"startLine\":245}";
-        SourcePosition range2 =
-                gsonDeserializer.fromJson(json2, SourcePosition.class);
-        assertEquals(new SourcePosition(245, -1, -1), range2);
-    }
-
-    public void testDeserialize() {
-        String json
-                = "{\"startLine\":11,\"startColumn\":22,\"startOffset\":33,\"endLine\":66,\"endColumn\":77,\"endOffset\":88}";
-        SourcePosition range =
-                gsonDeserializer.fromJson(json, SourcePosition.class);
-        assertEquals(range, new SourcePosition(11, 22, 33, 66, 77, 88));
-    }
-}
diff --git a/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java b/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java
index 18129ff..209ef5d 100644
--- a/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/rendering/HardwareConfigHelperTest.java
@@ -4,7 +4,6 @@
 import static com.android.ide.common.rendering.HardwareConfigHelper.getNexusLabel;
 import static com.android.ide.common.rendering.HardwareConfigHelper.isGeneric;
 import static com.android.ide.common.rendering.HardwareConfigHelper.isNexus;
-import static com.android.ide.common.rendering.HardwareConfigHelper.isRound;
 import static com.android.ide.common.rendering.HardwareConfigHelper.isTv;
 import static com.android.ide.common.rendering.HardwareConfigHelper.isWear;
 import static com.android.ide.common.rendering.HardwareConfigHelper.nexusRank;
@@ -77,37 +76,37 @@
         assertNotNull(qvga);
         assertFalse(isWear(qvga));
         assertFalse(isTv(qvga));
-        assertFalse(isRound(qvga));
+        assertFalse(qvga.isScreenRound());
 
         Device nexus5 = deviceManager.getDevice("Nexus 5", "Google");
         assertNotNull(nexus5);
         assertFalse(isWear(nexus5));
         assertFalse(isTv(nexus5));
-        assertFalse(isRound(nexus5));
+        assertFalse(nexus5.isScreenRound());
 
         Device square = deviceManager.getDevice("wear_square", "Google");
         assertNotNull(square);
         assertTrue(isWear(square));
-        assertFalse(isRound(square));
+        assertFalse(square.isScreenRound());
         assertFalse(isTv(square));
 
         Device round = deviceManager.getDevice("wear_round", "Google");
         assertNotNull(round);
         assertTrue(isWear(round));
-        assertTrue(isRound(round));
+        assertTrue(round.isScreenRound());
         assertFalse(isTv(round));
 
         Device tv1080p = deviceManager.getDevice("tv_1080p", "Google");
         assertNotNull(tv1080p);
         assertTrue(isTv(tv1080p));
         assertFalse(isWear(tv1080p));
-        assertFalse(isRound(tv1080p));
+        assertFalse(tv1080p.isScreenRound());
 
         Device tv720p = deviceManager.getDevice("tv_1080p", "Google");
         assertNotNull(tv720p);
         assertFalse(isWear(tv720p));
         assertTrue(isTv(tv720p));
-        assertFalse(isRound(tv720p));
+        assertFalse(tv720p.isScreenRound());
     }
 
     public void testNexusRank() {
diff --git a/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java b/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java
index 6f796f8..19a3214 100644
--- a/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/rendering/RenderSecurityManagerTest.java
@@ -156,7 +156,7 @@
         try {
             manager.setActive(true, myCredential);
 
-            System.loadLibrary("fontmanager");
+            System.loadLibrary("jsound");
         } catch (UnsatisfiedLinkError e) {
             // pass - library may not be present on all JDKs
         } finally {
diff --git a/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java b/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java
index 1068a4d..e2665b3 100644
--- a/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java
@@ -99,6 +99,24 @@
         assertEquals("google", SdkMavenRepository.GOOGLE.getDirName());
     }
 
+    @SuppressWarnings("ConstantConditions")
+    public void testGetByGroupId() {
+        assertSame(SdkMavenRepository.ANDROID, SdkMavenRepository.getByGroupId(
+                GradleCoordinate.parseCoordinateString(
+                        "com.android.support:appcompat-v7:13.0.0").getGroupId()));
+        assertSame(SdkMavenRepository.ANDROID, SdkMavenRepository.getByGroupId(
+                GradleCoordinate.parseCoordinateString(
+                        "com.android.support.test:espresso:0.2").getGroupId()));
+        assertSame(SdkMavenRepository.GOOGLE, SdkMavenRepository.getByGroupId(
+                GradleCoordinate.parseCoordinateString(
+                        "com.google.android.gms:play-services:5.2.08").getGroupId()));
+        assertSame(SdkMavenRepository.GOOGLE, SdkMavenRepository.getByGroupId(
+                GradleCoordinate.parseCoordinateString(
+                        "com.google.android.gms:play-services-wearable:5.0.77").getGroupId()));
+        assertNull(SdkMavenRepository.getByGroupId(GradleCoordinate.parseCoordinateString(
+                "com.google.guava:guava:11.0.2").getGroupId()));
+    }
+
     /**
      * Environment variable or system property containing the full path to an SDK install
      */
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java b/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java
index 1232ea0..3047ef9 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java
@@ -39,7 +39,7 @@
 
         for (String resKey : dataItemKeys) {
             List<? extends DataItem> items = map.get(resKey);
-            assertTrue("resource '" + resKey + "' is missing!", items.size() > 0);
+            assertTrue("resource '" + resKey + "' is missing!", !items.isEmpty());
         }
     }
 
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java
index 1f33214..21e6fd4 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java
@@ -53,8 +53,11 @@
                 {"foo.jpg", ResourceType.DRAWABLE, null},
                 {"foo.jpeg", ResourceType.DRAWABLE, null},
                 {"foo.bmp", ResourceType.DRAWABLE, null},
+                {"foo.webp", ResourceType.DRAWABLE, null},
                 {"foo.other.png", ResourceType.DRAWABLE, "'.'" + IS_NOT_A_VALID_ETC},
                 {"foo.xml", ResourceType.XML, null},
+                {"foo.xsd", ResourceType.XML, null},
+                {"_foo.png", ResourceType.DRAWABLE, null},
                 {"foo.png", ResourceType.XML, "The file name must end with .xml"},
                 {"foo.8.xml", ResourceType.DRAWABLE, "'.'" + IS_NOT_A_VALID_ETC},
                 {"foo", ResourceType.DRAWABLE, THE_FILE_NAME_MUST_END_WITH_XML_OR_PNG},
@@ -62,8 +65,8 @@
                 {"foo", ResourceType.RAW, null},
                 {"foo", ResourceType.RAW, null},
                 {"foo.txt", ResourceType.DRAWABLE, THE_FILE_NAME_MUST_END_WITH_XML_OR_PNG},
-                {"Foo.png", ResourceType.DRAWABLE,
-                        "File-based resource names must start with a lowercase letter"},
+                {"1foo.png", ResourceType.DRAWABLE, "The resource name must start with a letter"},
+                {"Foo.png", ResourceType.DRAWABLE, "'F'" + IS_NOT_A_VALID_ETC},
                 {"foo$.png", ResourceType.DRAWABLE, "'$'" + IS_NOT_A_VALID_ETC},
                 {"bAr.png", ResourceType.DRAWABLE, "'A'" + IS_NOT_A_VALID_ETC},
                 {"enum.png", ResourceType.DRAWABLE,
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java
index d82e1ca..bed8a07 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java
@@ -16,50 +16,123 @@
 
 package com.android.ide.common.res2;
 
-import com.android.ide.common.blame.FilePosition;
-import com.android.ide.common.blame.SourcePosition;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
-import junit.framework.TestCase;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import org.junit.Test;
+import org.xml.sax.SAXParseException;
 
 import java.io.File;
+import java.io.IOException;
+import java.util.List;
 
-public class MergingExceptionTest extends TestCase {
-    @SuppressWarnings("ThrowableInstanceNeverThrown")
+@SuppressWarnings({"ThrowableInstanceNeverThrown", "ThrowableResultOfMethodCallIgnored"})
+public class MergingExceptionTest {
+
+    private static final File file = new File("/some/random/path");
+
+    @Test
     public void testGetMessage() {
-        File file = new File("/some/random/path");
         assertEquals("Error: My error message",
-                new MergingException("My error message").getMessage());
+                MergingException.withMessage("My error message").build().getMessage());
         assertEquals("Error: My error message",
-                new MergingException("Error: My error message").getMessage());
+                MergingException.wrapException(new Exception()).withMessage(
+                        "Error: My error message").build().getMessage());
+        assertEquals("Error: My error message",
+                MergingException.withMessage("Error: My error message").build().getMessage());
         assertEquals("/some/random/path: Error: My error message",
-                new MergingException("My error message").addFile(file).getMessage());
+                MergingException.wrapException(new Exception("My error message")).withFile(file)
+                        .build().getMessage());
+        MergingException.Builder builder2 = MergingException
+                .wrapException(new Exception("My error message")).withFile(file)
+                .withPosition(new SourcePosition(49, -1, -1));
         assertEquals("/some/random/path:50: Error: My error message",
-                new MergingException("My error message").addFilePosition(
-                        new FilePosition(file, new SourcePosition(50, -1, -1)))
+                builder2.build()
                         .getMessage());
+        MergingException.Builder builder1 = MergingException
+                .wrapException(new Exception("My error message")).withFile(file)
+                .withPosition(new SourcePosition(49, 3, -1));
         assertEquals("/some/random/path:50:4: Error: My error message",
-                new MergingException("My error message").addFilePosition(
-                        new FilePosition(file, new SourcePosition(50, 4, -1)))
+                builder1.build()
                         .getMessage());
+        MergingException.Builder builder = MergingException
+                .wrapException(new Exception("My error message")).withFile(file)
+                .withPosition(new SourcePosition(49, 3, -1));
         assertEquals("/some/random/path:50:4: Error: My error message",
-                new MergingException("My error message").addFilePosition(
-                        new FilePosition(file, new SourcePosition(50, 4, -1)))
+                builder.build()
                         .getLocalizedMessage());
         assertEquals("/some/random/path: Error: My error message",
-                new MergingException("/some/random/path: My error message").addFile(file)
-                        .getMessage());
+                MergingException.withMessage("/some/random/path: My error message").withFile(file)
+                        .build().getMessage());
         assertEquals("/some/random/path: Error: My error message",
-                new MergingException("/some/random/path My error message").addFile(file)
-                        .getMessage());
+                MergingException.withMessage("/some/random/path My error message").withFile(file)
+                        .build().getMessage());
 
         // end of string handling checks
         assertEquals("/some/random/path: Error: ",
-                new MergingException("/some/random/path").addFile(file).getMessage());
+                MergingException.withMessage("/some/random/path").withFile(file).build()
+                        .getMessage());
         assertEquals("/some/random/path: Error: ",
-                new MergingException("/some/random/path").addFile(file).getMessage());
+                MergingException.withMessage("/some/random/path").withFile(file).build().getMessage());
         assertEquals("/some/random/path: Error: ",
-                new MergingException("/some/random/path:").addFile(file).getMessage());
+                MergingException.withMessage("/some/random/path:").withFile(file).build().getMessage());
         assertEquals("/some/random/path: Error: ",
-                new MergingException("/some/random/path: ").addFile(file).getMessage());
+                MergingException.withMessage("/some/random/path: ").withFile(file).build().getMessage());
+    }
+
+
+    @Test
+    public void testWrapSaxParseExceptionWithLocation() {
+        SAXParseException saxParseException = new SAXParseException("message", "", "", 5, 7);
+        List<Message> messages = MergingException
+                .wrapException(saxParseException).withFile(file).build().getMessages();
+        assertEquals(new Message(Message.Kind.ERROR, "message",
+                new SourceFilePosition(file, new SourcePosition(4, 6, -1))), messages.get(0));
+    }
+
+    @Test
+    public void testWrapSaxParseExceptionWithoutLocation() {
+        SAXParseException saxParseException = new SAXParseException("message", "", "", -1, -1);
+
+        List<Message> messages = MergingException
+                .wrapException(saxParseException).withFile(file).build().getMessages();
+        assertEquals(new Message(Message.Kind.ERROR, "message",
+                new SourceFilePosition(file, SourcePosition.UNKNOWN)), messages.get(0));
+    }
+
+    @Test
+    public void testThrowIfNonEmpty() throws MergingException{
+        MergingException.throwIfNonEmpty(ImmutableList.<Message>of());
+        try {
+            MergingException.throwIfNonEmpty(ImmutableList.<Message>of(
+                    new Message(Message.Kind.ERROR, "Message", SourceFilePosition.UNKNOWN)));
+            fail();
+        } catch (MergingException e) {
+            // ok
+        }
+    }
+
+
+    @Test
+    public void testMergingExceptionWithNullMessage() {
+        MergingException exception = MergingException.wrapException(new IOException()).build();
+        Message message = Iterables.getOnlyElement(exception.getMessages());
+        assertNotNull(message.getText());
+        assertNotNull(message.getRawMessage());
+    }
+
+    @Test
+    public void testConsumerExceptionWithNullMessage() {
+        MergingException exception = new MergeConsumer.ConsumerException(new IOException());
+        Message message = Iterables.getOnlyElement(exception.getMessages());
+        assertNotNull(message.getText());
+        assertNotNull(message.getRawMessage());
     }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java
index 611ab3c..3fd5f8d 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java
@@ -16,18 +16,12 @@
 
 package com.android.ide.common.res2;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import com.android.annotations.Nullable;
 import com.android.resources.ResourceType;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import java.io.File;
 import java.util.Arrays;
 import java.util.Collection;
 
@@ -56,8 +50,7 @@
                 {"foo", ResourceType.RAW, null},
                 {"foo", ResourceType.RAW, null},
                 {"foo.txt", ResourceType.DRAWABLE, THE_FILE_NAME_MUST_END_WITH_XML_OR_PNG},
-                {"Foo.png", ResourceType.DRAWABLE,
-                        "File-based resource names must start with a lowercase letter"},
+                {"Foo.png", ResourceType.DRAWABLE, "'F'" + IS_NOT_A_VALID_ETC},
                 {"foo$.png", ResourceType.DRAWABLE, "'$'" + IS_NOT_A_VALID_ETC},
                 {"enum.png", ResourceType.DRAWABLE,
                         "enum is not a valid resource name (reserved Java keyword)"},
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java
index 9206dc6..d2d64e6 100755
--- a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceMergerTest.java
@@ -59,6 +59,9 @@
     @Mock
     PngCruncher mPngCruncher;
 
+    @Mock
+    ResourcePreprocessor mPreprocessor;
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -327,7 +330,7 @@
 
         File folder = Files.createTempDir();
         merger.writeBlobTo(folder,
-                new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+                getConsumer());
 
         ResourceMerger loadedMerger = new ResourceMerger();
         assertTrue(loadedMerger.loadFromBlob(folder, true /*incrementalState*/));
@@ -397,7 +400,7 @@
 
         File folder = Files.createTempDir();
         merger.writeBlobTo(folder,
-                new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+                getConsumer());
 
         ResourceMerger loadedMerger = new ResourceMerger();
         assertTrue(loadedMerger.loadFromBlob(folder, true /*incrementalState*/));
@@ -530,8 +533,7 @@
         File resFolder = getFolderCopy(new File(root, "resOut"));
 
         // write the content of the resource merger.
-        MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
-                false, null);
+        MergedResourceWriter writer = getConsumer(resFolder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         // Check the content.
@@ -645,8 +647,7 @@
         File resFolder = getFolderCopy(new File(root, "resOut"));
 
         // write the content of the resource merger.
-        MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
-                false, null);
+        MergedResourceWriter writer = getConsumer(resFolder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         // Check the content.
@@ -723,8 +724,7 @@
         File resFolder = getFolderCopy(new File(root, "resOut"));
 
         // write the content of the resource merger.
-        MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
-                false, null);
+        MergedResourceWriter writer = getConsumer(resFolder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         // Check the content.
@@ -823,8 +823,7 @@
         File resFolder = getFolderCopy(new File(root, "resOut"));
 
         // write the content of the resource merger.
-        MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
-                false, null);
+        MergedResourceWriter writer = getConsumer(resFolder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         // deleted layout/file_replaced_by_alias.xml
@@ -860,7 +859,7 @@
         // write merger1 on disk to test writing empty ResourceSets.
         File folder = Files.createTempDir();
         merger1.writeBlobTo(folder,
-                new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+                getConsumer());
 
         // reload it
         ResourceMerger loadedMerger = new ResourceMerger();
@@ -999,8 +998,7 @@
         File outFolder = getFolderCopy(new File(root, "out"));
 
         // write the content of the resource merger.
-        MergedResourceWriter writer = new MergedResourceWriter(outFolder, mPngCruncher, false,
-                false, null);
+        MergedResourceWriter writer = getConsumer(outFolder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         File outDrawableFolder = new File(outFolder, ResourceFolderType.DRAWABLE.getName());
@@ -1049,7 +1047,7 @@
         File folder = Files.createTempDir();
         folder.deleteOnExit();
 
-        MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false, false, null);
+        MergedResourceWriter writer = getConsumer(folder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         // load the result as a set.
@@ -1323,7 +1321,7 @@
         File folder = Files.createTempDir();
         folder.deleteOnExit();
 
-        MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false, false, null);
+        MergedResourceWriter writer = getConsumer(folder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         // load the result as a set.
@@ -1401,7 +1399,7 @@
 
         File folder = Files.createTempDir();
 
-        MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false, false, null);
+        MergedResourceWriter writer = getConsumer(folder);
         resourceMerger.mergeData(writer, false /*doCleanUp*/);
 
         return folder;
@@ -1529,7 +1527,7 @@
         }
         try {
         merger.writeBlobTo(folder,
-                new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+                getConsumer());
         } catch (MergingException e) {
             File file = new File(folder, "merger.xml");
             assertEquals(file.getPath() + ": Error: (Permission denied)",
@@ -1544,7 +1542,7 @@
 
         File folder = Files.createTempDir();
         merger.writeBlobTo(folder,
-                new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+                getConsumer());
 
         // new merger to read the blob
         ResourceMerger loadedMerger = new ResourceMerger();
@@ -1564,7 +1562,9 @@
             file = file.getAbsoluteFile();
             assertEquals(
                     file.getPath() +
-                            ": Error: File-based resource names must start with a lowercase letter",
+                            ": Error: 'A' is not a valid file-based resource name character: "
+                            + "File-based resource names must contain only lowercase a-z, 0-9,"
+                            + " or underscore",
                     e.getMessage());
             return;
         }
@@ -1584,7 +1584,7 @@
             file = file.getAbsoluteFile();
             assertEquals(
                     file.getPath() +
-                            ": Error: File-based resource names must start with a lowercase letter",
+                            ": Error: The resource name must start with a letter",
                     e.getMessage());
             return;
         }
@@ -1603,14 +1603,12 @@
             resourceMerger.addDataSet(resourceSet);
 
 
-            File folder = Files.createTempDir();
-            MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false,
-                    false, null);
+            MergedResourceWriter writer = getConsumer();
             resourceMerger.mergeData(writer, false /*doCleanUp*/);
         } catch (MergingException e) {
             File file = new File(root, "values" + File.separator + "dimens.xml");
             file = file.getAbsoluteFile();
-            assertEquals(file.getPath() + ":3:5: Error: The content of elements must consist "
+            assertEquals(file.getPath() + ":4:6: Error: The content of elements must consist "
                     + "of well-formed character data or markup.",
                     e.getMessage());
             return;
@@ -1630,14 +1628,12 @@
             resourceMerger.addDataSet(resourceSet);
 
 
-            File folder = Files.createTempDir();
-            MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false,
-                    false, null);
+            MergedResourceWriter writer = getConsumer();
             resourceMerger.mergeData(writer, false /*doCleanUp*/);
         } catch (MergingException e) {
             File file = new File(root, "values" + File.separator + "dimens.xml");
             file = file.getAbsoluteFile();
-            assertEquals(file.getPath() + ":1:16: Error: Open quote is expected for "
+            assertEquals(file.getPath() + ":2:17: Error: Open quote is expected for "
                     + "attribute \"{1}\" associated with an  element type  \"name\".",
                     e.getMessage());
             return;
@@ -1689,4 +1685,20 @@
             return item.getIgnoredFromDiskMerge();
         }
     }
+
+    @NonNull
+    private MergedResourceWriter getConsumer() {
+        return getConsumer(Files.createTempDir());
+    }
+
+    @NonNull
+    private MergedResourceWriter getConsumer(File tempDir) {
+        return new MergedResourceWriter(
+                tempDir,
+                mPngCruncher,
+                false,
+                false,
+                null,
+                mPreprocessor);
+    }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java
index 6166830..f64e33c 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/ResourceSetTest.java
@@ -112,16 +112,18 @@
             set.loadFromFiles(logger);
         } catch (DuplicateDataException e) {
             gotException = true;
+
+
+
+
             String message = e.getMessage();
             // Clean up paths etc for unit test
             int index = message.indexOf("dupSet");
             assertTrue(index != -1);
             String prefix = message.substring(0, index);
-            message = message.replaceAll(prefix, "<PREFIX>").replace('\\','/');
-            assertEquals("<PREFIX>dupSet/res2/drawable/icon.png\t<PREFIX>dupSet/res1/drawable/icon.png: "
-                    + "Error: Duplicate resources: "
-                    + "<PREFIX>dupSet/res2/drawable/icon.png:drawable/icon, "
-                    + "<PREFIX>dupSet/res1/drawable/icon.png:drawable/icon", message);
+            message = message.replace(prefix, "<PREFIX>").replace('\\','/');
+            assertEquals("<PREFIX>dupSet/res1/drawable/icon.png\t<PREFIX>dupSet/res2/drawable/icon.png: "
+                    + "Error: Duplicate resources", message);
         }
 
         checkLogger(logger);
@@ -141,7 +143,7 @@
         } catch (MergingException e) {
             gotException = true;
             assertEquals(new File(root, "values" + separator + "dimens.xml").getAbsolutePath() +
-                    ":0:0: Error: Content is not allowed in prolog.",
+                    ":1:1: Error: Content is not allowed in prolog.",
                     e.getMessage());
         }
 
@@ -204,7 +206,7 @@
         } catch (MergingException e) {
             gotException = true;
             assertEquals(new File(root, "values" + separator + "values.xml").getAbsolutePath() +
-                    ":6:5: Error: The element type \"declare-styleable\" "
+                    ":7:6: Error: The element type \"declare-styleable\" "
                     + "must be terminated by the matching end-tag \"</declare-styleable>\".",
                     e.getMessage());
         }
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java
index 040c198..f7df995 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java
@@ -68,6 +68,17 @@
         assertEquals("German", LocaleManager.getLanguageName("de"));
         assertEquals("Hindi", LocaleManager.getLanguageName("hi"));
 
+        // Check deprecated codes
+        assertEquals("Indonesian", LocaleManager.getLanguageName("id"));
+        assertEquals("Indonesian", LocaleManager.getLanguageName("in"));
+        assertEquals("Indonesian", LocaleManager.getLanguageName("ind"));
+        assertEquals("Hebrew", LocaleManager.getLanguageName("he"));
+        assertEquals("Hebrew", LocaleManager.getLanguageName("iw"));
+        assertEquals("Hebrew", LocaleManager.getLanguageName("heb"));
+        assertEquals("Yiddish", LocaleManager.getLanguageName("yi"));
+        assertEquals("Yiddish", LocaleManager.getLanguageName("ji"));
+        assertEquals("Yiddish", LocaleManager.getLanguageName("yid"));
+
         // 3 letter language lookup
         assertEquals("English", LocaleManager.getLanguageName("eng"));
         assertEquals("Norwegian", LocaleManager.getLanguageName("nor"));
@@ -175,6 +186,17 @@
         assertEquals("nb", LocaleManager.getLanguageAlpha2("nob"));
         assertEquals("NOR", LocaleManager.getRegionAlpha3("NO"));
         assertEquals("NO", LocaleManager.getRegionAlpha2("NOR"));
+
+        // Check deprecated codes
+        assertEquals("in", LocaleManager.getLanguageAlpha2("ind"));
+        assertEquals("ind", LocaleManager.getLanguageAlpha3("in"));
+        assertEquals("ind", LocaleManager.getLanguageAlpha3("id"));
+        assertEquals("iw", LocaleManager.getLanguageAlpha2("heb"));
+        assertEquals("heb", LocaleManager.getLanguageAlpha3("he"));
+        assertEquals("heb", LocaleManager.getLanguageAlpha3("iw"));
+        assertEquals("ji", LocaleManager.getLanguageAlpha2("yid"));
+        assertEquals("yid", LocaleManager.getLanguageAlpha3("yi"));
+        assertEquals("yid", LocaleManager.getLanguageAlpha3("ji"));
     }
 
     @SuppressWarnings("ConstantConditions")
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java
index 9270fc7..6bb4a49 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java
@@ -17,9 +17,9 @@
 package com.android.ide.common.resources;
 
 import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ArrayResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.res2.MergingException;
 import com.android.ide.common.res2.ResourceRepository;
 import com.android.ide.common.resources.configuration.FolderConfiguration;
 import com.android.resources.ResourceType;
@@ -39,6 +39,11 @@
                         + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                         + "<resources>\n"
                         + "    <string name=\"ok\">Ok</string>\n"
+                        + "    <array name=\"my_fw_array\">\"\n"
+                        + "        <item>  fw_value1</item>\n"   // also test trimming.
+                        + "        <item>fw_value2\n</item>\n"
+                        + "        <item>fw_value3</item>\n"
+                        + "    </array>\n"
                         + "</resources>\n",
 
                         "values/themes.xml", ""
@@ -105,7 +110,12 @@
                         + "    <string name=\"menu_settings\">Settings</string>\n"
                         + "    <string name=\"dummy\" translatable=\"false\">Ignore Me</string>\n"
                         + "    <string name=\"wallpaper_instructions\">Tap picture to set portrait wallpaper</string>\n"
-                        + "    <string name=\"xliff_string\">First: <xliff:g id=\"firstName\">%1$s</xliff:g> Last: <xliff:g id=\"lastName\">%2$s</xliff:g></string>"
+                        + "    <string name=\"xliff_string\">First: <xliff:g id=\"firstName\">%1$s</xliff:g> Last: <xliff:g id=\"lastName\">%2$s</xliff:g></string>\n"
+                        + "    <array name=\"my_array\">\"\n"
+                        + "        <item>  value1</item>\n"   // also test trimming.
+                        + "        <item>value2\n</item>\n"
+                        + "        <item>value3</item>\n"
+                        + "    </array>\n"
                         + "</resources>\n",
 
                         "values-es/strings.xml", ""
@@ -199,6 +209,20 @@
         assertEquals("#ffffffff", resolver.resolveResValue(
                 resolver.findResValue("@android:color/bright_foreground_dark", false)).getValue());
 
+        // Test array values.
+        ResourceValue resValue = resolver.findResValue("@array/my_array", false);
+        assertTrue(resValue instanceof ArrayResourceValue);
+        assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+        assertEquals("value1", ((ArrayResourceValue) resValue).getElement(0));
+        assertEquals("value2", ((ArrayResourceValue) resValue).getElement(1));
+        assertEquals("value3", ((ArrayResourceValue) resValue).getElement(2));
+        resValue = resolver.findResValue("@android:array/my_fw_array", false);
+        assertTrue(resValue instanceof ArrayResourceValue);
+        assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+        assertEquals("fw_value1", ((ArrayResourceValue) resValue).getElement(0));
+        assertEquals("fw_value2", ((ArrayResourceValue) resValue).getElement(1));
+        assertEquals("fw_value3", ((ArrayResourceValue) resValue).getElement(2));
+
 
         // Now do everything over again, but this time without a resource resolver.
         // Also set a lookup chain.
@@ -251,8 +275,19 @@
                 + "@android:color/background_dark => #ff000000",
                 ResourceItemResolver.getDisplayString("?foo", chain));
 
-        assertEquals("?foo => ?android:colorForeground => @color/bright_foreground_light => "
-                + "@android:color/background_dark => #ff000000",
-                ResourceItemResolver.getDisplayString("?foo", chain));
+        // Test array values.
+        resValue = resolver.findResValue("@array/my_array", false);
+        assertTrue(resValue instanceof ArrayResourceValue);
+        assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+        assertEquals("value1", ((ArrayResourceValue) resValue).getElement(0));
+        assertEquals("value2", ((ArrayResourceValue) resValue).getElement(1));
+        assertEquals("value3", ((ArrayResourceValue) resValue).getElement(2));
+        resValue = resolver.findResValue("@android:array/my_fw_array", false);
+        assertTrue(resValue instanceof ArrayResourceValue);
+        assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+        assertEquals("fw_value1", ((ArrayResourceValue) resValue).getElement(0));
+        assertEquals("fw_value2", ((ArrayResourceValue) resValue).getElement(1));
+        assertEquals("fw_value3", ((ArrayResourceValue) resValue).getElement(2));
+
     }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java
index f61b679..78beca2 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceResolverTest.java
@@ -8,8 +8,6 @@
 import com.android.resources.Density;
 import com.android.resources.ResourceType;
 import com.google.common.collect.Lists;
-
-import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 import java.io.IOException;
@@ -86,6 +84,10 @@
                         + "    </style>"
                         + "    <style name=\"RandomStyle2\" parent=\"RandomStyle\">\n"
                         + "    </style>"
+                        + "    <style name=\"Theme.FakeTheme\" parent=\"\">\n"
+                        + "    </style>"
+                        + "    <style name=\"Theme\" parent=\"RandomStyle\">\n"
+                        + "    </style>"
                         + "</resources>\n",
 
                         "values/strings.xml", ""
@@ -199,6 +201,8 @@
         // isTheme
         assertFalse(resolver.isTheme(resolver.findResValue("@style/RandomStyle", false), null));
         assertFalse(resolver.isTheme(resolver.findResValue("@style/RandomStyle2", false), null));
+        assertFalse(resolver.isTheme(resolver.findResValue("@style/Theme.FakeTheme", false), null));
+        assertFalse(resolver.isTheme(resolver.findResValue("@style/Theme", false), null));
         //    check XML escaping in value resources
         StyleResourceValue randomStyle = (StyleResourceValue) resolver.findResValue(
                 "@style/RandomStyle", false);
@@ -259,7 +263,7 @@
         try {
             val = resolver.resolveValue(ResourceType.STRING, "bright_foreground_dark",
                     "@color/background_light", false);
-        } catch (AssertionFailedError expected) {
+        } catch (AssertionError expected) {
             failed = true;
         }
         assertTrue("incorrect resource returned: " + val, failed);
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java
index 426390c..55739d7 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java
@@ -67,6 +67,10 @@
         assertEquals("foo", ResourceUrl.parse("?android:foo").name);
         assertTrue(ResourceUrl.parse("?android:foo").framework);
         assertTrue(ResourceUrl.parse("?android:foo").theme);
+        assertFalse(ResourceUrl.parse("?foo", false).framework);
+        assertTrue(ResourceUrl.parse("?android:foo", false).framework);
+        assertTrue(ResourceUrl.parse("?foo", true).framework);
+        assertTrue(ResourceUrl.parse("?attr/foo", true).framework);
 
         assertEquals("@+id/foo", ResourceUrl.parse("@+id/foo").toString());
         assertEquals("@layout/foo", ResourceUrl.parse("@layout/foo").toString());
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java b/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java
index 492ea1c..60e9ee6 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java
@@ -1,8 +1,8 @@
 package com.android.ide.common.resources;
 
 import static com.android.SdkConstants.FD_RES;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import com.android.annotations.NonNull;
 import com.android.ide.common.res2.MergingException;
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
index 07f42aa..bb29ac7 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/FolderConfigurationTest.java
@@ -18,9 +18,11 @@
 
 import com.android.ide.common.res2.ResourceFile;
 import com.android.ide.common.res2.ResourceItem;
+import com.android.resources.Density;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
 import com.android.resources.UiMode;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
@@ -228,6 +230,26 @@
 
     }
 
+    public void testIsRoundMatch() {
+        FolderConfiguration configForFolder = FolderConfiguration
+                .getConfigForFolder("values-en-round");
+        assertNotNull(configForFolder);
+        assertNotNull(configForFolder.getScreenRoundQualifier());
+        assertEquals(ScreenRound.ROUND, configForFolder.getScreenRoundQualifier().getValue());
+        runConfigMatchTest("en-rgb-Round-Port-HDPI-notouch-12key", 4,
+                "",
+                "en",
+                "fr-rCa",
+                "en-notround-hdpi",
+                "en-notouch");
+
+        runConfigMatchTest("en-rgb-Round-Port-HDPI-notouch-12key", 2,
+                "",
+                "en",
+                "en-round-hdpi",
+                "port-12key");
+    }
+
     // --- helper methods
 
     private static final class MockConfigurable implements Configurable {
@@ -427,4 +449,50 @@
                 configBcpEn.findMatchingConfigurables(
                         Arrays.asList(itemBlank, itemBcpEn, itemEn, itemDe)));
     }
+
+    public void testFromQualifierString() throws Exception {
+        FolderConfiguration blankFolder = FolderConfiguration.getConfigForQualifierString("");
+        FolderConfiguration enFolder = FolderConfiguration.getConfigForQualifierString("en");
+        FolderConfiguration deFolder = FolderConfiguration.getConfigForQualifierString("de");
+        FolderConfiguration deBcp47Folder = FolderConfiguration.getConfigForQualifierString("b+de");
+        FolderConfiguration twoQualifiersFolder =
+                FolderConfiguration.getConfigForQualifierString("de-hdpi");
+
+        assertNotNull(enFolder);
+        assertNotNull(deFolder);
+        assertNotNull(deBcp47Folder);
+        assertFalse(enFolder.isMatchFor(deFolder));
+        assertFalse(deFolder.isMatchFor(enFolder));
+        assertFalse(enFolder.isMatchFor(deBcp47Folder));
+        assertFalse(deBcp47Folder.isMatchFor(enFolder));
+
+        assertTrue(enFolder.isMatchFor(blankFolder));
+        assertTrue(deFolder.isMatchFor(blankFolder));
+        assertTrue(deBcp47Folder.isMatchFor(blankFolder));
+
+        assertEquals("de", twoQualifiersFolder.getLocaleQualifier().getLanguage());
+        assertEquals(Density.HIGH, twoQualifiersFolder.getDensityQualifier().getValue());
+    }
+
+    public void testCopyOf() throws Exception {
+        FolderConfiguration deBcp47Folder = FolderConfiguration.getConfigForFolder("values-b+de");
+        FolderConfiguration copy = FolderConfiguration.copyOf(deBcp47Folder);
+        assertTrue(copy.isMatchFor(deBcp47Folder));
+
+        copy.setLocaleQualifier(new LocaleQualifier("en"));
+        assertEquals("en", copy.getLocaleQualifier().getLanguage());
+        assertEquals("de", deBcp47Folder.getLocaleQualifier().getLanguage());
+
+        copy.setDensityQualifier(new DensityQualifier(Density.HIGH));
+        assertEquals(Density.HIGH, copy.getDensityQualifier().getValue());
+        assertNull(deBcp47Folder.getDensityQualifier());
+
+        FolderConfiguration blankFolder = FolderConfiguration.getConfigForFolder("values");
+        copy = FolderConfiguration.copyOf(blankFolder);
+        assertTrue(copy.isMatchFor(blankFolder));
+
+        copy.setVersionQualifier(new VersionQualifier(21));
+        assertEquals(21, copy.getVersionQualifier().getVersion());
+        assertNull(blankFolder.getVersionQualifier());
+    }
 }
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
index 3aac5f3..f74fe9d 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
@@ -46,7 +46,7 @@
         assertEquals("port", config.getScreenOrientationQualifier().toString()); //$NON-NLS-1$
     }
 
-    public void testLanscape() {
+    public void testLandscape() {
         assertEquals(true, soq.checkAndSet("land", config)); //$NON-NLS-1$
         assertTrue(config.getScreenOrientationQualifier() != null);
         assertEquals(ScreenOrientation.LANDSCAPE,
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenRoundQualifierTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenRoundQualifierTest.java
new file mode 100644
index 0000000..c70ae9a
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenRoundQualifierTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.ide.common.resources.configuration;
+
+import com.android.resources.ScreenRound;
+
+import junit.framework.TestCase;
+
+public class ScreenRoundQualifierTest extends TestCase {
+
+    private ScreenRoundQualifier soq;
+    private FolderConfiguration config;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        soq = new ScreenRoundQualifier();
+        config = new FolderConfiguration();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        soq = null;
+        config = null;
+    }
+
+    public void testRound() {
+        assertEquals(true, soq.checkAndSet("round", config));
+        assertTrue(config.getScreenRoundQualifier() != null);
+        assertEquals(ScreenRound.ROUND, config.getScreenRoundQualifier().getValue());
+        assertEquals("round", config.getScreenRoundQualifier().toString());
+    }
+
+    public void testNotRound() {
+        assertEquals(true, soq.checkAndSet("notround", config));
+        assertTrue(config.getScreenRoundQualifier() != null);
+        assertEquals(ScreenRound.NOTROUND,
+                config.getScreenRoundQualifier().getValue());
+        assertEquals("notround", config.getScreenRoundQualifier().toString());
+    }
+
+    public void testFailures() {
+        assertEquals(false, soq.checkAndSet("", config));
+        assertEquals(false, soq.checkAndSet("ROUND", config));
+        assertEquals(false, soq.checkAndSet("not-round", config));
+    }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/util/GeneratorTest.java b/sdk-common/src/test/java/com/android/ide/common/util/GeneratorTest.java
new file mode 100644
index 0000000..14c7f76
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/util/GeneratorTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 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.ide.common.util;
+
+import junit.framework.TestCase;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Shared test infrastructure for asset (either bitmap or vector) generator.
+ */
+public abstract class GeneratorTest extends TestCase {
+  protected abstract String getTestDataRelPath();
+
+  protected String generateGoldenImage(File targetDir,
+                                       BufferedImage goldenImage,
+                                       String missingFilePath,
+                                       String filePath) throws IOException {
+    if (targetDir == null) {
+      fail(
+        "Did not find " + missingFilePath + ". Set $ANDROID_SRC to have it created automatically");
+    }
+    File fileName = new File(targetDir, filePath);
+    assertFalse(fileName.exists());
+    if (!fileName.getParentFile().exists()) {
+      boolean mkdir = fileName.getParentFile().mkdirs();
+      assertTrue(fileName.getParent(), mkdir);
+    }
+
+    ImageIO.write(goldenImage, "PNG", fileName);
+    return fileName.getPath();
+  }
+
+  public static void assertImageSimilar(String imageName,
+                                        BufferedImage goldenImage,
+                                        BufferedImage image,
+                                        float maxPercentDifferent) throws IOException {
+    assertTrue("Widths differ too much for " + imageName,
+               Math.abs(goldenImage.getWidth() - image.getWidth()) < 2);
+    assertTrue("Widths differ too much for " + imageName,
+               Math.abs(goldenImage.getHeight() - image.getHeight()) < 2);
+
+    assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
+
+    if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
+      BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
+                                             BufferedImage.TYPE_INT_ARGB);
+      temp.getGraphics().drawImage(goldenImage, 0, 0, null);
+      goldenImage = temp;
+    }
+    assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
+
+    int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
+    int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
+
+    // Blur the images to account for the scenarios where there are pixel
+    // differences
+    // in where a sharp edge occurs
+    // goldenImage = blur(goldenImage, 6);
+    // image = blur(image, 6);
+
+    int width = 3 * imageWidth;
+    int height = imageHeight;
+    BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+    Graphics g = deltaImage.getGraphics();
+
+    // Compute delta map
+    long delta = 0;
+    for (int y = 0; y < imageHeight; y++) {
+      for (int x = 0; x < imageWidth; x++) {
+        int goldenRgb = goldenImage.getRGB(x, y);
+        int rgb = image.getRGB(x, y);
+        if (goldenRgb == rgb) {
+          deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+          continue;
+        }
+
+        // If the pixels have no opacity, don't delta colors at all
+        if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
+          deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+          continue;
+        }
+
+        int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
+        int newR = 128 + deltaR & 0xFF;
+        int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
+        int newG = 128 + deltaG & 0xFF;
+        int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
+        int newB = 128 + deltaB & 0xFF;
+
+        int avgAlpha =
+          ((((goldenRgb & 0xFF000000) >>> 24) + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
+
+        int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
+        deltaImage.setRGB(imageWidth + x, y, newRGB);
+
+        delta += Math.abs(deltaR);
+        delta += Math.abs(deltaG);
+        delta += Math.abs(deltaB);
+      }
+    }
+
+    // 3 different colors, 256 color levels
+    long total = imageHeight * imageWidth * 3L * 256L;
+    float percentDifference = (float)(delta * 100 / (double)total);
+
+    if (percentDifference > maxPercentDifferent) {
+      // Expected on the left
+      // Golden on the right
+      g.drawImage(goldenImage, 0, 0, null);
+      g.drawImage(image, 2 * imageWidth, 0, null);
+
+      // Labels
+      if (imageWidth > 80) {
+        g.setColor(Color.RED);
+        g.drawString("Expected", 10, 20);
+        g.drawString("Actual", 2 * imageWidth + 10, 20);
+      }
+
+      File output = new File(getTempDir(), "delta-" + imageName.replace(File.separatorChar, '_'));
+      if (output.exists()) {
+        output.delete();
+      }
+      ImageIO.write(deltaImage, "PNG", output);
+      String message =
+        String.format("Images differ (by %.1f%%) - see details in %s", percentDifference, output);
+      System.out.println(message);
+      fail(message);
+    }
+
+    g.dispose();
+  }
+
+  protected static File getTempDir() {
+    if (System.getProperty("os.name").equals("Mac OS X")) {
+      return new File("/tmp"); //$NON-NLS-1$
+    }
+
+    return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+  }
+
+  /**
+   * Get the location to write missing golden files to
+   */
+  protected File getTargetDir() {
+    // Set $ANDROID_SRC to point to your git AOSP working tree
+    String sdk = System.getenv("ANDROID_SRC");
+    if (sdk != null) {
+      File root = new File(sdk);
+      if (root.exists()) {
+        File testData = new File(root, getTestDataRelPath().replace('/', File.separatorChar));
+        if (testData.exists()) {
+          return testData;
+        }
+      }
+    }
+
+    return null;
+  }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/vectordrawable/VectorDrawbleGeneratorTest.java b/sdk-common/src/test/java/com/android/ide/common/vectordrawable/VectorDrawbleGeneratorTest.java
new file mode 100644
index 0000000..77664f7
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/vectordrawable/VectorDrawbleGeneratorTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 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.ide.common.vectordrawable;
+
+import com.android.ide.common.util.GeneratorTest;
+import com.android.testutils.TestUtils;
+import junit.framework.TestCase;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+
+@SuppressWarnings("javadoc")
+public class VectorDrawbleGeneratorTest extends GeneratorTest {
+    private static final String TEST_DATA_REL_PATH =
+      "tools/base/sdk-common/src/test/resources/testData/vectordrawable";
+
+    @Override
+    protected String getTestDataRelPath() {
+        return TEST_DATA_REL_PATH;
+    }
+
+    private void checkVectorConversion(String testFileName) throws IOException {
+        String imageName = testFileName + ".png";
+        String svgName = testFileName + ".svg";
+
+        String parentDir =  "vectordrawable" + File.separator;
+        File parentDirFile = TestUtils.getRoot("vectordrawable");
+
+        File svgFile = new File(parentDirFile, svgName);
+        OutputStream outStream = new ByteArrayOutputStream();
+        try {
+            Svg2Vector.parseSvgToXml(svgFile, outStream);
+        }
+        catch (Exception e) {
+            TestCase.assertTrue("Failure: Exception in Svg2Vector.parseSvgToXml!", false);
+        }
+
+        final VdPreview.TargetSize imageTargetSize = VdPreview.TargetSize.createSizeFromWidth(24);
+        StringBuilder builder = new StringBuilder();
+        BufferedImage image = VdPreview.getPreviewFromVectorXml(imageTargetSize, outStream.toString(), builder);
+
+        String pngPath = parentDir + imageName;
+        File pngFile = new File(parentDirFile, imageName);
+        InputStream is = new FileInputStream(pngFile);
+        if (is == null) {
+            // Generate golden images here.
+            generateGoldenImage(getTargetDir(), image, pngPath, parentDir + imageName);
+        } else {
+            BufferedImage goldenImage = ImageIO.read(is);
+            assertImageSimilar(pngPath, goldenImage, image, 1.0f);
+        }
+    }
+
+    //public void testControlPoints01() throws Exception {
+    //    checkVectorConversion("test_control_points_01");
+    //}
+    //
+    //public void testControlPoints02() throws Exception {
+    //    checkVectorConversion("test_control_points_02");
+    //}
+
+    public void testControlPoints03() throws Exception {
+        checkVectorConversion("test_control_points_03");
+    }
+
+    public void testIconContentCut() throws Exception {
+        checkVectorConversion("ic_content_cut_24px");
+    }
+
+    public void testIconInput() throws Exception {
+        checkVectorConversion("ic_input_24px");
+    }
+
+    public void testIconLiveHelp() throws Exception {
+        checkVectorConversion("ic_live_help_24px");
+    }
+
+    public void testIconLocalLibrary() throws Exception {
+        checkVectorConversion("ic_local_library_24px");
+    }
+
+    public void testIconLocalPhone() throws Exception {
+        checkVectorConversion("ic_local_phone_24px");
+    }
+
+    public void testIconMicOff() throws Exception {
+        checkVectorConversion("ic_mic_off_24px");
+    }
+
+    public void testShapes() throws Exception {
+        checkVectorConversion("ic_shapes");
+    }
+
+    public void testIconTempHigh() throws Exception {
+        checkVectorConversion("ic_temp_high");
+    }
+
+    public void testIconPlusSign() throws Exception {
+        checkVectorConversion("ic_plus_sign");
+    }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java b/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java
index aa88fc7..ef48708 100644
--- a/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java
@@ -16,8 +16,6 @@
 
 package com.android.ide.common.xml;
 
-import com.android.ide.common.xml.AndroidManifestParser;
-import com.android.ide.common.xml.ManifestData;
 import com.android.ide.common.xml.ManifestData.SupportsScreens;
 
 import java.io.InputStream;
diff --git a/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java b/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java
index 730fbae..d30e215 100644
--- a/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java
@@ -915,7 +915,7 @@
         Node text = doc.createTextNode("  This is my text  ");
         child3.appendChild(text);
 
-        String xml = XmlUtils.toXml(doc, true);
+        String xml = XmlUtils.toXml(doc);
         assertEquals(
                 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                         "<myroot baz=\"baz\" foo=\"bar\"><mychild/><hasComment><!--This is my comment--></hasComment><hasText>  This is my text  </hasText></myroot>",
@@ -958,7 +958,7 @@
         Document doc = parse(xml);
         assertNotNull(doc);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<resources>\n"
@@ -994,7 +994,7 @@
         Document doc = parse(xml);
         assertNotNull(doc);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(xml, formatted);
 
         xml = XmlPrettyPrinter.prettyPrint(doc, false);
@@ -1022,7 +1022,7 @@
         Document doc = parse(xml);
         assertNotNull(doc);
 
-        String formatted = XmlUtils.toXml(doc, true);
+        String formatted = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<resources>\n"
@@ -1058,7 +1058,7 @@
         Document doc = parse(xml);
         assertNotNull(doc);
 
-        xml = XmlUtils.toXml(doc, true);
+        xml = XmlUtils.toXml(doc);
         assertEquals(""
                 + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<!-- ============== --><!-- Generic styles --><!-- ============== --><root/>",
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.png
new file mode 100644
index 0000000..a92c02a
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.svg
new file mode 100644
index 0000000..b89a0d0
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.png
new file mode 100644
index 0000000..2a564a2
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.svg
new file mode 100644
index 0000000..64d1d0b
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.png
new file mode 100644
index 0000000..081843c
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.svg
new file mode 100644
index 0000000..abe3129
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 16h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 11.9 13 12.5 13 14h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.png
new file mode 100644
index 0000000..674628d
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.svg
new file mode 100644
index 0000000..5fa5109
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.png
new file mode 100644
index 0000000..58ea1c7
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.svg
new file mode 100644
index 0000000..5908b6a
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.png
new file mode 100644
index 0000000..a188c50
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.svg
new file mode 100644
index 0000000..4c27fb5
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.png
new file mode 100644
index 0000000..f760018
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.svg
new file mode 100644
index 0000000..3e2ea1f
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.svg
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.png
new file mode 100644
index 0000000..e97567b
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.svg
new file mode 100644
index 0000000..c834caa
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.svg
@@ -0,0 +1,12 @@
+<svg width="36" height="36" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+  <g>
+    <g id="svg_1">
+      <polygon points="16.41,24.832 17.504,26.004 24,20 17.504,14 16.41,15.168 19.496,18 2,18 2,22 19.5,22" id="svg_2"/>
+    </g>
+    <g id="svg_3">
+      <rect id="svg_5" opacity="0.5" fill="#FF0000" height="12" width="12"/>
+      <circle id="svg_6" opacity="0.5" fill="#00FF00" r="10" cy="12" cx="12"/>
+      <line id="svg_8" stroke="#0000FF" stroke-width="2" y2="12" x2="24" y1="0" x1="0"/>
+    </g>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.png
new file mode 100644
index 0000000..5a32de5
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.svg
new file mode 100644
index 0000000..0ad5628
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.svg
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.png b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.png
new file mode 100644
index 0000000..50c19f1
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.svg b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.svg
new file mode 100644
index 0000000..d4813aa
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.svg
@@ -0,0 +1,7 @@
+<svg width="800" height="800.0000000000001" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+  <title>Layer 1</title>
+  <path d="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299" id="svg_1" stroke="#FF0000" fill="none"/>
+ </g>
+</svg>
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.png b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.png
new file mode 100644
index 0000000..96b2f81
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.svg b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.svg
new file mode 100644
index 0000000..0ba747e
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.svg
@@ -0,0 +1,7 @@
+<svg width="800" height="800.0000000000001" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+  <title>Layer 1</title>
+  <path d="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299" id="svg_1" stroke="#FF0000" fill="none"/>
+ </g>
+</svg>
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.png b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.png
new file mode 100644
index 0000000..940fa57
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.svg b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.svg
new file mode 100644
index 0000000..c8e1d47
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.svg
@@ -0,0 +1,7 @@
+<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+  <title>Layer 1</title>
+  <path d="m0,200q200,250 200,0t200,0Q500,400,600,200T800,200" id="svg_1" stroke="#000000" fill="#000000"/>
+ </g>
+</svg>
diff --git a/sdklib/build.gradle b/sdklib/build.gradle
index 45a3cf6..bc76ff4 100644
--- a/sdklib/build.gradle
+++ b/sdklib/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
-    repositories {
-        maven { url = uri(rootProject.cloneArtifacts.repository) }
-    }
-    dependencies {
-        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
-    }
-}
-
 apply plugin: 'java'
+apply plugin: 'jacoco'
 apply plugin: 'sdk-java-lib'
 
 evaluationDependsOn(':base:dvlib')
@@ -20,6 +12,7 @@
     compile project(':base:layoutlib-api')
     compile project(':base:dvlib')
 
+    compile 'com.google.code.gson:gson:2.2.4'
     compile 'org.apache.commons:commons-compress:1.8.1'
     compile 'org.apache.httpcomponents:httpclient:4.1.1'
     compile 'org.apache.httpcomponents:httpmime:4.1'
diff --git a/sdklib/sdklib-base.iml b/sdklib/sdklib-base.iml
index 5f22ace..9a3eb80 100644
--- a/sdklib/sdklib-base.iml
+++ b/sdklib/sdklib-base.iml
@@ -16,5 +16,6 @@
     <orderEntry type="library" exported="" name="http-client" level="project" />
     <orderEntry type="library" exported="" name="commons-compress" level="project" />
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="library" exported="" name="gson" level="project" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/sdklib/sdklib.iml b/sdklib/sdklib.iml
index e868d2c..9240161 100644
--- a/sdklib/sdklib.iml
+++ b/sdklib/sdklib.iml
@@ -16,5 +16,6 @@
     <orderEntry type="library" exported="" name="http-client" level="project" />
     <orderEntry type="library" exported="" name="commons-compress" level="project" />
     <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+    <orderEntry type="library" exported="" name="gson" level="project" />
   </component>
-</module>
+</module>
\ No newline at end of file
diff --git a/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java b/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java
index 0d5d653..e4520be 100755
--- a/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java
+++ b/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java
@@ -89,7 +89,7 @@
                     return new AndroidVersion(api, suffix);
                 }
             }
-        } else if (hashString.length() > 0 && Character.isDigit(hashString.charAt(0))) {
+        } else if (!hashString.isEmpty() && Character.isDigit(hashString.charAt(0))) {
             // For convenience, interpret a single integer as the proper "android-NN" form.
             try {
                 int api = Integer.parseInt(hashString);
diff --git a/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java b/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java
index 349c5df..6606bc8 100644
--- a/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java
+++ b/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java
@@ -80,15 +80,18 @@
     int NO_USB_ID = 0;
 
     /** An optional library provided by an Android Target */
-    interface IOptionalLibrary {
+    interface OptionalLibrary {
         /** The name of the library, as used in the manifest (&lt;uses-library&gt;). */
+        @NonNull
         String getName();
-        /** The file name of the jar file. */
-        String getJarName();
-        /** Absolute OS path to the jar file. */
-        String getJarPath();
+        /** Location of the jar file. */
+        @NonNull
+        File getJar();
         /** Description of the library. */
+        @NonNull
         String getDescription();
+        /** Whether the library requires a manifest entry */
+        boolean isManifestEntryRequired();
     }
 
     /**
@@ -187,6 +190,32 @@
     List<String> getBootClasspath();
 
     /**
+     * Returns a list of optional libraries for this target.
+     *
+     * These libraries are not automatically added to the classpath.
+     * Using them requires adding a <code>uses-library</code> entry in the manifest.
+     *
+     * @return a list of libraries.
+     *
+     * @see OptionalLibrary#getName()
+     */
+    @NonNull
+    List<OptionalLibrary> getOptionalLibraries();
+
+    /**
+     * Returns the additional libraries for this target.
+     *
+     * These libraries are automatically added to the classpath, but using them requires
+     * adding a <code>uses-library</code> entry in the manifest.
+     *
+     * @return a list of libraries.
+     *
+     * @see OptionalLibrary#getName()
+     */
+    @NonNull
+    List<OptionalLibrary> getAdditionalLibraries();
+
+    /**
      * Returns whether the target is able to render layouts.
      */
     boolean hasRenderingLibrary();
@@ -215,12 +244,6 @@
     File getDefaultSkin();
 
     /**
-     * Returns the available optional libraries for this target.
-     * @return an array of optional libraries or <code>null</code> if there is none.
-     */
-    IOptionalLibrary[] getOptionalLibraries();
-
-    /**
      * Returns the list of libraries available for a given platform.
      *
      * @return an array of libraries provided by the platform or <code>null</code> if there is none.
diff --git a/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java b/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java
index aefb450..cfedbf1 100755
--- a/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java
@@ -30,7 +30,7 @@
      * release. This number is used as a baseline and any more recent platforms
      * found can be used to increase the highest known number.
      */
-    public static final int HIGHEST_KNOWN_API = 22;
+    public static final int HIGHEST_KNOWN_API = 23;
 
     /**
      * Like {@link #HIGHEST_KNOWN_API} but does not include preview platforms
@@ -90,6 +90,7 @@
             case 20: return "4.4";
             case 21: return "5.0";
             case 22: return "5.1";
+            case 23: return "5.X";
             // If you add more versions here, also update #getBuildCodes and
             // #HIGHEST_KNOWN_API
 
@@ -134,6 +135,9 @@
             case 21:
             case 22:
                 return "Lollipop";
+            case 23:
+                return "MNC";
+
             // If you add more versions here, also update #getBuildCodes and
             // #HIGHEST_KNOWN_API
 
@@ -176,6 +180,7 @@
             case 20: return "KITKAT_WATCH"; //$NON-NLS-1$
             case 21: return "LOLLIPOP"; //$NON-NLS-1$
             case 22: return "LOLLIPOP_MR1"; //$NON-NLS-1$
+            case 23: return "MNC"; //$NON-NLS-1$
             // If you add more versions here, also update #getAndroidName and
             // #HIGHEST_KNOWN_API
         }
diff --git a/sdklib/src/main/java/com/android/sdklib/SystemImage.java b/sdklib/src/main/java/com/android/sdklib/SystemImage.java
index f25ab57..ded928f 100755
--- a/sdklib/src/main/java/com/android/sdklib/SystemImage.java
+++ b/sdklib/src/main/java/com/android/sdklib/SystemImage.java
@@ -23,8 +23,10 @@
 import com.android.sdklib.internal.androidTarget.PlatformTarget;
 import com.android.sdklib.io.FileOp;
 import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.base.Objects;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.Locale;
 
 
@@ -37,6 +39,7 @@
     public static final IdDisplay DEFAULT_TAG = new IdDisplay("default",    //$NON-NLS-1$
                                                               "Default");   //$NON-NLS-1$
 
+    @Deprecated
     private final LocationType mLocationtype;
     private final IdDisplay mTag;
     private final IdDisplay mAddonVendor;
@@ -225,7 +228,10 @@
         return mLocation;
     }
 
-    /** Indicates the location strategy for this system image in the SDK. */
+    /**
+     * Indicates the location strategy for this system image in the SDK.
+     * @deprecated
+     */
     @NonNull
     @Override
     public LocationType getLocationType() {
@@ -299,5 +305,21 @@
         return sb.toString();
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof SystemImage)) {
+          return false;
+        }
+        SystemImage other = (SystemImage)o;
+        return mTag.equals(other.mTag) && mAbiType.equals(other.getAbiType()) && Objects.equal(mAddonVendor, other.mAddonVendor)
+               && mLocation.equals(other.mLocation) && Arrays.equals(mSkins, other.mSkins);
+    }
 
+    @Override
+    public int hashCode() {
+        int hashCode = Objects.hashCode(mTag, mAbiType, mAddonVendor, mLocation);
+        hashCode *= 37;
+        hashCode += Arrays.hashCode(mSkins);
+        return hashCode;
+    }
 }
diff --git a/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java b/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java
index e6f6329..92b19f6 100644
--- a/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java
+++ b/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java
@@ -120,7 +120,7 @@
                 }
             } while (index < args.length);
 
-            if (zipArchives.size() == 0) {
+            if (zipArchives.isEmpty()) {
                 printAndExit("No zip archive, there must be one for the resources");
             }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java b/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java
index 9e95f41..a618d47 100644
--- a/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java
+++ b/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java
@@ -209,7 +209,7 @@
         }
 
         // the actual clean up.
-        if (results.size() > 0) {
+        if (!results.isEmpty()) {
             for (File f : results) {
                 jarList.remove(f.getAbsolutePath());
             }
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/Device.java b/sdklib/src/main/java/com/android/sdklib/devices/Device.java
index 067b5e4..242a830 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/Device.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/Device.java
@@ -20,6 +20,7 @@
 import com.android.annotations.Nullable;
 import com.android.dvlib.DeviceSchema;
 import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
 
 import java.awt.Dimension;
 import java.util.ArrayList;
@@ -39,6 +40,7 @@
  * the {@link DeviceSchema} standards.
  */
 public final class Device {
+
     /** Name of the device */
     @NonNull
     private final String mName;
@@ -262,10 +264,25 @@
      *
      * @return the optional boot.props of the device. Can be null or empty.
      */
+    @NonNull
     public Map<String, String> getBootProps() {
         return mBootProps;
     }
 
+    /**
+     * A convenience method to get if the screen for this device is round.
+     */
+    public boolean isScreenRound() {
+        return getDefaultHardware().getScreen().getScreenRound() == ScreenRound.ROUND;
+    }
+
+    /**
+     * A convenience method to get the chin size for this device.
+     */
+    public int getChinSize() {
+        return getDefaultHardware().getScreen().getChin();
+    }
+
     public static class Builder {
         private String mName;
         private String mId;
@@ -347,6 +364,14 @@
             return false;
         }
 
+        /**
+         * Only for use by the {@link DeviceParser}, so that it can modify the states after they've
+         * been added.
+         */
+        List<State> getAllStates() {
+            return mState;
+        }
+
         public void setMeta(@NonNull Meta meta) {
             mMeta = meta;
         }
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java b/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java
index abdeeea..f2b1734 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java
@@ -30,6 +30,8 @@
 import com.android.sdklib.repository.PkgProps;
 import com.android.utils.ILogger;
 import com.google.common.base.Charsets;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
 import com.google.common.hash.HashFunction;
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
@@ -70,10 +72,10 @@
     private static final Pattern PATH_PROPERTY_PATTERN =
         Pattern.compile('^' + PkgProps.EXTRA_PATH + '=' + DEVICE_PROFILES_PROP + '$');
     private ILogger mLog;
-    private Collection<Device> mVendorDevices;
-    private Collection<Device> mSysImgDevices;
-    private Collection<Device> mUserDevices;
-    private Collection<Device> mDefaultDevices;
+    private Table<String, String, Device> mVendorDevices;
+    private Table<String, String, Device> mSysImgDevices;
+    private Table<String, String, Device> mUserDevices;
+    private Table<String, String, Device> mDefaultDevices;
     private final Object mLock = new Object();
     private final List<DevicesChangedListener> sListeners = new ArrayList<DevicesChangedListener>();
     private final String mOsSdkPath;
@@ -181,19 +183,19 @@
     @Nullable
     public Device getDevice(@NonNull String id, @NonNull String manufacturer) {
         initDevicesLists();
-        Device d = getDeviceImpl(mUserDevices, id, manufacturer);
+        Device d = mUserDevices.get(id, manufacturer);
         if (d != null) {
             return d;
         }
-        d = getDeviceImpl(mSysImgDevices, id, manufacturer);
+        d = mSysImgDevices.get(id, manufacturer);
         if (d != null) {
             return d;
         }
-        d = getDeviceImpl(mDefaultDevices, id, manufacturer);
+        d = mDefaultDevices.get(id, manufacturer);
         if (d != null) {
             return d;
         }
-        d = getDeviceImpl(mVendorDevices, id, manufacturer);
+        d = mVendorDevices.get(id, manufacturer);
         return d;
     }
 
@@ -230,20 +232,20 @@
     @NonNull
     public Collection<Device> getDevices(@NonNull EnumSet<DeviceFilter> deviceFilter) {
         initDevicesLists();
-        LinkedHashSet<Device> devices = new LinkedHashSet<Device>();
+        Table<String, String, Device> devices = HashBasedTable.create();
         if (mUserDevices != null && (deviceFilter.contains(DeviceFilter.USER))) {
-            devices.addAll(mUserDevices);
+            devices.putAll(mUserDevices);
         }
         if (mDefaultDevices != null && (deviceFilter.contains(DeviceFilter.DEFAULT))) {
-            devices.addAll(mDefaultDevices);
+            devices.putAll(mDefaultDevices);
         }
         if (mVendorDevices != null && (deviceFilter.contains(DeviceFilter.VENDOR))) {
-            devices.addAll(mVendorDevices);
+            devices.putAll(mVendorDevices);
         }
         if (mSysImgDevices != null && (deviceFilter.contains(DeviceFilter.SYSTEM_IMAGES))) {
-            devices.addAll(mSysImgDevices);
+            devices.putAll(mSysImgDevices);
         }
-        return Collections.unmodifiableSet(devices);
+        return Collections.unmodifiableCollection(devices.values());
     }
 
     private void initDevicesLists() {
@@ -265,25 +267,22 @@
             if (mDefaultDevices != null) {
                 return false;
             }
-            InputStream stream = null;
+            InputStream stream = DeviceManager.class
+                    .getResourceAsStream(SdkConstants.FN_DEVICES_XML);
             try {
-                stream = DeviceManager.class.getResourceAsStream(SdkConstants.FN_DEVICES_XML);
+                assert stream != null : SdkConstants.FN_DEVICES_XML + " not bundled in sdklib.";
                 mDefaultDevices = DeviceParser.parse(stream);
                 return true;
             } catch (IllegalStateException e) {
                 // The device builders can throw IllegalStateExceptions if
                 // build gets called before everything is properly setup
                 mLog.error(e, null);
-                mDefaultDevices = new LinkedHashSet<Device>();
+                mDefaultDevices = HashBasedTable.create();
             } catch (Exception e) {
                 mLog.error(e, "Error reading default devices");
-                mDefaultDevices = new LinkedHashSet<Device>();
+                mDefaultDevices = HashBasedTable.create();
             } finally {
-                if (stream != null) {
-                    try {
-                        stream.close();
-                    } catch (IOException ignore) {}
-                }
+                Closeables.closeQuietly(stream);
             }
         }
         return false;
@@ -300,49 +299,34 @@
                 return false;
             }
 
-            mVendorDevices = new LinkedHashSet<Device>();
+            mVendorDevices = HashBasedTable.create();
 
             // Load builtin devices
-            InputStream stream = null;
+            InputStream stream = DeviceManager.class.getResourceAsStream("nexus.xml");
             try {
-                stream = DeviceManager.class.getResourceAsStream("nexus.xml");
-                mVendorDevices.addAll(DeviceParser.parse(stream));
+                mVendorDevices.putAll(DeviceParser.parse(stream));
             } catch (Exception e) {
                 mLog.error(e, "Could not load nexus devices");
             } finally {
-                try {
-                    Closeables.close(stream, true /* swallowIOException */);
-                } catch (IOException e) {
-                    // Cannot happen
-                }
+                Closeables.closeQuietly(stream);
             }
 
-            stream = null;
+            stream = DeviceManager.class.getResourceAsStream("wear.xml");
             try {
-                stream = DeviceManager.class.getResourceAsStream("wear.xml");
-                mVendorDevices.addAll(DeviceParser.parse(stream));
+                mVendorDevices.putAll(DeviceParser.parse(stream));
             } catch (Exception e) {
                 mLog.error(e, "Could not load wear devices");
             } finally {
-                try {
-                    Closeables.close(stream, true /* swallowIOException */);
-                } catch (IOException e) {
-                    // Cannot happen
-                }
+                Closeables.closeQuietly(stream);
             }
 
-            stream = null;
+            stream = DeviceManager.class.getResourceAsStream("tv.xml");
             try {
-                stream = DeviceManager.class.getResourceAsStream("tv.xml");
-                mVendorDevices.addAll(DeviceParser.parse(stream));
+                mVendorDevices.putAll(DeviceParser.parse(stream));
             } catch (Exception e) {
                 mLog.error(e, "Could not load tv devices");
             } finally {
-                try {
-                    Closeables.close(stream, true /* swallowIOException */);
-                } catch (IOException e) {
-                    // Cannot happen
-                }
+                Closeables.closeQuietly(stream);
             }
 
             if (mOsSdkPath != null) {
@@ -352,7 +336,7 @@
                 for (File deviceDir : deviceDirs) {
                     File deviceXml = new File(deviceDir, SdkConstants.FN_DEVICES_XML);
                     if (deviceXml.isFile()) {
-                        mVendorDevices.addAll(loadDevices(deviceXml));
+                        mVendorDevices.putAll(loadDevices(deviceXml));
                     }
                 }
                 return true;
@@ -370,7 +354,7 @@
             if (mSysImgDevices != null) {
                 return false;
             }
-            mSysImgDevices = new LinkedHashSet<Device>();
+            mSysImgDevices = HashBasedTable.create();
 
             if (mOsSdkPath == null) {
                 return false;
@@ -399,7 +383,7 @@
 
                         File deviceXml = new File(abiFolder, SdkConstants.FN_DEVICES_XML);
                         if (fop.isFile(deviceXml)) {
-                            mSysImgDevices.addAll(loadDevices(deviceXml));
+                            mSysImgDevices.putAll(loadDevices(deviceXml));
                         }
                     }
                 }
@@ -419,14 +403,14 @@
             }
             // User devices should be saved out to
             // $HOME/.android/devices.xml
-            mUserDevices = new LinkedHashSet<Device>();
+            mUserDevices = HashBasedTable.create();
             File userDevicesFile = null;
             try {
                 userDevicesFile = new File(
                         AndroidLocation.getFolder(),
                         SdkConstants.FN_DEVICES_XML);
                 if (userDevicesFile.exists()) {
-                    mUserDevices.addAll(DeviceParser.parse(userDevicesFile));
+                    mUserDevices.putAll(DeviceParser.parse(userDevicesFile));
                     return true;
                 }
             } catch (AndroidLocationException e) {
@@ -464,7 +448,7 @@
                 assert mUserDevices != null;
             }
             if (mUserDevices != null) {
-                mUserDevices.add(d);
+                mUserDevices.put(d.getId(), d.getManufacturer(), d);
             }
             changed = true;
         }
@@ -480,16 +464,9 @@
                 assert mUserDevices != null;
             }
             if (mUserDevices != null) {
-                Iterator<Device> it = mUserDevices.iterator();
-                while (it.hasNext()) {
-                    Device userDevice = it.next();
-                    if (userDevice.getId().equals(d.getId())
-                            && userDevice.getManufacturer().equals(d.getManufacturer())) {
-                        it.remove();
-                        notifyListeners();
-                        return;
-                    }
-
+                if (mUserDevices.contains(d.getId(), d.getManufacturer())) {
+                    mUserDevices.remove(d.getId(), d.getManufacturer());
+                    notifyListeners();
                 }
             }
         }
@@ -523,15 +500,15 @@
             return;
         }
 
-        if (mUserDevices.size() == 0) {
+        if (mUserDevices.isEmpty()) {
             userDevicesFile.delete();
             return;
         }
 
         synchronized (mLock) {
-            if (mUserDevices.size() > 0) {
+            if (!mUserDevices.isEmpty()) {
                 try {
-                    DeviceWriter.writeToXml(new FileOutputStream(userDevicesFile), mUserDevices);
+                    DeviceWriter.writeToXml(new FileOutputStream(userDevicesFile), mUserDevices.values());
                 } catch (FileNotFoundException e) {
                     mLog.warning("Couldn't open file: %1$s", e.getMessage());
                 } catch (ParserConfigurationException e) {
@@ -573,7 +550,7 @@
         props.put(HardwareProperties.HW_ORIENTATION_SENSOR,
                 getBooleanVal(sensors.contains(Sensor.GYROSCOPE)));
         props.put(HardwareProperties.HW_AUDIO_INPUT, getBooleanVal(hw.hasMic()));
-        props.put(HardwareProperties.HW_SDCARD, getBooleanVal(hw.getRemovableStorage().size() > 0));
+        props.put(HardwareProperties.HW_SDCARD, getBooleanVal(!hw.getRemovableStorage().isEmpty()));
         props.put(HardwareProperties.HW_LCD_DENSITY,
                 Integer.toString(hw.getScreen().getPixelDensity().getDpiValue()));
         props.put(HardwareProperties.HW_PROXIMITY_SENSOR,
@@ -668,7 +645,7 @@
     }
 
     @NonNull
-    private Collection<Device> loadDevices(@NonNull File deviceXml) {
+    private Table<String, String, Device> loadDevices(@NonNull File deviceXml) {
         try {
             return DeviceParser.parse(deviceXml);
         } catch (SAXException e) {
@@ -684,7 +661,7 @@
             // build gets called before everything is properly setup
             mLog.error(e, null);
         }
-        return new LinkedHashSet<Device>();
+        return HashBasedTable.create();
     }
 
     private void notifyListeners() {
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java b/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java
index d9613a0..059f9fb 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java
@@ -16,6 +16,9 @@
 
 package com.android.sdklib.devices;
 
+import static com.android.SdkConstants.VALUE_FALSE;
+import static com.android.SdkConstants.VALUE_TRUE;
+
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.dvlib.DeviceSchema;
@@ -26,10 +29,14 @@
 import com.android.resources.NavigationState;
 import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
 import com.android.resources.TouchScreen;
 import com.android.resources.UiMode;
+import com.google.common.base.Splitter;
 
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
@@ -52,8 +59,11 @@
 public class DeviceParser {
 
     private static class DeviceHandler extends DefaultHandler {
-        private static final String sSpaceRegex = "[\\s]+";
-        private final List<Device> mDevices = new ArrayList<Device>();
+        private static final Splitter sSpaceSplitter = Splitter.on(' ').omitEmptyStrings();
+        private static final String ROUND_BOOT_PROP = "ro.emulator.circular";
+        private static final String CHIN_BOOT_PROP = "ro.emu.win_outset_bottom_px";
+
+        private final Table<String, String, Device> mDevices = HashBasedTable.create();
         private final StringBuilder mStringAccumulator = new StringBuilder();
         private final File mParentFolder;
         private Meta mMeta;
@@ -70,7 +80,7 @@
         }
 
         @NonNull
-        public List<Device> getDevices() {
+        public Table<String, String, Device> getDevices() {
             return mDevices;
         }
 
@@ -126,7 +136,8 @@
         @Override
         public void endElement(String uri, String localName, String name) throws SAXException {
             if (DeviceSchema.NODE_DEVICE.equals(localName)) {
-                mDevices.add(mBuilder.build());
+                Device device = mBuilder.build();
+                mDevices.put(device.getId(), device.getManufacturer(), device);
             } else if (DeviceSchema.NODE_NAME.equals(localName)) {
                 mBuilder.setName(getString(mStringAccumulator));
             } else if (DeviceSchema.NODE_ID.equals(localName)) {
@@ -338,6 +349,7 @@
                 assert mBootProp != null && mBootProp.length == 2 &&
                        mBootProp[0] != null && mBootProp[1] != null;
                 mBuilder.addBootProp(mBootProp[0], mBootProp[1]);
+                checkAndSetIfRound(mBootProp[0], mBootProp[1]);
                 mBootProp = null;
             } else if (DeviceSchema.NODE_SKIN.equals(localName)) {
                 String path = getString(mStringAccumulator).replace('/', File.separatorChar);
@@ -350,9 +362,31 @@
             throw e;
         }
 
-        private List<String> getStringList(StringBuilder stringAccumulator) {
+        private void checkAndSetIfRound(String bootPropKey, String bootPropValue) {
+            // This is a ugly hack. To keep the existing devices.xmls working, the roundness of the
+            // screen is stored in a boot property.
+            ScreenRound roundness = null;
+            if (ROUND_BOOT_PROP.equals(bootPropKey)) {
+                if (VALUE_TRUE.equals(bootPropValue)) {
+                    roundness = ScreenRound.ROUND;
+                } else if (VALUE_FALSE.equals(bootPropValue)) {
+                    roundness = ScreenRound.NOTROUND;
+                }
+                for (State state : mBuilder.getAllStates()) {
+                    state.getHardware().getScreen().setScreenRound(roundness);
+                }
+            }
+            if (CHIN_BOOT_PROP.equals(bootPropKey)) {
+                int chin = Integer.parseInt(bootPropValue);
+                for (State state : mBuilder.getAllStates()) {
+                    state.getHardware().getScreen().setChin(chin);
+                }
+            }
+        }
+
+        private static List<String> getStringList(StringBuilder stringAccumulator) {
             List<String> filteredStrings = new ArrayList<String>();
-            for (String s : getString(mStringAccumulator).split(sSpaceRegex)) {
+            for (String s : sSpaceSplitter.split(stringAccumulator)) {
                 if (s != null && !s.isEmpty()) {
                     filteredStrings.add(s.trim());
                 }
@@ -413,40 +447,46 @@
     }
 
     @NonNull
-    public static List<Device> parse(@NonNull File devicesFile)
+    public static Table<String, String, Device> parse(@NonNull File devicesFile)
             throws SAXException, ParserConfigurationException, IOException {
-        InputStream stream = null;
-        try {
-            stream = new FileInputStream(devicesFile);
-            return parseImpl(stream, devicesFile.getAbsoluteFile().getParentFile());
-        } finally {
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException ignore) {}
-            }
-        }
+        // stream closed by parseImpl.
+        @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+        InputStream stream = new FileInputStream(devicesFile);
+        return parseImpl(stream, devicesFile.getAbsoluteFile().getParentFile());
     }
 
+    /**
+     * This method closes the stream.
+     */
     @NonNull
-    public static List<Device> parse(@NonNull InputStream devices)
+    public static Table<String, String, Device> parse(@NonNull InputStream devices)
             throws SAXException, IOException, ParserConfigurationException {
         return parseImpl(devices, null);
     }
 
+    /**
+     * After parsing, this method closes the stream.
+     */
     @NonNull
-    private static List<Device> parseImpl(@NonNull InputStream devices, @Nullable File parentDir)
+    private static Table<String, String, Device> parseImpl(@NonNull InputStream devices, @Nullable File parentDir)
             throws SAXException, IOException, ParserConfigurationException {
-        if (!devices.markSupported()) {
-            devices = new BufferedInputStream(devices);
+        try {
+            if (!devices.markSupported()) {
+                //noinspection IOResourceOpenedButNotSafelyClosed
+                devices = new BufferedInputStream(devices);  // closed in the finally block.
+            }
+            devices.mark(500000);
+            int version = DeviceSchema.getXmlSchemaVersion(devices);
+            SAXParser parser = getParser(version);
+            DeviceHandler dHandler = new DeviceHandler(parentDir);
+            devices.reset();
+            parser.parse(devices, dHandler);
+            return dHandler.getDevices();
         }
-        devices.mark(500000);
-        int version = DeviceSchema.getXmlSchemaVersion(devices);
-        SAXParser parser = getParser(version);
-        DeviceHandler dHandler = new DeviceHandler(parentDir);
-        devices.reset();
-        parser.parse(devices, dHandler);
-        return dHandler.getDevices();
+        finally {
+            // It's better to close the stream here since we may have created it above.
+            devices.close();
+        }
     }
 
     @NonNull
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/Screen.java b/sdklib/src/main/java/com/android/sdklib/devices/Screen.java
index cd665c2..3230974 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/Screen.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/Screen.java
@@ -16,8 +16,10 @@
 
 package com.android.sdklib.devices;
 
+import com.android.annotations.Nullable;
 import com.android.resources.Density;
 import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
 import com.android.resources.TouchScreen;
 
@@ -34,6 +36,9 @@
     private Multitouch mMultitouch;
     private TouchScreen mMechanism;
     private ScreenType mScreenType;
+    private int mChin;
+    @Nullable
+    private ScreenRound mScreenRound;
 
     public ScreenSize getSize() {
         return mScreenSize;
@@ -123,6 +128,33 @@
         mScreenType = screenType;
     }
 
+    @Nullable
+    public ScreenRound getScreenRound() {
+        return mScreenRound;
+    }
+
+    public void setScreenRound(@Nullable ScreenRound screenRound) {
+        mScreenRound = screenRound;
+    }
+
+    /**
+     * Get the "chin" height in pixels. This is for round screens with a flat section at the
+     * bottom. The "chin" height is the largest perpendicular distance from the flat section to
+     * the original circle.
+     * @return The offset in pixels.
+     */
+    public int getChin() {
+        return mChin;
+    }
+
+    /**
+     * Sets the "chin" height in pixels.
+     * @see #getChin()
+     */
+    public void setChin(int chin) {
+        mChin = chin;
+    }
+
     /**
      * Returns a copy of the object that shares no state with it,
      * but is initialized to equivalent values.
@@ -142,6 +174,8 @@
         s.mMultitouch = mMultitouch;
         s.mMechanism = mMechanism;
         s.mScreenType = mScreenType;
+        s.mScreenRound = mScreenRound;
+        s.mChin = mChin;
         return s;
     }
 
@@ -164,7 +198,9 @@
                 && s.mYdpi == mYdpi
                 && s.mMultitouch == mMultitouch
                 && s.mMechanism == mMechanism
-                && s.mScreenType == mScreenType;
+                && s.mScreenType == mScreenType
+                && s.mScreenRound == mScreenRound
+                && s.mChin == mChin;
     }
 
     @Override
@@ -184,6 +220,10 @@
         hash = 31 * hash + mMultitouch.ordinal();
         hash = 31 * hash + mMechanism.ordinal();
         hash = 31 * hash + mScreenType.ordinal();
+        hash = 31 * hash + mChin;
+        if (mScreenRound != null) {
+            hash = 31 * hash + mScreenRound.ordinal();
+        }
         return hash;
     }
 
@@ -212,6 +252,10 @@
         sb.append(mMechanism);
         sb.append(", mScreenType=");
         sb.append(mScreenType);
+        sb.append(", mScreenRound=");
+        sb.append(mScreenRound);
+        sb.append(", mChin=");
+        sb.append(mChin);
         sb.append("]");
         return sb.toString();
     }
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml b/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
index 004439a..52eef2d 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
+++ b/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
@@ -54,7 +54,7 @@
                 armeabi
             </d:abi>
             <d:dock> </d:dock>
-            <d:power-type>plugged-in</d:power-type>
+            <d:power-type>battery</d:power-type>
             <d:skin>nexus_one</d:skin>
         </d:hardware>
         <d:software>
@@ -137,7 +137,7 @@
                 armeabi
             </d:abi>
             <d:dock> </d:dock>
-            <d:power-type>plugged-in</d:power-type>
+            <d:power-type>battery</d:power-type>
             <d:skin>nexus_s</d:skin>
         </d:hardware>
         <d:software>
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/wear.xml b/sdklib/src/main/java/com/android/sdklib/devices/wear.xml
index d585109..7b297fb 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/wear.xml
+++ b/sdklib/src/main/java/com/android/sdklib/devices/wear.xml
@@ -185,4 +185,87 @@
             </d:boot-prop>
         </d:boot-props>
     </d:device>
+    <d:device>
+        <d:name>Android Wear Round Chin</d:name>
+        <d:id>wear_round_chin_320_290</d:id>
+        <d:manufacturer>Google</d:manufacturer>
+        <d:meta/>
+        <d:hardware>
+            <d:screen>
+                <d:screen-size>small</d:screen-size>
+                <d:diagonal-length>1.65</d:diagonal-length>
+                <d:pixel-density>tvdpi</d:pixel-density>
+                <d:screen-ratio>notlong</d:screen-ratio>
+                <d:dimensions>
+                    <d:x-dimension>320</d:x-dimension>
+                    <d:y-dimension>290</d:y-dimension>
+                </d:dimensions>
+                <d:xdpi>205</d:xdpi>
+                <d:ydpi>205</d:ydpi>
+                <d:touch>
+                    <d:multitouch>jazz-hands</d:multitouch>
+                    <d:mechanism>finger</d:mechanism>
+                    <d:screen-type>capacitive</d:screen-type>
+                </d:touch>
+            </d:screen>
+            <d:networking>
+                Bluetooth
+                Wifi
+                NFC
+            </d:networking>
+            <d:sensors>
+                Accelerometer
+                Barometer
+                Compass
+                GPS
+                Gyroscope
+                LightSensor
+                ProximitySensor
+            </d:sensors>
+            <d:mic>true</d:mic>
+            <d:keyboard>qwerty</d:keyboard>
+            <d:nav>dpad</d:nav>
+            <d:ram unit="MiB">512</d:ram>
+            <d:buttons>hard</d:buttons>
+            <d:internal-storage unit="KiB">7811891</d:internal-storage>
+            <d:removable-storage unit="TiB"/>
+            <d:cpu>Qualcomm Snapdragon S4 Pro</d:cpu>
+            <d:gpu>Adreno 320</d:gpu>
+            <d:abi>armeabi-v7a</d:abi>
+            <d:dock/>
+            <d:power-type>battery</d:power-type>
+            <d:skin>AndroidWearRoundChin320x290</d:skin>
+        </d:hardware>
+        <d:software>
+            <d:api-level>22-</d:api-level>
+            <d:live-wallpaper-support>true</d:live-wallpaper-support>
+            <d:bluetooth-profiles/>
+            <d:gl-version>2.0</d:gl-version>
+            <d:gl-extensions/>
+            <d:status-bar>false</d:status-bar>
+        </d:software>
+        <d:state name="Portrait">
+            <d:description>The device in portrait orientation</d:description>
+            <d:screen-orientation>port</d:screen-orientation>
+            <d:keyboard-state>keyshidden</d:keyboard-state>
+            <d:nav-state>navexposed</d:nav-state>
+        </d:state>
+        <d:state name="Landscape" default="true">
+            <d:description>The device in landscape orientation</d:description>
+            <d:screen-orientation>land</d:screen-orientation>
+            <d:keyboard-state>keyshidden</d:keyboard-state>
+            <d:nav-state>navexposed</d:nav-state>
+        </d:state>
+        <d:tag-id>android-wear</d:tag-id>
+        <d:boot-props>
+            <d:boot-prop>
+                <d:prop-name>ro.emulator.circular</d:prop-name>
+                <d:prop-value>true</d:prop-value>
+            </d:boot-prop>
+            <d:boot-prop>
+                <d:prop-name>ro.emu.win_outset_bottom_px</d:prop-name>
+                <d:prop-value>30</d:prop-value>
+            </d:boot-prop>
+        </d:boot-props>
+    </d:device>
 </d:devices>
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java
index 15ceef3..83a3e55 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java
@@ -25,11 +25,11 @@
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.ISystemImage;
 import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.collect.ImmutableList;
 
 import java.io.File;
 import java.io.FileFilter;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -41,40 +41,6 @@
  */
 public final class AddOnTarget implements IAndroidTarget {
 
-    private static final class OptionalLibrary implements IOptionalLibrary {
-        private final String mJarName;
-        private final String mJarPath;
-        private final String mName;
-        private final String mDescription;
-
-        OptionalLibrary(String jarName, String jarPath, String name, String description) {
-            mJarName = jarName;
-            mJarPath = jarPath;
-            mName = name;
-            mDescription = description;
-        }
-
-        @Override
-        public String getJarName() {
-            return mJarName;
-        }
-
-        @Override
-        public String getJarPath() {
-            return mJarPath;
-        }
-
-        @Override
-        public String getName() {
-            return mName;
-        }
-
-        @Override
-        public String getDescription() {
-            return mDescription;
-        }
-    }
-
     private final String mLocation;
     private final PlatformTarget mBasePlatform;
     private final String mName;
@@ -87,7 +53,7 @@
 
     private File[] mSkins;
     private File mDefaultSkin;
-    private IOptionalLibrary[] mLibraries;
+    private ImmutableList<OptionalLibrary> mLibraries;
     private int mVendorId = NO_USB_ID;
 
     /**
@@ -115,7 +81,7 @@
             boolean hasRenderingLibrary,
             boolean hasRenderingResources,
             PlatformTarget basePlatform) {
-        if (location.endsWith(File.separator) == false) {
+        if (!location.endsWith(File.separator)) {
             location = location + File.separator;
         }
 
@@ -135,15 +101,19 @@
 
         // handle the optional libraries.
         if (libMap != null) {
-            mLibraries = new IOptionalLibrary[libMap.size()];
-            int index = 0;
+            ImmutableList.Builder<OptionalLibrary> builder = ImmutableList.builder();
             for (Entry<String, String[]> entry : libMap.entrySet()) {
                 String jarFile = entry.getValue()[0];
                 String desc = entry.getValue()[1];
-                mLibraries[index++] = new OptionalLibrary(jarFile,
-                        mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile,
-                        entry.getKey(), desc);
+                builder.add(new OptionalLibraryImpl(
+                        entry.getKey(),
+                        new File(mLocation, SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile),
+                        desc,
+                        true /*requireManifestEntry*/));
             }
+            mLibraries = builder.build();
+        } else {
+            mLibraries = ImmutableList.of();
         }
     }
 
@@ -288,7 +258,19 @@
 
     @Override @NonNull
     public List<String> getBootClasspath() {
-        return Collections.singletonList(getPath(IAndroidTarget.ANDROID_JAR));
+        return mBasePlatform.getBootClasspath();
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getOptionalLibraries() {
+        return mBasePlatform.getOptionalLibraries();
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getAdditionalLibraries() {
+        return mLibraries;
     }
 
     @Override
@@ -308,11 +290,6 @@
         return mDefaultSkin;
     }
 
-    @Override
-    public IOptionalLibrary[] getOptionalLibraries() {
-        return mLibraries;
-    }
-
     /**
      * Returns the list of libraries of the underlying platform.
      *
@@ -364,7 +341,7 @@
 
         // The receiver is an add-on. There are 2 big use cases: The add-on has libraries
         // or the add-on doesn't (in which case we consider it a platform).
-        if (mLibraries == null || mLibraries.length == 0) {
+        if (mLibraries.isEmpty()) {
             return mBasePlatform.canRunOn(target);
         } else {
             // the only targets that can run the receiver are the same add-on in the same or later
@@ -412,7 +389,7 @@
      * @see java.lang.Comparable#compareTo(java.lang.Object)
      */
     @Override
-    public int compareTo(IAndroidTarget target) {
+    public int compareTo(@NonNull IAndroidTarget target) {
         // quick check.
         if (this == target) {
             return 0;
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/MissingTarget.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/MissingTarget.java
new file mode 100644
index 0000000..4454040
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/MissingTarget.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2015 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.sdklib.internal.androidTarget;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISystemImage;
+import com.android.sdklib.SdkVersionInfo;
+import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A target that we don't have, but is referenced (e.g. by a system image).
+ */
+public class MissingTarget implements IAndroidTarget {
+
+    private final String mVendor;
+
+    private final AndroidVersion mVersion;
+
+    private final List<ISystemImage> mSystemImages = Lists.newArrayList();
+
+    private final String mName;
+
+    public MissingTarget(String vendor, String name, AndroidVersion version) {
+        mVendor = vendor;
+        mVersion = version;
+        mName = name;
+    }
+
+    @Override
+    public String getLocation() {
+        return null;
+    }
+
+    @Override
+    public String getVendor() {
+        return mVendor;
+    }
+
+    @Override
+    public String getName() {
+        return mName;
+    }
+
+    @Override
+    public String getFullName() {
+        return getName();
+    }
+
+    @Override
+    public String getClasspathName() {
+        return null;
+    }
+
+    @Override
+    public String getShortClasspathName() {
+        return null;
+    }
+
+    @Override
+    public String getDescription() {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public AndroidVersion getVersion() {
+        return mVersion;
+    }
+
+    @Override
+    public String getVersionName() {
+        return SdkVersionInfo.getAndroidName(getVersion().getApiLevel());
+    }
+
+    @Override
+    public int getRevision() {
+        return 0;
+    }
+
+    @Override
+    public boolean isPlatform() {
+        return mVendor == null;
+    }
+
+    @Override
+    public IAndroidTarget getParent() {
+        return null;
+    }
+
+    @Override
+    public String getPath(int pathId) {
+        return null;
+    }
+
+    @Override
+    public File getFile(int pathId) {
+        return null;
+    }
+
+    @Override
+    public BuildToolInfo getBuildToolInfo() {
+        return null;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getBootClasspath() {
+        return ImmutableList.of();
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getOptionalLibraries() {
+        return ImmutableList.of();
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getAdditionalLibraries() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public boolean hasRenderingLibrary() {
+        return false;
+    }
+
+    @NonNull
+    @Override
+    public File[] getSkins() {
+        return new File[0];
+    }
+
+    @Nullable
+    @Override
+    public File getDefaultSkin() {
+        return null;
+    }
+
+    @Override
+    public String[] getPlatformLibraries() {
+        return new String[0];
+    }
+
+    @Override
+    public String getProperty(String name) {
+        return null;
+    }
+
+    @Override
+    public Integer getProperty(String name, Integer defaultValue) {
+        return null;
+    }
+
+    @Override
+    public Boolean getProperty(String name, Boolean defaultValue) {
+        return null;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return null;
+    }
+
+    @Override
+    public int getUsbVendorId() {
+        return 0;
+    }
+
+    @Override
+    public ISystemImage[] getSystemImages() {
+        return mSystemImages.toArray(new ISystemImage[mSystemImages.size()]);
+    }
+
+    public void addSystemImage(ISystemImage image) {
+        mSystemImages.add(image);
+    }
+
+    @Nullable
+    @Override
+    public ISystemImage getSystemImage(@NonNull IdDisplay tag, @NonNull String abiType) {
+        for (ISystemImage image : mSystemImages) {
+            if (tag.equals(image.getTag()) && abiType.equals(image.getAbiType())) {
+                return image;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean canRunOn(IAndroidTarget target) {
+        return false;
+    }
+
+    @Override
+    public String hashString() {
+        return AndroidTargetHash.getTargetHashString(this);
+    }
+
+    @Override
+    public int compareTo(IAndroidTarget o) {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof MissingTarget)) {
+            return false;
+        }
+        MissingTarget other = (MissingTarget) obj;
+        return Objects.equal(mVendor, other.mVendor) && Objects.equal(mVersion, other.mVersion);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mVendor, mVersion);
+    }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/OptionalLibraryImpl.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/OptionalLibraryImpl.java
new file mode 100644
index 0000000..97f7eff
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/OptionalLibraryImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.sdklib.internal.androidTarget;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.IAndroidTarget;
+
+import java.io.File;
+
+/**
+ * Internal implementation of OptionalLibrary
+ */
+public class OptionalLibraryImpl implements IAndroidTarget.OptionalLibrary {
+
+    @NonNull
+    private final String mLibraryName;
+    @NonNull
+    private final File mJarFile;
+    @NonNull
+    private final String mDescription;
+    private final boolean mRequireManifestEntry;
+
+    public OptionalLibraryImpl(
+            @NonNull String libraryName,
+            @NonNull File jarFile,
+            @NonNull String description,
+            boolean requireManifestEntry) {
+        mLibraryName = libraryName;
+        mJarFile = jarFile;
+        mDescription = description;
+        mRequireManifestEntry = requireManifestEntry;
+    }
+
+    @Override
+    @NonNull
+    public String getName() {
+        return mLibraryName;
+    }
+
+    @Override
+    @NonNull
+    public File getJar() {
+        return mJarFile;
+    }
+
+    @Override
+    @NonNull
+    public String getDescription() {
+        return mDescription;
+    }
+
+    @Override
+    public boolean isManifestEntryRequired() {
+        return mRequireManifestEntry;
+    }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java
index 4825977..cdd5a9e 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java
@@ -27,6 +27,7 @@
 import com.android.sdklib.SdkManager.LayoutlibVersion;
 import com.android.sdklib.repository.descriptors.IdDisplay;
 import com.android.utils.SparseArray;
+import com.google.common.collect.ImmutableList;
 
 import java.io.File;
 import java.util.Arrays;
@@ -54,6 +55,7 @@
     private final SparseArray<String> mPaths = new SparseArray<String>();
     private File[] mSkins;
     private final ISystemImage[] mSystemImages;
+    private final List<OptionalLibrary> mOptionalLibraries;
     private final LayoutlibVersion mLayoutlibVersion;
     private final BuildToolInfo mBuildToolInfo;
 
@@ -78,6 +80,7 @@
             LayoutlibVersion layoutlibVersion,
             ISystemImage[] systemImages,
             Map<String, String> properties,
+            List<OptionalLibrary> optionalLibraries,
             @NonNull BuildToolInfo buildToolInfo) {
         if (!platformOSPath.endsWith(File.separator)) {
             platformOSPath = platformOSPath + File.separator;
@@ -91,6 +94,7 @@
         mBuildToolInfo = buildToolInfo;
         mSystemImages = systemImages == null ? new ISystemImage[0] : systemImages;
         Arrays.sort(mSystemImages);
+        mOptionalLibraries = ImmutableList.copyOf(optionalLibraries);
 
         if (mVersion.isPreview()) {
             mName =  String.format(PLATFORM_NAME_PREVIEW, mVersionName);
@@ -235,7 +239,6 @@
         return new File(getPath(pathId));
     }
 
-
     @Override
     public BuildToolInfo getBuildToolInfo() {
         return mBuildToolInfo;
@@ -243,7 +246,25 @@
 
     @Override @NonNull
     public List<String> getBootClasspath() {
-        return Collections.singletonList(getPath(IAndroidTarget.ANDROID_JAR));
+        return ImmutableList.of(getPath(IAndroidTarget.ANDROID_JAR));
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getOptionalLibraries() {
+        return mOptionalLibraries;
+    }
+
+    /**
+     * Always returns null, as a standard platform has no additional libraries.
+     *
+     * {@inheritDoc}
+     * @see com.android.sdklib.IAndroidTarget#getAdditionalLibraries()
+     */
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getAdditionalLibraries() {
+        return ImmutableList.of();
     }
 
     /**
@@ -284,17 +305,6 @@
     }
 
     /**
-     * Always returns null, as a standard platform ha no optional libraries.
-     *
-     * {@inheritDoc}
-     * @see com.android.sdklib.IAndroidTarget#getOptionalLibraries()
-     */
-    @Override
-    public IOptionalLibrary[] getOptionalLibraries() {
-        return null;
-    }
-
-    /**
      * Currently always return a fixed list with "android.test.runner" in it.
      * <p/>
      * TODO change the fixed library list to be build-dependent later.
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java b/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java
index 99def1d..6e9ab71 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java
@@ -825,7 +825,7 @@
             // Config file.
             HashMap<String, String> values = new HashMap<String, String>();
 
-           if (setImagePathProperties(target, tag, abiType, values, log) == false) {
+            if (setImagePathProperties(target, tag, abiType, values, log) == false) {
                log.error(null, "Failed to set image path properties in the AVD folder.");
                needCleanup = true;
                return null;
@@ -928,7 +928,7 @@
                 values.put(AVD_INI_SKIN_PATH, skinPath);
             }
 
-            if (sdcard != null && sdcard.length() > 0) {
+            if (sdcard != null && !sdcard.isEmpty()) {
                 // Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
                 // in the AVD folder, and do not put any value in config.ini.
 
@@ -1088,7 +1088,7 @@
             report.append(String.format(", %s processor", AvdInfo.getPrettyAbiType(tag, abiType)));
 
             // display the chosen hardware config
-            if (finalHardwareValues.size() > 0) {
+            if (!finalHardwareValues.isEmpty()) {
                 report.append(",\nwith the following hardware config:\n");
                 List<String> keys = new ArrayList<String>(finalHardwareValues.keySet());
                 Collections.sort(keys);
@@ -1868,7 +1868,7 @@
             Map<String, String> map = new HashMap<String, String>();
             while ((line = reader.readLine()) != null) {
                 line = line.trim();
-                if (line.length() > 0 && line.charAt(0) != '#') {
+                if (!line.isEmpty() && line.charAt(0) != '#') {
 
                     Matcher m = INI_LINE_PATTERN.matcher(line);
                     if (m.matches()) {
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java b/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java
index 8bf07cc..6b91b45 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java
@@ -207,7 +207,7 @@
             String line = null;
             HardwareProperty prop = null;
             while ((line = reader.readLine()) != null) {
-                if (line.length() > 0 && line.charAt(0) != '#') {
+                if (!line.isEmpty() && line.charAt(0) != '#') {
                     Matcher m = PATTERN_PROP.matcher(line);
                     if (m.matches()) {
                         String key = m.group(1);
@@ -249,7 +249,7 @@
                             int n = 0;
                             for (int i = 0; i < v.length; i++) {
                                 String s = v[i] = v[i].trim();
-                                if (s.length() > 0) {
+                                if (!s.isEmpty()) {
                                     n++;
                                 }
                             }
@@ -257,7 +257,7 @@
                             n = 0;
                             for (int i = 0; i < v.length; i++) {
                                 String s = v[i];
-                                if (s.length() > 0) {
+                                if (!s.isEmpty()) {
                                     prop.mEnum[n++] = s;
                                 }
                             }
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java b/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java
index d339ea5..6ba4e72 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java
@@ -77,7 +77,7 @@
 
         String javaHome = System.getProperty("java.home");
 
-        if (javaHome != null && javaHome.length() > 0) {
+        if (javaHome != null && !javaHome.isEmpty()) {
             keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand;
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java b/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java
index 9588de2..c5712d7 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java
@@ -1224,7 +1224,7 @@
             // with an actual AndroidManifest.xml file. The nodes may not have the requested
             // attributes though, if which case we should warn.
 
-            if (packageName == null || packageName.length() == 0) {
+            if (packageName == null || packageName.isEmpty()) {
                 mLog.error(null,
                         "Missing <manifest package=\"...\"> in '%1$s'",
                         manifestFile.getName());
@@ -1252,7 +1252,7 @@
                 }
             }
 
-            if (activityName.length() == 0) {
+            if (activityName.isEmpty()) {
                 mLog.warning("Missing <activity %1$s:name=\"...\"> in '%2$s'.\n" +
                         "No activity will be generated.",
                         AndroidXPathFactory.DEFAULT_NS_PREFIX, manifestFile.getName());
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java
index 55dbfd7..816c7f0 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java
@@ -579,7 +579,7 @@
                             strUrl = baseUrl + strUrl;
                         }
 
-                        if (strUrl.length() > 0 && strName.length() > 0) {
+                        if (!strUrl.isEmpty() && !strName.isEmpty()) {
                             sites.add(new Site(strUrl, strName, type));
                         }
                     }
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java
index 1e3b8fb..c756d1f 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -768,7 +768,7 @@
                 props.load(fis);
 
                 // To be valid, there must be at least one property in it.
-                if (props.size() > 0) {
+                if (!props.isEmpty()) {
                     return props;
                 }
             }
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java
index 98f0abc..cb1af74 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java
@@ -556,7 +556,7 @@
                                 getTextContent().trim();
 
                         if (codeName == null || versName == null ||
-                                codeName.length() == 0 || versName.length() == 0) {
+                                codeName.isEmpty() || versName.isEmpty()) {
                             // bad names. ignore.
                             continue;
                         }
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java
index 44f050a..338463c 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java
@@ -468,7 +468,7 @@
                 String domain = result.getDomain();
 
                 // proceed in case there is indeed a user
-                if (user != null && user.length() > 0) {
+                if (user != null && !user.isEmpty()) {
                     Credentials credentials = new NTCredentials(user, password,
                             workstation, domain);
                     httpClient.getCredentialsProvider().setCredentials(authScope, credentials);
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java
index 73f5625..d4aa340 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java
@@ -288,9 +288,9 @@
                     String etag = props.getProperty(HttpHeaders.ETAG);
                     String lastMod = props.getProperty(HttpHeaders.LAST_MODIFIED);
 
-                    if (etag != null && etag.length() > 0) {
+                    if (etag != null && !etag.isEmpty()) {
                         headers.add(new BasicHeader(HttpHeaders.IF_MATCH, etag));
-                    } else if (lastMod != null && lastMod.length() > 0) {
+                    } else if (lastMod != null && !lastMod.isEmpty()) {
                         headers.add(new BasicHeader(HttpHeaders.IF_MATCH, lastMod));
                     }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java
index 9d138a4..f5c404d 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java
@@ -22,7 +22,7 @@
 import com.android.annotations.VisibleForTesting.Visibility;
 import com.android.sdklib.AndroidVersion;
 import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
 import com.android.sdklib.SdkManager;
 import com.android.sdklib.internal.repository.sources.SdkSource;
 import com.android.sdklib.repository.AddonManifestIniProps;
@@ -36,12 +36,14 @@
 import com.android.sdklib.repository.descriptors.PkgDesc;
 import com.android.sdklib.repository.local.LocalAddonPkgInfo;
 import com.android.utils.Pair;
+import com.google.common.collect.ImmutableList;
 
 import org.w3c.dom.Node;
 
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
@@ -160,17 +162,17 @@
                                                           SdkRepoConstants.NODE_NAME);
 
         // The old <name> is equivalent to the new <name-display>
-        if (nameDisp.length() == 0) {
+        if (nameDisp.isEmpty()) {
             nameDisp = name;
         }
 
         // For a missing id, we simply use a sanitized version of the display name
-        if (nameId.length() == 0) {
-            nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(name.length() > 0 ? name : nameDisp);
+        if (nameId.isEmpty()) {
+            nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(!name.isEmpty() ? name : nameDisp);
         }
 
-        assert nameId.length() > 0;
-        assert nameDisp.length() > 0;
+        assert !nameId.isEmpty();
+        assert !nameDisp.isEmpty();
 
         mNameId = nameId.trim();
         mDisplayName = nameDisp.trim();
@@ -186,18 +188,18 @@
                                                             SdkAddonConstants.NODE_VENDOR);
 
         // The old <vendor> is equivalent to the new <vendor-display>
-        if (vendorDisp.length() == 0) {
+        if (vendorDisp.isEmpty()) {
             vendorDisp = vendor;
         }
 
         // For a missing id, we simply use a sanitized version of the display vendor
-        if (vendorId.length() == 0) {
-            boolean hasVendor = vendor.length() > 0;
+        if (vendorId.isEmpty()) {
+            boolean hasVendor = !vendor.isEmpty();
             vendorId = LocalAddonPkgInfo.sanitizeDisplayToNameId(hasVendor ? vendor : vendorDisp);
         }
 
-        assert vendorId.length() > 0;
-        assert vendorDisp.length() > 0;
+        assert !vendorId.isEmpty();
+        assert !vendorDisp.isEmpty();
 
         mVendorId      = vendorId.trim();
         mVendorDisplay = vendorDisp.trim();
@@ -259,17 +261,19 @@
         String name     = getProperty(props, PkgProps.ADDON_NAME, target.getName());
 
         // The old <name> is equivalent to the new <name-display>
-        if (nameDisp.length() == 0) {
+        //noinspection ConstantConditions
+        if (nameDisp.isEmpty()) {
             nameDisp = name;
         }
 
         // For a missing id, we simply use a sanitized version of the display name
-        if (nameId.length() == 0) {
-            nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(name.length() > 0 ? name : nameDisp);
+        //noinspection ConstantConditions
+        if (nameId.isEmpty()) {
+            nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(!name.isEmpty() ? name : nameDisp);
         }
 
-        assert nameId.length() > 0;
-        assert nameDisp.length() > 0;
+        assert !nameId.isEmpty();
+        assert !nameDisp.isEmpty();
 
         mNameId = nameId.trim();
         mDisplayName = nameDisp.trim();
@@ -282,18 +286,21 @@
         String vendor     = getProperty(props, PkgProps.ADDON_VENDOR, target.getVendor());
 
         // The old <vendor> is equivalent to the new <vendor-display>
-        if (vendorDisp.length() == 0) {
+        //noinspection ConstantConditions
+        if (vendorDisp.isEmpty()) {
             vendorDisp = vendor;
         }
 
         // For a missing id, we simply use a sanitized version of the display vendor
-        if (vendorId.length() == 0) {
-            boolean hasVendor = vendor.length() > 0;
+        //noinspection ConstantConditions
+        if (vendorId.isEmpty()) {
+            //noinspection ConstantConditions
+            boolean hasVendor = !vendor.isEmpty();
             vendorId = LocalAddonPkgInfo.sanitizeDisplayToNameId(hasVendor ? vendor : vendorDisp);
         }
 
-        assert vendorId.length() > 0;
-        assert vendorDisp.length() > 0;
+        assert !vendorId.isEmpty();
+        assert !vendorDisp.isEmpty();
 
         mVendorId = vendorId.trim();
         mVendorDisplay = vendorDisp.trim();
@@ -303,13 +310,14 @@
         mVersion = target.getVersion();
         mLayoutlibVersion = new LayoutlibVersionMixin(props);
 
-        IOptionalLibrary[] optLibs = target.getOptionalLibraries();
-        if (optLibs == null || optLibs.length == 0) {
+        List<OptionalLibrary> optLibs = target.getAdditionalLibraries();
+        if (optLibs.isEmpty()) {
             mLibs = new Lib[0];
         } else {
-            mLibs = new Lib[optLibs.length];
-            for (int i = 0; i < optLibs.length; i++) {
-                mLibs[i] = new Lib(optLibs[i].getName(), optLibs[i].getDescription());
+            mLibs = new Lib[optLibs.size()];
+            for (int i = 0; i < optLibs.size(); i++) {
+                OptionalLibrary optionalLibrary = optLibs.get(i);
+                mLibs[i] = new Lib(optionalLibrary.getName(), optionalLibrary.getDescription());
             }
         }
 
@@ -588,7 +596,7 @@
                 getDisplayVendor());
 
         String d = getDescription();
-        if (d != null && d.length() > 0) {
+        if (d != null && !d.isEmpty()) {
             s += '\n' + d;
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java
index 454bc53..3ee5ed2 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java
@@ -165,12 +165,12 @@
     public String getLongDescription() {
 
         String s = mLongDescription;
-        if (s != null && s.length() != 0) {
+        if (s != null && !s.isEmpty()) {
             return s;
         }
 
         s = getDescription();
-        if (s != null && s.length() != 0) {
+        if (s != null && !s.isEmpty()) {
             return s;
         }
         return getShortDescription();
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java
index 73faba3..2aa30ae 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java
@@ -26,6 +26,7 @@
 import com.android.sdklib.repository.FullRevision.PreviewComparison;
 import com.android.sdklib.repository.IDescription;
 import com.android.sdklib.repository.PkgProps;
+import com.android.sdklib.repository.PreciseRevision;
 import com.android.sdklib.repository.descriptors.IPkgDesc;
 import com.android.sdklib.repository.descriptors.PkgDesc;
 
@@ -229,7 +230,7 @@
      */
     @Override
     public String installId() {
-        return INSTALL_ID_BASE + getRevision().toString().replace(' ', '_');
+        return getPkgDesc().getInstallId();
     }
 
     /**
@@ -270,7 +271,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
@@ -297,7 +298,21 @@
     @Override
     public File getInstallFolder(String osSdkRoot, SdkManager sdkManager) {
         File folder = new File(osSdkRoot, SdkConstants.FD_BUILD_TOOLS);
-        folder = new File(folder, getRevision().toString().replace(' ', '_'));
+        StringBuilder sb = new StringBuilder();
+
+        PreciseRevision revision = getPkgDesc().getPreciseRevision();
+        int[] version = revision.toIntArray(false);
+        for (int i = 0; i < version.length; i++) {
+            sb.append(version[i]);
+            if (i != version.length - 1) {
+                sb.append('.');
+            }
+        }
+        if (getPkgDesc().getPreciseRevision().isPreview()) {
+            sb.append(PkgDesc.PREVIEW_SUFFIX);
+        }
+
+        folder = new File(folder, sb.toString());
         return folder;
     }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java
index 5e51f40..3582f5b 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java
@@ -70,7 +70,7 @@
             PackageParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
         String codeName =
             PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
-        if (codeName.length() == 0) {
+        if (codeName.isEmpty()) {
             codeName = null;
         }
         mVersion = new AndroidVersion(apiLevel, codeName);
@@ -216,7 +216,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java
index 8390fcb..fccbd21 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java
@@ -128,24 +128,24 @@
         String vid   =
             PackageParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR_ID);
 
-        if (vid.length() == 0) {
+        if (vid.isEmpty()) {
             // If vid is missing, use the old <vendor> attribute.
             // Note that in a valid XML, vendor-id cannot be an empty string.
             // The only reason vid can be empty is when <vendor-id> is missing, which
             // happens in an addon-3 schema, in which case the old <vendor> needs to be used.
             String vendor = PackageParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR);
             vid = sanitizeLegacyVendor(vendor);
-            if (vname.length() == 0) {
+            if (vname.isEmpty()) {
                 vname = vendor;
             }
         }
-        if (vname.length() == 0) {
+        if (vname.isEmpty()) {
             // The vendor-display name can be empty, in which case we use the vendor-id.
             vname = vid;
         }
         mVendor = new IdDisplay(vid.trim(), vname.trim());
 
-        if (name.length() == 0) {
+        if (name.isEmpty()) {
             // If name is missing, use the <path> attribute as done in an addon-3 schema.
             name = LocalExtraPkgInfo.getPrettyName(mVendor, mPath);
         }
@@ -179,7 +179,7 @@
                     String path = child.getTextContent();
                     if (path != null) {
                         path = path.trim();
-                        if (path.length() > 0) {
+                        if (!path.isEmpty()) {
                             paths.add(path);
                         }
                     }
@@ -251,22 +251,22 @@
         String vid   = vendorId != null ? vendorId :
                               getProperty(props, PkgProps.EXTRA_VENDOR_ID, ""); //$NON-NLS-1$
 
-        if (vid == null || vid.length() == 0) {
+        if (vid == null || vid.isEmpty()) {
             // If vid is missing, use the old <vendor> attribute.
             // <vendor> did not exist prior to schema repo-v3 and tools r8.
             String vendor = getProperty(props, PkgProps.EXTRA_VENDOR, "");      //$NON-NLS-1$
             vid = sanitizeLegacyVendor(vendor);
-            if (vname == null || vname.length() == 0) {
+            if (vname == null || vname.isEmpty()) {
                 vname = vendor;
             }
         }
-        if (vname == null || vname.length() == 0) {
+        if (vname == null || vname.isEmpty()) {
             // The vendor-display name can be empty, in which case we use the vendor-id.
             vname = vid;
         }
         mVendor = new IdDisplay(vid.trim(), vname.trim());
 
-        if (name == null || name.length() == 0) {
+        if (name == null || name.isEmpty()) {
             // If name is missing, use the <path> attribute as done in an addon-3 schema.
             name = LocalExtraPkgInfo.getPrettyName(mVendor, mPath);
         }
@@ -279,10 +279,10 @@
 
         String projectFiles = getProperty(props, PkgProps.EXTRA_PROJECT_FILES, null);
         ArrayList<String> filePaths = new ArrayList<String>();
-        if (projectFiles != null && projectFiles.length() > 0) {
+        if (projectFiles != null && !projectFiles.isEmpty()) {
             for (String filePath : projectFiles.split(Pattern.quote(File.pathSeparator))) {
                 filePath = filePath.trim();
-                if (filePath.length() > 0) {
+                if (!filePath.isEmpty()) {
                     filePaths.add(filePath);
                 }
             }
@@ -330,7 +330,7 @@
             props.setProperty(PkgProps.EXTRA_PROJECT_FILES, sb.toString());
         }
 
-        if (mOldPaths != null && mOldPaths.length() > 0) {
+        if (mOldPaths != null && !mOldPaths.isEmpty()) {
             props.setProperty(PkgProps.EXTRA_OLD_PATHS, mOldPaths);
         }
     }
@@ -398,7 +398,7 @@
 
         // Sanitize the path
         String path = mPath.replaceAll("[^a-zA-Z0-9-]+", "_");      //$NON-NLS-1$
-        if (path.length() == 0 || path.equals("_")) {               //$NON-NLS-1$
+        if (path.isEmpty() || path.equals("_")) {               //$NON-NLS-1$
             int h = path.hashCode();
             path = String.format("extra%08x", h);                   //$NON-NLS-1$
         }
@@ -427,7 +427,7 @@
         // and cannot be empty. Let's be defensive and enforce that anyway since things
         // like "____" are still valid values that we don't want to allow.
 
-        if (vendorDisplay != null && vendorDisplay.length() > 0) {
+        if (vendorDisplay != null && !vendorDisplay.isEmpty()) {
             String vendor = vendorDisplay.trim();
             // Sanitize the vendor
             vendor = vendor.replaceAll("[^a-zA-Z0-9-]+", "_");      //$NON-NLS-1$
@@ -511,7 +511,7 @@
                 getVendorDisplay());
 
         String d = getDescription();
-        if (d != null && d.length() > 0) {
+        if (d != null && !d.isEmpty()) {
             s += '\n' + d;
         }
 
@@ -585,12 +585,12 @@
         File path = new File(osSdkRoot, SdkConstants.FD_EXTRAS);
 
         String vendor = getVendorId();
-        if (vendor != null && vendor.length() > 0) {
+        if (vendor != null && !vendor.isEmpty()) {
             path = new File(path, vendor);
         }
 
         String name = getPath();
-        if (name != null && name.length() > 0) {
+        if (name != null && !name.isEmpty()) {
             path = new File(path, name);
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java
index d441e67..4cf6f9b 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java
@@ -153,8 +153,13 @@
             descUrl = "";
         }
 
-        mLicense     = new License(getProperty(props, PkgProps.PKG_LICENSE, license),
-                                   getProperty(props, PkgProps.PKG_LICENSE_REF, null));
+        license = getProperty(props, PkgProps.PKG_LICENSE, license);
+        if (license != null) {
+            mLicense = new License(license, getProperty(props, PkgProps.PKG_LICENSE_REF, null));
+        }
+        else {
+            mLicense = null;
+        }
         mListDisplay = getProperty(props, PkgProps.PKG_LIST_DISPLAY, "");       //$NON-NLS-1$
         mDescription = getProperty(props, PkgProps.PKG_DESC,         description);
         mDescUrl     = getProperty(props, PkgProps.PKG_DESC_URL,     descUrl);
@@ -258,28 +263,28 @@
     public void saveProperties(@NonNull Properties props) {
         if (mLicense != null) {
             String license = mLicense.getLicense();
-            if (license != null && license.length() > 0) {
+            if (license != null && !license.isEmpty()) {
                 props.setProperty(PkgProps.PKG_LICENSE, license);
             }
             String licenseRef = mLicense.getLicenseRef();
-            if (licenseRef != null && licenseRef.length() > 0) {
+            if (licenseRef != null && !licenseRef.isEmpty()) {
                 props.setProperty(PkgProps.PKG_LICENSE_REF, licenseRef);
             }
         }
-        if (mListDisplay != null && mListDisplay.length() > 0) {
+        if (mListDisplay != null && !mListDisplay.isEmpty()) {
             props.setProperty(PkgProps.PKG_LIST_DISPLAY, mListDisplay);
         }
-        if (mDescription != null && mDescription.length() > 0) {
+        if (mDescription != null && !mDescription.isEmpty()) {
             props.setProperty(PkgProps.PKG_DESC, mDescription);
         }
-        if (mDescUrl != null && mDescUrl.length() > 0) {
+        if (mDescUrl != null && !mDescUrl.isEmpty()) {
             props.setProperty(PkgProps.PKG_DESC_URL, mDescUrl);
         }
 
-        if (mReleaseNote != null && mReleaseNote.length() > 0) {
+        if (mReleaseNote != null && !mReleaseNote.isEmpty()) {
             props.setProperty(PkgProps.PKG_RELEASE_NOTE, mReleaseNote);
         }
-        if (mReleaseUrl != null && mReleaseUrl.length() > 0) {
+        if (mReleaseUrl != null && !mReleaseUrl.isEmpty()) {
             props.setProperty(PkgProps.PKG_RELEASE_URL, mReleaseUrl);
         }
         if (mObsolete != null) {
@@ -567,17 +572,17 @@
                 isObsolete() ? " (Obsolete)" : ""));
 
         s = getDescUrl();
-        if (s != null && s.length() > 0) {
+        if (s != null && !s.isEmpty()) {
             sb.append(String.format("\n\nMore information at %1$s", s));
         }
 
         s = getReleaseNote();
-        if (s != null && s.length() > 0) {
+        if (s != null && !s.isEmpty()) {
             sb.append("\n\nRelease note:\n").append(s);
         }
 
         s = getReleaseNoteUrl();
-        if (s != null && s.length() > 0) {
+        if (s != null && !s.isEmpty()) {
             sb.append("\nRelease note URL: ").append(s);
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java
index 96b1641..ff4e4bb 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java
@@ -89,7 +89,7 @@
             PackageParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
         String codeName =
             PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
-        if (codeName.length() == 0) {
+        if (codeName.isEmpty()) {
             codeName = null;
         }
         mVersion = new AndroidVersion(apiLevel, codeName);
@@ -285,7 +285,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java
index d757a40..85c6953 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java
@@ -231,7 +231,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java
index f9cca7f..d31805e 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java
@@ -88,7 +88,7 @@
             PackageParserUtils.getXmlInt   (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
         String codeName =
             PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
-        if (codeName.length() == 0) {
+        if (codeName.isEmpty()) {
             codeName = null;
         }
         mVersion = new AndroidVersion(apiLevel, codeName);
@@ -272,7 +272,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
@@ -359,7 +359,7 @@
         if (installFolder != null && installFolder.isDirectory()) {
             // Get the hash computed during the last installation
             String storedHash = readContentHash(installFolder);
-            if (storedHash != null && storedHash.length() > 0) {
+            if (storedHash != null && !storedHash.isEmpty()) {
 
                 // Get the hash of the folder now
                 String currentHash = computeContentHash(installFolder);
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java
index 234b3e7..6942431 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java
@@ -80,7 +80,7 @@
             PackageParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
         String codeName =
             PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
-        if (codeName.length() == 0) {
+        if (codeName.isEmpty()) {
             codeName = null;
         }
         mVersion = new AndroidVersion(apiLevel, codeName);
@@ -284,7 +284,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java
index 7bc4bf6..c5ce04a 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java
@@ -83,7 +83,7 @@
             PackageParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
         String codeName =
             PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
-        if (codeName.length() == 0) {
+        if (codeName.isEmpty()) {
             codeName = null;
         }
         mVersion = new AndroidVersion(apiLevel, codeName);
@@ -125,8 +125,8 @@
                     SdkAddonConstants.NODE_VENDOR_DISPLAY,
                     vendorId);
 
-            assert vendorId.length() > 0;
-            assert vendorDisp.length() > 0;
+            assert !vendorId.isEmpty();
+            assert !vendorDisp.isEmpty();
 
             vendor = new IdDisplay(vendorId, vendorDisp);
 
@@ -186,8 +186,8 @@
         }
         else {
             // An add-on system-image
-            assert vendorId.length() > 0;
-            assert vendorDisp.length() > 0;
+            assert !vendorId.isEmpty();
+            assert !vendorDisp.isEmpty();
 
             vendor = new IdDisplay(vendorId, vendorDisp);
 
@@ -480,7 +480,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java
index f58e609..1b8b3b8 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java
@@ -215,7 +215,7 @@
     @Override
     public String getLongDescription() {
         String s = getDescription();
-        if (s == null || s.length() == 0) {
+        if (s == null || s.isEmpty()) {
             s = getShortDescription();
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java
index 2d5dbb1..3d2efb5 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java
@@ -263,7 +263,7 @@
         }
 
         // we must have found the root node, and it must have an XML namespace prefix.
-        if (oldRoot == null || prefix == null || prefix.length() == 0) {
+        if (oldRoot == null || prefix == null || prefix.isEmpty()) {
             return null;
         }
 
@@ -362,7 +362,7 @@
 
                             Node node = findChild(archive, null, prefix, RepoConstants.NODE_URL);
                             String url = node == null ? null : node.getTextContent().trim();
-                            if (url == null || url.length() == 0) {
+                            if (url == null || url.isEmpty()) {
                                 continue;
                             }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java
index 9996989..54ebd34 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java
@@ -294,7 +294,7 @@
     @Override
     public String getShortDescription() {
 
-        if (mUiName != null && mUiName.length() > 0) {
+        if (mUiName != null && !mUiName.isEmpty()) {
 
             String host = "malformed URL";
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java
index 5aaf936..c209838 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java
@@ -98,7 +98,7 @@
      * want to install.
      */
     public boolean isDependencyFor() {
-        return mDependencyFor.size() > 0;
+        return !mDependencyFor.isEmpty();
     }
 
     /**
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java
index 095923f..1da1b01 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java
@@ -74,9 +74,9 @@
     /**
      * Setting to enabling previews in the package list
      * Type: Boolean.
-     * Default: False.
+     * Default: True.
      */
-    String KEY_ENABLE_PREVIEWS = "sdkman.enable.previews";   //$NON-NLS-1$
+    String KEY_ENABLE_PREVIEWS = "sdkman.enable.previews2";   //$NON-NLS-1$
 
     /**
      * Setting to set the density of the monitor.
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java
index 7a3da42..c0314d4 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java
@@ -445,7 +445,7 @@
         // We override SdkRepoConstants.URL_GOOGLE_SDK_SITE if this is defined
         String baseUrl = System.getenv("SDK_TEST_BASE_URL");            //$NON-NLS-1$
         if (baseUrl != null) {
-            if (baseUrl.length() > 0 && baseUrl.endsWith("/")) {        //$NON-NLS-1$
+            if (!baseUrl.isEmpty() && baseUrl.endsWith("/")) {        //$NON-NLS-1$
                 if (url.startsWith(SdkRepoConstants.URL_GOOGLE_SDK_SITE)) {
                     url = baseUrl + url.substring(SdkRepoConstants.URL_GOOGLE_SDK_SITE.length());
                 }
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java
index 330fddb..ff2000f 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java
@@ -697,7 +697,7 @@
             }
         }
 
-        if (aiFound.size() > 0) {
+        if (!aiFound.isEmpty()) {
             ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]);
             Arrays.sort(result);
             return result;
@@ -876,6 +876,10 @@
                 Package p = a.getParentPackage();
                 if (p instanceof PlatformToolPackage) {
                     FullRevision r = ((PlatformToolPackage) p).getRevision();
+                    // If computing dependencies for a non-preview package, don't offer preview dependencies
+                    if (r.isPreview() && !rev.isPreview()) {
+                        continue;
+                    }
                     if (r.compareTo(localRev) >= compareThreshold) {
                         localRev = r;
                         localAiMax = ai;
@@ -902,6 +906,11 @@
                 Package p = a.getParentPackage();
                 if (p instanceof PlatformToolPackage) {
                     FullRevision r = ((PlatformToolPackage) p).getRevision();
+                    // If computing dependencies for a non-preview package, don't offer preview dependencies
+                    if (r.isPreview() && !rev.isPreview()) {
+                        continue;
+                    }
+
                     if (r.compareTo(localRev) >= compareThreshold) {
                         localRev = r;
                         localAiMax = null;
@@ -934,6 +943,11 @@
         for (Package p : remotePkgs) {
             if (p instanceof PlatformToolPackage) {
                 FullRevision r = ((PlatformToolPackage) p).getRevision();
+                // If computing dependencies for a non-preview package, don't offer preview dependencies
+                if (r.isPreview() && !rev.isPreview()) {
+                    continue;
+                }
+
                 if (r.compareTo(rev) >= 0) {
                     // Make sure there's at least one valid archive here
                     for (Archive a : p.getArchives()) {
@@ -1358,7 +1372,7 @@
     protected void fetchRemotePackages(
             final Collection<Package> remotePkgs,
             final SdkSource[] remoteSources) {
-        if (remotePkgs.size() > 0) {
+        if (!remotePkgs.isEmpty()) {
             return;
         }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java
index d59d9e2..8183e1b 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java
@@ -226,11 +226,11 @@
 
         Properties props = System.getProperties();
 
-        if (proxyHost != null && proxyHost.length() > 0) {
+        if (proxyHost != null && !proxyHost.isEmpty()) {
             props.setProperty(JAVA_PROP_HTTP_PROXY_HOST,  proxyHost);
             props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost);
         }
-        if (proxyPort != null && proxyPort.length() > 0) {
+        if (proxyPort != null && !proxyPort.isEmpty()) {
             props.setProperty(JAVA_PROP_HTTP_PROXY_PORT,  proxyPort);
             props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort);
         }
@@ -453,7 +453,7 @@
                     byte[] readBuffer = new byte[2048];
                     String reply = readLine(readBuffer).trim();
                     mSdkLog.info("\n");               //$NON-NLS-1$
-                    if (reply.length() > 0 && reply.length() <= 3) {
+                    if (!reply.isEmpty() && reply.length() <= 3) {
                         char c = reply.charAt(0);
                         if (c == 'y' || c == 'Y') {
                             return true;
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java
index f94dfad..c15cbf7 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java
@@ -193,7 +193,10 @@
          * @see ISettingsPage#KEY_ENABLE_PREVIEWS
          */
         public boolean getEnablePreviews() {
-            return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ENABLE_PREVIEWS));
+            return Boolean.parseBoolean(
+                    mProperties.getProperty(
+                            ISettingsPage.KEY_ENABLE_PREVIEWS,
+                            Boolean.TRUE.toString()));
         }
 
         /**
@@ -288,6 +291,7 @@
             setShowUpdateOnly(mSettings.getShowUpdateOnly());
             setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, mSettings.getAskBeforeAdbRestart());
             setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, mSettings.getUseDownloadCache());
+            setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, mSettings.getEnablePreviews());
 
         } catch (Exception e) {
             if (mSdkLog != null) {
@@ -375,11 +379,11 @@
 
         // Only change the proxy if have something in the preferences.
         // Do not erase the default settings by empty values.
-        if (proxyHost != null && proxyHost.length() > 0) {
+        if (proxyHost != null && !proxyHost.isEmpty()) {
             props.setProperty(JAVA_PROP_HTTP_PROXY_HOST,  proxyHost);
             props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost);
         }
-        if (proxyPort != null && proxyPort.length() > 0) {
+        if (proxyPort != null && !proxyPort.isEmpty()) {
             props.setProperty(JAVA_PROP_HTTP_PROXY_PORT,  proxyPort);
             props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort);
         }
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java
index 3c1c42c..c1b691c 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java
@@ -789,7 +789,7 @@
 
         Collections.sort(archives);
 
-        if (archives.size() > 0) {
+        if (!archives.isEmpty()) {
             return installArchives(archives, flags);
         }
         return null;
@@ -845,7 +845,7 @@
         List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeAll);
 
         // Filter the selected archives to only keep the ones matching the filter
-        if (pkgFilter != null && pkgFilter.size() > 0 && archives != null && archives.size() > 0) {
+        if (pkgFilter != null && !pkgFilter.isEmpty() && archives != null && !archives.isEmpty()) {
             // Map filter types to an SdkRepository Package type,
             // e.g. create a map "platform" => PlatformPackage.class
             HashMap<String, Class<? extends Package>> pkgMap =
@@ -862,7 +862,7 @@
                     Package p = a.getParentPackage();
                     if (p != null) {
                         String iid = p.installId().toLowerCase(Locale.US);
-                        if (iid != null && iid.length() > 0 && !installIdMap.containsKey(iid)) {
+                        if (iid != null && !iid.isEmpty() && !installIdMap.containsKey(iid)) {
                             installIdMap.put(iid, p);
                         }
                     }
@@ -886,7 +886,7 @@
                 if (installIdMap.containsKey(iid)) {
                     userFilteredInstallIds.add(iid);
 
-                } else if (iid.replaceAll("[0-9]+", "").length() == 0) {//$NON-NLS-1$ //$NON-NLS-2$
+                } else if (iid.replaceAll("[0-9]+", "").isEmpty()) {//$NON-NLS-1$ //$NON-NLS-2$
                     // An all-digit number is a package index requested by the user.
                     int index = Integer.parseInt(iid);
                     userFilteredIndices.put(index, index);
@@ -1144,9 +1144,9 @@
                 License lic = p.getLicense();
                 if (lic != null &&
                         lic.getLicenseRef() != null &&
-                        lic.getLicense().length() > 0 &&
+                        !lic.getLicense().isEmpty() &&
                         lic.getLicense() != null &&
-                        lic.getLicense().length() > 0) {
+                        !lic.getLicense().isEmpty()) {
                     return lic;
                 }
             }
@@ -1246,7 +1246,7 @@
      * This can be called from any thread.
      */
     public void broadcastOnSdkLoaded() {
-        if (mListeners.size() > 0) {
+        if (!mListeners.isEmpty()) {
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -1267,7 +1267,7 @@
      * This can be called from any thread.
      */
     private void broadcastOnSdkReload() {
-        if (mListeners.size() > 0) {
+        if (!mListeners.isEmpty()) {
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -1288,7 +1288,7 @@
      * This can be called from any thread.
      */
     private void broadcastPreInstallHook() {
-        if (mListeners.size() > 0) {
+        if (!mListeners.isEmpty()) {
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -1309,7 +1309,7 @@
      * This can be called from any thread.
      */
     private void broadcastPostInstallHook() {
-        if (mListeners.size() > 0) {
+        if (!mListeners.isEmpty()) {
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/License.java b/sdklib/src/main/java/com/android/sdklib/repository/License.java
index b280b56..7040ec1 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/License.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/License.java
@@ -18,6 +18,15 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.sdklib.SdkManager;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 
 /**
  * License text, with an optional license XML reference.
@@ -25,15 +34,14 @@
 public class License {
     private final String mLicense;
     private final String mLicenseRef;
+    private final String mLicenseHash;
 
-    public License(@NonNull String license) {
-        mLicense = license;
-        mLicenseRef = null;
-    }
+    private static final String LICENSE_DIR = "licenses";
 
     public License(@NonNull String license, @Nullable String licenseRef) {
         mLicense = license;
         mLicenseRef = licenseRef;
+        mLicenseHash = Hashing.sha1().hashBytes(mLicense.getBytes()).toString();
     }
 
     /** Returns the license text. Never null. */
@@ -42,6 +50,12 @@
         return mLicense;
     }
 
+    /** Returns the hash of the license text. Never null. */
+    @NonNull
+    public String getLicenseHash() {
+        return mLicenseHash;
+    }
+
     /**
      * Returns the license XML reference.
      * Could be null, e.g. in tests or synthetic packages
@@ -106,5 +120,57 @@
         }
         return true;
     }
+
+    /**
+     * Checks whether this license has previously been accepted.
+     * @param sdkRoot The root directory of the Android SDK
+     * @return true if this license has already been accepted
+     */
+    public boolean checkAccepted(@Nullable File sdkRoot) {
+        if (sdkRoot == null) {
+            return false;
+        }
+        File licenseDir = new File(sdkRoot, LICENSE_DIR);
+        File licenseFile = new File(licenseDir, mLicenseRef == null ? mLicenseHash : mLicenseRef);
+        if (!licenseFile.exists()) {
+            return false;
+        }
+        try {
+            String hash = Files.readFirstLine(licenseFile, Charsets.UTF_8);
+            return hash.equals(mLicenseHash);
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Marks this license as accepted.
+     *
+     * @param sdkRoot The root directory of the Android SDK
+     * @return true if the acceptance was persisted successfully.
+     */
+    public boolean setAccepted(@Nullable File sdkRoot) {
+        if (sdkRoot == null) {
+            return false;
+        }
+        if (checkAccepted(sdkRoot)) {
+            return true;
+        }
+        File licenseDir = new File(sdkRoot, LICENSE_DIR);
+        if (licenseDir.exists() && !licenseDir.isDirectory()) {
+            return false;
+        }
+        if (!licenseDir.exists()) {
+            licenseDir.mkdir();
+        }
+        File licenseFile = new File(licenseDir, mLicenseRef == null ? mLicenseHash : mLicenseRef);
+        try {
+            Files.write(mLicenseHash, licenseFile, Charsets.UTF_8);
+        }
+        catch (IOException e) {
+            return false;
+        }
+        return true;
+    }
 }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java b/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java
index 531a695..81e9744 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java
@@ -104,7 +104,7 @@
     @Override
     public int[] toIntArray(boolean includePreview) {
         int[] result;
-        if (mPrecision >= PRECISION_PREVIEW && isPreview()) {
+        if (mPrecision >= PRECISION_PREVIEW) {
             if (includePreview) {
                 result = new int[mPrecision];
                 result[3] = getPreview();
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java
index f69c5e1..44f6f15 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java
@@ -22,6 +22,7 @@
 
 /**
  * Public constants common to the sdk-repository and sdk-addon XML Schemas.
+ * @deprecated moved to studio
  */
 public class RepoConstants {
 
@@ -209,5 +210,4 @@
 
         return stream;
     }
-
 }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/RepoXsdUtil.java b/sdklib/src/main/java/com/android/sdklib/repository/RepoXsdUtil.java
new file mode 100644
index 0000000..805fa8a
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/repository/RepoXsdUtil.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 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.sdklib.repository;
+
+import com.google.common.collect.Lists;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * Utilities related to the respository XSDs.
+ */
+public class RepoXsdUtil {
+
+    public static final String NODE_IMPORT = "import";
+    public static final String NODE_INCLUDE = "include";
+
+    public static final String ATTR_SCHEMA_LOCATION = "schemaLocation";
+
+
+    /**
+     * Gets StreamSources for the given xsd (implied by the name and version), as well as any xsds imported or included by the main one.
+     *
+     * @param rootElement The root of the filename of the XML schema. This is by convention the same
+     *                    as the root element declared by the schema.
+     * @param version     The XML schema revision number, an integer >= 1.
+     */
+    public static StreamSource[] getXsdStream(final String rootElement, int version) {
+        String filename = String.format("%1$s-%2$02d.xsd", rootElement, version);      //$NON-NLS-1$
+        final List<StreamSource> streams = Lists.newArrayList();
+        InputStream stream = null;
+        try {
+            stream = RepoXsdUtil.class.getResourceAsStream(filename);
+            if (stream == null) {
+                filename = String.format("%1$s-%2$d.xsd", rootElement, version);      //$NON-NLS-1$
+                stream = RepoXsdUtil.class.getResourceAsStream(filename);
+            }
+            if (stream == null) {
+                // Try the alternate schemas that are not published yet.
+                // This allows us to internally test with new schemas before the
+                // public repository uses it.
+                filename = String.format("-%1$s-%2$02d.xsd", rootElement, version);      //$NON-NLS-1$
+                stream = RepoXsdUtil.class.getResourceAsStream(filename);
+            }
+
+            // Parse the schema and find any imports or includes so we can return them as well.
+            // Currently transitive includes are not supported.
+            SAXParserFactory.newInstance().newSAXParser().parse(stream, new DefaultHandler() {
+                @Override
+                public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
+                    name = name.substring(name.indexOf(':') + 1);
+                    if (name.equals(NODE_IMPORT) || name.equals(NODE_INCLUDE)) {
+                        String importFile = attributes.getValue(ATTR_SCHEMA_LOCATION);
+                        streams.add(new StreamSource(RepoXsdUtil.class.getResourceAsStream(importFile)));
+                    }
+                }
+            });
+            // create and add the first stream again, since SaxParser closes the original one
+            streams.add(new StreamSource(RepoXsdUtil.class.getResourceAsStream(filename)));
+        } catch (Exception e) {
+            // Some implementations seem to return null on failure,
+            // others throw an exception. We want to return null.
+            return null;
+        }
+        return streams.toArray(new StreamSource[streams.size()]);
+    }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java
index a3b16be..805f85b 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java
@@ -21,6 +21,7 @@
 
 /**
  * Public constants for the sdk-addon XML Schema.
+ * @deprecated moved to studio
  */
 public class SdkAddonConstants extends RepoConstants {
 
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java
index 4f6b897..3468b77 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java
@@ -21,6 +21,7 @@
 
 /**
  * Public constants for the sdk-addons-list XML Schema.
+ * @deprecated moved to studio
  */
 public class SdkAddonsListConstants {
 
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java
index b9e5f1c..7b32543 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java
@@ -25,6 +25,7 @@
 
 /**
  * Public constants for the sdk-repository XML Schema.
+ * @deprecated moved to studio
  */
 public class SdkRepoConstants extends RepoConstants {
 
@@ -32,7 +33,7 @@
      * The latest version of the sdk-repository XML Schema.
      * Valid version numbers are between 1 and this number, included.
      */
-    public static final int NS_LATEST_VERSION = 10;
+    public static final int NS_LATEST_VERSION = 11;
 
     /**
      * The min version of the sdk-repository XML Schema we'll try to load.
@@ -114,7 +115,6 @@
     /** A source package. */
     public static final String NODE_SOURCE          = "source";               //$NON-NLS-1$
 
-
     /**
      * List of possible nodes in a repository XML. Used to populate options automatically
      * in the no-GUI mode.
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java
index 22f8aa2..df3e5d6 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java
@@ -21,6 +21,7 @@
 
 /**
  * Public constants for the sdk-stats XML Schema.
+ * @deprecated moved to studio
  */
 public class SdkStatsConstants {
 
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java
index 3ea0091..d3e5752 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java
@@ -21,6 +21,7 @@
 
 /**
  * Public constants for the sdk-sys-img XML Schema.
+ * @deprecated moved to studio
  */
 public class SdkSysImgConstants extends RepoConstants {
 
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java
index afba846..5effda3 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java
@@ -83,10 +83,10 @@
     /**
      * Returns the package's revision or null. This will come from the {@link FullRevision} or
      * {@link MajorRevision}, with the precision set as appropriate.
-     * @return A non-null value if {@link #hasMajorRevision()} or {@link #hasFullRevision()}
-     * is true; otherwise a null value.
+     * @return A representation of {@link #getMajorRevision()} or {@link #getFullRevision()},
+     * depending on which one exists.
      */
-    @Nullable
+    @NonNull
     PreciseRevision getPreciseRevision();
 
   /**
@@ -140,26 +140,50 @@
 
     /**
      * Indicates whether <em>this</em> package descriptor is an update for the given
-     * existing descriptor.
+     * existing descriptor. Preview versions are never considered updates for non-
+     * previews, and vice versa.
      *
      * @param existingDesc A non-null existing descriptor.
      * @return True if this package is an update for the given one.
      */
     boolean isUpdateFor(@NonNull IPkgDesc existingDesc);
 
+  /**
+   * Indicates whether <em>this</em> package descriptor is an update for the given
+   * existing descriptor, using the given comparison method.
+   *
+   * @param existingDesc A non-null existing descriptor.
+   * @param previewComparison The {@link FullRevision.PreviewComparison} method to use
+   *                          when comparing the packages.
+   * @return True if this package is an update for the given one.
+   */
+    boolean isUpdateFor(@NonNull IPkgDesc existingDesc,
+                        @NonNull FullRevision.PreviewComparison previewComparison);
+
     /**
-     * Returns a stable string id that can be used to reference this package.
-     * @return A stable string id that can be used to reference this package.
+     * Returns a stable string id that can be used to reference this package, including
+     * a suffix indicating that this package is a preview if it is.
      */
     @NonNull
     String getInstallId();
 
     /**
+     * Returns a stable string id that can be used to reference this package, which
+     * excludes the preview suffix.
+     */
+    String getBaseInstallId();
+
+    /**
      * Returns the canonical location where such a package would be installed.
      * @param sdkLocation The root of the SDK.
      * @return the canonical location where such a package would be installed.
      */
     @NonNull
     File getCanonicalInstallFolder(@NonNull File sdkLocation);
+
+    /**
+     * @return True if the revision of this package is a preview.
+     */
+    boolean isPreview();
 }
 
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java
index 3e905a8..6c1e152 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java
@@ -23,10 +23,10 @@
 import com.android.sdklib.AndroidTargetHash;
 import com.android.sdklib.AndroidVersion;
 import com.android.sdklib.SystemImage;
-import com.android.sdklib.repository.License;
 import com.android.sdklib.io.FileOp;
 import com.android.sdklib.repository.FullRevision;
 import com.android.sdklib.repository.FullRevision.PreviewComparison;
+import com.android.sdklib.repository.License;
 import com.android.sdklib.repository.MajorRevision;
 import com.android.sdklib.repository.NoPreviewRevision;
 import com.android.sdklib.repository.PreciseRevision;
@@ -47,6 +47,7 @@
  * methods provided in the base {@link PkgDesc}.
  */
 public class PkgDesc implements IPkgDesc {
+    public static final String PREVIEW_SUFFIX = "-preview";
     private final PkgType mType;
     private final FullRevision mFullRevision;
     private final MajorRevision mMajorRevision;
@@ -186,7 +187,7 @@
         return mMajorRevision;
     }
 
-    @Nullable
+    @NonNull
     @Override
     public final PreciseRevision getPreciseRevision() {
         if (mMajorRevision == null) {
@@ -202,6 +203,11 @@
         return mAndroidVersion;
     }
 
+    @Override
+    public boolean isPreview() {
+        return getPreciseRevision().isPreview();
+    }
+
     @Nullable
     @Override
     public String getPath() {
@@ -238,10 +244,19 @@
 
     @Override
     public String getInstallId() {
+        String id = getBaseInstallId();
+        if (getPreciseRevision().isPreview()) {
+            return id + PREVIEW_SUFFIX;
+        }
+        return id;
+    }
+
+    @Override
+    public String getBaseInstallId() {
         StringBuilder sb = new StringBuilder();
 
         /* iid patterns:
-        tools, platform-tools => FOLDER / FOLDER-preview
+        tools, platform-tools => FOLDER
         build-tools => FOLDER-REV
         doc, sample, source => ENUM-API
         extra => ENUM-VENDOR.id-PATH
@@ -255,17 +270,25 @@
         case PKG_TOOLS:
         case PKG_PLATFORM_TOOLS:
             sb.append(mType.getFolderName());
-            if (getFullRevision().isPreview()) {
-                sb.append("-preview");
-            }
             break;
 
         case PKG_BUILD_TOOLS:
-            sb.append(mType.getFolderName());
-            sb.append('-').append(getFullRevision().toString());
+            sb.append(mType.getFolderName()).append('-');
+            // Add version number without the preview revision number. This is to make preview
+            // packages be updatable to the next revision.
+            int[] version = getPreciseRevision().toIntArray(false);
+            for (int i = 0; i < version.length; i++) {
+                sb.append(version[i]);
+                if (i != version.length - 1) {
+                    sb.append('.');
+                }
+            }
             break;
 
         case PKG_DOC:
+            sb.append("doc");
+            break;
+
         case PKG_SAMPLE:
         case PKG_SOURCE:
             sb.append(mType.toString().toLowerCase(Locale.US).replace("pkg_", ""));
@@ -280,7 +303,8 @@
             break;
 
         case PKG_PLATFORM:
-            sb.append(AndroidTargetHash.PLATFORM_HASH_PREFIX).append(getAndroidVersion().getApiString());
+            sb.append(AndroidTargetHash.PLATFORM_HASH_PREFIX)
+                    .append(getAndroidVersion().getApiString());
             break;
 
         case PKG_ADDON:
@@ -310,7 +334,11 @@
               .append(getVendor().getId())
               .append('-')
               .append(getAndroidVersion().getApiString());
-          break;
+            break;
+
+        case PKG_NDK:
+            sb.append("ndk");
+            break;
 
         default:
             throw new IllegalArgumentException("IID not defined for type " + mType.toString());
@@ -347,13 +375,16 @@
         case PKG_PLATFORM:
         case PKG_SAMPLE:
         case PKG_SOURCE:
-            f = FileOp.append(f, AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(getAndroidVersion().getApiString()));
+            f = FileOp.append(f, AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(
+                    getAndroidVersion().getApiString()));
             break;
 
         case PKG_SYS_IMAGE:
             f = FileOp.append(f,
-                    AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(getAndroidVersion().getApiString()),
-                    sanitize(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android" : getTag().getId()),
+                    AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(
+                            getAndroidVersion().getApiString()),
+                    sanitize(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android"
+                            : getTag().getId()),
                     sanitize(getPath()));   // path==abi
             break;
 
@@ -376,7 +407,8 @@
             break;
 
         default:
-            throw new IllegalArgumentException("CanonicalFolder not defined for type " + mType.toString());
+            throw new IllegalArgumentException(
+                    "CanonicalFolder not defined for type " + mType.toString());
         }
 
         return f;
@@ -394,10 +426,16 @@
      */
     @Override
     public boolean isUpdateFor(@NonNull IPkgDesc existingDesc) {
+        return isUpdateFor(existingDesc, PreviewComparison.COMPARE_NUMBER);
+    }
+
+    @Override
+    public boolean isUpdateFor(@NonNull IPkgDesc existingDesc,
+            @NonNull PreviewComparison previewComparison) {
         if (mCustomIsUpdateFor != null) {
             return mCustomIsUpdateFor.isUpdateFor(this, existingDesc);
         } else {
-            return isGenericUpdateFor(existingDesc);
+            return isGenericUpdateFor(existingDesc, previewComparison);
         }
     }
 
@@ -407,9 +445,11 @@
      * as needed.
      *
      * @param existingDesc A non-null package descriptor to compare with.
+     * @param previewComparison The type of preview comparison to do.
      * @return True if this package descriptor would generally update the given one.
      */
-    private boolean isGenericUpdateFor(@NonNull IPkgDesc existingDesc) {
+    private boolean isGenericUpdateFor(@NonNull IPkgDesc existingDesc,
+                                       PreviewComparison previewComparison) {
 
         if (existingDesc == null || !getType().equals(existingDesc.getType())) {
             return false;
@@ -454,7 +494,8 @@
         // Packages that have a full revision are generally updates if it increases
         // but keeps the same kind of preview (e.g. previews are only updates by previews.)
         if (hasFullRevision() &&
-                getFullRevision().isPreview() == existingDesc.getFullRevision().isPreview()) {
+                (previewComparison == PreviewComparison.IGNORE
+                        || existingDesc.isPreview() == isPreview())) {
             // If both packages match in their preview type (both previews or both not previews)
             // then is the RC/preview number an update?
             return getFullRevision().compareTo(existingDesc.getFullRevision(),
@@ -558,10 +599,13 @@
         // Flags for list description pattern string, used in PkgType:
         //      $MAJ  $FULL  $API  $PATH  $TAG  $VEND  $NAME (for extras)
 
-        result = result.replace("$MAJ",  hasMajorRevision()  ? getMajorRevision().toShortString() : "");
-        result = result.replace("$FULL", hasFullRevision()   ? getFullRevision() .toShortString() : "");
-        result = result.replace("$API",  hasAndroidVersion() ? getAndroidVersion().getApiString() : "");
-        result = result.replace("$PATH", hasPath()           ? getPath()                          : "");
+        result = result
+                .replace("$MAJ", hasMajorRevision() ? getMajorRevision().toShortString() : "");
+        result = result
+                .replace("$FULL", hasFullRevision() ? getFullRevision().toShortString() : "");
+        result = result
+                .replace("$API", hasAndroidVersion() ? getAndroidVersion().getApiString() : "");
+        result = result.replace("$PATH", hasPath() ? getPath() : "");
         result = result.replace("$TAG",  hasTag() && !getTag().equals(SystemImage.DEFAULT_TAG) ?
                                                 getTag().getDisplay() : "");
         result = result.replace("$VEND",  hasVendor() ? getVendor().getDisplay() : "");
@@ -808,7 +852,7 @@
          *
          * @param revision The revision of the tool package.
          * @param minPlatformToolsRev The {@code min-platform-tools-rev}.
-         *                  Use {@link FullRevision#NOT_SPECIFIED} to indicate there is no requirement.
+         *          Use {@link FullRevision#NOT_SPECIFIED} to indicate there is no requirement.
          * @return A {@link PkgDesc} describing this tool package.
          */
         @NonNull
@@ -849,10 +893,11 @@
                     // Generic test checks that the preview type is the same (both previews or not).
                     // Build tool is different in that the full revision must be an exact match
                     // and not an increase.
-                    return thisPkgDesc.isGenericUpdateFor(existingDesc) &&
-                        thisPkgDesc.getFullRevision().compareTo(
-                                            existingDesc.getFullRevision(),
-                                           PreviewComparison.COMPARE_TYPE) == 0;
+                    return thisPkgDesc
+                            .isGenericUpdateFor(existingDesc, PreviewComparison.COMPARE_NUMBER) &&
+                            thisPkgDesc.getFullRevision().compareTo(
+                                    existingDesc.getFullRevision(),
+                                    PreviewComparison.COMPARE_TYPE) == 0;
                 }
             };
             return p;
@@ -903,7 +948,7 @@
         @NonNull
         public static Builder newExtra(@NonNull  IdDisplay vendor,
                                        @NonNull  String path,
-                                       @NonNull  String displayName,
+                                       @Nullable  String displayName,
                                        @Nullable String[] oldPaths,
                                        @NonNull  NoPreviewRevision revision) {
             Builder p = new Builder(PkgType.PKG_EXTRA);
@@ -1057,6 +1102,19 @@
             return p;
         }
 
+        /**
+         * Creates a new NDK package descriptor.
+         *
+         * @param revision The revision of the NDK package.
+         * @return A {@link PkgDesc} describing this NDK package.
+         */
+        @NonNull
+        public static Builder newNdk(@NonNull FullRevision revision) {
+            Builder p = new Builder(PkgType.PKG_NDK);
+            p.mFullRevision = revision;
+            return p;
+        }
+
         public Builder setLicense(@Nullable License license) {
             mLicense = license;
             return this;
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java
index d2c02f6..b5f2efb 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java
@@ -45,7 +45,7 @@
                  @Nullable IdDisplay vendor,
                  @Nullable FullRevision minToolsRev,
                  @Nullable FullRevision minPlatformToolsRev,
-                 @NonNull  String nameDisplay,
+                 @Nullable  String nameDisplay,
                  @Nullable final String[] oldPaths) {
         super(type,
               license,
@@ -76,7 +76,7 @@
     @NonNull
     @Override
     public String getNameDisplay() {
-        return mNameDisplay;
+        return mNameDisplay == null ? String.format("Unknown (%s)", getInstallId()) : mNameDisplay;
     }
 
     // ---- Helpers ----
@@ -90,7 +90,7 @@
      */
     @NonNull
     public static String[] convertOldPaths(@Nullable String oldPathsProperty) {
-        if (oldPathsProperty == null || oldPathsProperty.length() == 0) {
+        if (oldPathsProperty == null || oldPathsProperty.isEmpty()) {
             return new String[0];
         }
         return oldPathsProperty.split(";");  //$NON-NLS-1$
@@ -147,7 +147,7 @@
         if (otherPath != null && thisPath != null && thisVendor != null) {
             if (otherPath.equals(thisVendor + '-' + thisPath) &&
                     (otherVendor == null ||
-                     otherVendor.length() == 0 ||
+                            otherVendor.isEmpty() ||
                      otherVendor.equals(thisVendor))) {
                 return true;
             }
@@ -155,7 +155,7 @@
         if (thisPath != null && otherPath != null && otherVendor != null) {
             if (thisPath.equals(otherVendor + '-' + otherPath) &&
                     (thisVendor == null ||
-                     thisVendor.length() == 0 ||
+                            thisVendor.isEmpty() ||
                      thisVendor.equals(otherVendor))) {
                 return true;
             }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java
index 251f627..9f0ccc2 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java
@@ -64,7 +64,7 @@
     /** Filter the SDK/docs folder.
      *  Has {@link MajorRevision}. */
     PKG_DOC(0x0010, SdkConstants.FD_DOCS,
-            "Documentation for Android SDK $API{?$MAJ>1:, rev $MAJ}",
+            "Documentation for Android SDK",
             true /*maj-r*/, false, true /*api*/, false, false, false, false, false),
 
     /** Filter the SDK/platforms.
@@ -114,7 +114,11 @@
      *  Cast the descriptor to {@link IPkgDescExtra} to get extra's specific attributes. */
     PKG_EXTRA(0x4000, SdkConstants.FD_EXTRAS,
             "{|$NAME|$VEND $PATH|}{?$FULL>1:, rev $FULL}",
-            false, true /*full-r*/, false, true /*path*/, false, true /*vend*/, false, false);
+            false, true /*full-r*/, false, true /*path*/, false, true /*vend*/, false, false),
+
+    /** The SDK/ndk folder. */
+    PKG_NDK(0x8000, SdkConstants.FD_NDK, "",
+                    false, true, false, false, false, false, false, false);
 
     /** A collection of all the known PkgTypes. */
     public static final EnumSet<PkgType> PKG_ALL = EnumSet.allOf(PkgType.class);
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java
index f378837..7dda921 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java
@@ -54,7 +54,8 @@
     private static final Pattern PATTERN_USB_IDS = Pattern.compile(
            "^0x[a-f0-9]{4}$", Pattern.CASE_INSENSITIVE);                    //$NON-NLS-1$
 
-    private final @NonNull IPkgDescAddon mAddonDesc;
+    @NonNull
+    private final IPkgDescAddon mAddonDesc;
 
     public LocalAddonPkgInfo(@NonNull LocalSdk localSdk,
                              @NonNull File localDir,
@@ -234,7 +235,7 @@
 
             Map<String, File> skinsMap = new TreeMap<String, File>();
 
-            for (File f : parseSkinFolder(targetSkinFolder)) {
+            for (File f : PackageParserUtils.parseSkinFolder(targetSkinFolder, fileOp)) {
                 skinsMap.put(f.getName().toLowerCase(Locale.US), f);
             }
             for (ISystemImage si : systemImages) {
@@ -446,20 +447,7 @@
                 final IdDisplay tag = mAddonDesc.getName();
                 final String abi = d.getPath();
                 if (abi != null && !tagToAbiFound.containsEntry(tag, abi)) {
-                    List<File> parsedSkins = parseSkinFolder(
-                            new File(pkg.getLocalDir(), SdkConstants.FD_SKINS));
-                    File[] skins = FileOp.EMPTY_FILE_ARRAY;
-                    if (!parsedSkins.isEmpty()) {
-                        skins = parsedSkins.toArray(new File[parsedSkins.size()]);
-                    }
-
-                    found.add(new SystemImage(
-                            pkg.getLocalDir(),
-                            LocationType.IN_SYSTEM_IMAGE,
-                            tag,
-                            mAddonDesc.getVendor(),
-                            abi,
-                            skins));
+                    found.add(((LocalAddonSysImgPkgInfo)pkg).getSystemImage());
                     tagToAbiFound.put(tag, abi);
                 }
             }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java
index 60c9cf5..e02c8e9 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java
@@ -19,6 +19,7 @@
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.ISystemImage;
 import com.android.sdklib.repository.MajorRevision;
 import com.android.sdklib.repository.descriptors.IPkgDesc;
 import com.android.sdklib.repository.descriptors.IdDisplay;
@@ -36,7 +37,8 @@
 public class LocalAddonSysImgPkgInfo extends LocalPkgInfo {
 
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalAddonSysImgPkgInfo(@NonNull LocalSdk localSdk,
                               @NonNull File localDir,
@@ -56,4 +58,8 @@
     public IPkgDesc getDesc() {
         return mDesc;
     }
+
+    public ISystemImage getSystemImage() {
+        return LocalSysImgPkgInfo.getSystemImage(mDesc, getLocalDir(), getLocalSdk().getFileOp());
+    }
 }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java
index f40c34a..0251cc3 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java
@@ -29,8 +29,10 @@
 public class LocalBuildToolPkgInfo extends LocalPkgInfo {
 
 
-    private final @Nullable BuildToolInfo mBuildToolInfo;
-    private final @NonNull  IPkgDesc mDesc;
+    @Nullable
+    private final BuildToolInfo mBuildToolInfo;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalBuildToolPkgInfo(@NonNull LocalSdk localSdk,
                                  @NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java
index 23b68d1..7e3a791 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java
@@ -224,7 +224,7 @@
             return mDir.equals(((MapComparator) obj).mDir);
         }
         return false;
-    };
+    }
 
     /**
      * Helper for Map.contains() to make sure we're comparing the inner directory File
@@ -270,6 +270,6 @@
                 return mDir.equals(((MapComparator) obj).mDir);
             }
             return false;
-        };
+        }
     }
 }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java
index 56aca59..004f450 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java
@@ -27,7 +27,8 @@
 
 public class LocalDocPkgInfo extends LocalPkgInfo {
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalDocPkgInfo(@NonNull LocalSdk localSdk,
                            @NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java
index 7aaf69d..5ec811b 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java
@@ -29,7 +29,8 @@
 
 public class LocalExtraPkgInfo extends LocalPkgInfo {
 
-    private final @NonNull IPkgDescExtra mDesc;
+    @NonNull
+    private final IPkgDescExtra mDesc;
 
     public LocalExtraPkgInfo(@NonNull  LocalSdk localSdk,
                              @NonNull  File localDir,
@@ -76,7 +77,7 @@
         // and that "vendor" would end up in the path when we reload the extra from
         // disk. Detect this and compensate.
         String disp = vendor == null ? null : vendor.getDisplay();
-        if (disp != null && disp.length() > 0) {
+        if (disp != null && !disp.isEmpty()) {
             if (name.startsWith(disp + "-")) {  //$NON-NLS-1$
                 name = name.substring(disp.length() + 1);
             }
@@ -86,11 +87,11 @@
         if (name != null) {
             name = name.replaceAll("[ _\t\f-]+", " ").trim();   //$NON-NLS-1$ //$NON-NLS-2$
         }
-        if (name == null || name.length() == 0) {
+        if (name == null || name.isEmpty()) {
             name = "Unknown Extra";
         }
 
-        if (disp != null && disp.length() > 0) {
+        if (disp != null && !disp.isEmpty()) {
             name = disp + " " + name;  //$NON-NLS-1$
             name = name.replaceAll("[ _\t\f-]+", " ").trim();   //$NON-NLS-1$ //$NON-NLS-2$
         }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalNdkPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalNdkPkgInfo.java
new file mode 100644
index 0000000..8858300
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalNdkPkgInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 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.sdklib.repository.local;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.repository.FullRevision;
+import com.android.sdklib.repository.descriptors.IPkgDesc;
+import com.android.sdklib.repository.descriptors.PkgDesc;
+
+import java.io.File;
+import java.util.Properties;
+
+/**
+ * Local package representing the Android NDK
+ */
+public class LocalNdkPkgInfo extends LocalPkgInfo {
+  @NonNull
+  private final IPkgDesc mDesc;
+
+  protected LocalNdkPkgInfo(@NonNull LocalSdk localSdk,
+                            @NonNull File localDir,
+                            @NonNull Properties sourceProps,
+                            @NonNull FullRevision revision) {
+    super(localSdk, localDir, sourceProps);
+    mDesc = PkgDesc.Builder.newNdk(revision).setDescriptionShort("Android NDK").setListDisplay("Android NDK").create();
+  }
+
+  @NonNull
+  @Override
+  public IPkgDesc getDesc() {
+    return mDesc;
+  }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java
index d3781ff..534677e 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java
@@ -19,12 +19,15 @@
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
 import com.android.sdklib.AndroidVersion;
 import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
 import com.android.sdklib.ISystemImage;
 import com.android.sdklib.ISystemImage.LocationType;
 import com.android.sdklib.SdkManager.LayoutlibVersion;
 import com.android.sdklib.SystemImage;
+import com.android.sdklib.internal.androidTarget.OptionalLibraryImpl;
 import com.android.sdklib.internal.androidTarget.PlatformTarget;
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.io.FileOp;
@@ -36,12 +39,27 @@
 import com.android.sdklib.repository.descriptors.IdDisplay;
 import com.android.sdklib.repository.descriptors.PkgDesc;
 import com.android.sdklib.repository.descriptors.PkgType;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.TreeMultimap;
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.util.*;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
 
 @SuppressWarnings("ConstantConditions")
 public class LocalPlatformPkgInfo extends LocalPkgInfo {
@@ -238,10 +256,11 @@
                 layoutlibVersion,
                 systemImages,
                 platformProp,
+                getOptionalLibraries(platformFolder),
                 sdk.getLatestBuildTool());
 
         // add the skins from the platform. Make a copy to not modify the original collection.
-        List<File> skins = new ArrayList<File>(parseSkinFolder(pt.getFile(IAndroidTarget.SKINS)));
+        List<File> skins = new ArrayList<File>(PackageParserUtils.parseSkinFolder(pt.getFile(IAndroidTarget.SKINS), fileOp));
 
         // add the system-image specific skins, if any.
         for (ISystemImage systemImage : systemImages) {
@@ -299,19 +318,7 @@
                 IdDisplay tag = d.getTag();
                 String abi = d.getPath();
                 if (tag != null && abi != null && !tagToAbiFound.containsEntry(tag, abi)) {
-                    List<File> parsedSkins = parseSkinFolder(
-                            new File(pkg.getLocalDir(), SdkConstants.FD_SKINS));
-                    File[] skins = FileOp.EMPTY_FILE_ARRAY;
-                    if (!parsedSkins.isEmpty()) {
-                        skins = parsedSkins.toArray(new File[parsedSkins.size()]);
-                    }
-
-                    found.add(new SystemImage(
-                            pkg.getLocalDir(),
-                            LocationType.IN_SYSTEM_IMAGE,
-                            tag,
-                            abi,
-                            skins));
+                    found.add(((LocalSysImgPkgInfo)pkg).getSystemImage());
                     tagToAbiFound.put(tag, abi);
                 }
             }
@@ -362,39 +369,57 @@
         return found.toArray(new ISystemImage[found.size()]);
     }
 
-    /**
-     * Parses the skin folder and builds the skin list.
-     * @param skinRootFolder The path to the skin root folder.
-     */
-    @NonNull
-    protected List<File> parseSkinFolder(@NonNull File skinRootFolder) {
-        IFileOp fileOp = getLocalSdk().getFileOp();
-
-        if (fileOp.isDirectory(skinRootFolder)) {
-            ArrayList<File> skinList = new ArrayList<File>();
-
-            File[] files = fileOp.listFiles(skinRootFolder);
-
-            for (File skinFolder : files) {
-                if (fileOp.isDirectory(skinFolder)) {
-                    // check for layout file
-                    File layout = new File(skinFolder, SdkConstants.FN_SKIN_LAYOUT);
-
-                    if (fileOp.isFile(layout)) {
-                        // for now we don't parse the content of the layout and
-                        // simply add the directory to the list.
-                        skinList.add(skinFolder);
-                    }
-                }
-            }
-
-            Collections.sort(skinList);
-            return skinList;
+    private List<OptionalLibrary> getOptionalLibraries(@NonNull File platformDir) {
+        File optionalDir = new File(platformDir, "optional");
+        if (!optionalDir.isDirectory()) {
+            return Collections.emptyList();
         }
 
-        return Collections.emptyList();
+        File optionalJson = new File(optionalDir, "optional.json");
+        if (!optionalJson.isFile()) {
+            return Collections.emptyList();
+        }
+
+        return getLibsFromJson(optionalJson);
     }
 
+    public static class Library {
+        String name;
+        String jar;
+        boolean manifest;
+    }
+
+
+    @VisibleForTesting
+    static List<OptionalLibrary> getLibsFromJson(@NonNull File jsonFile) {
+
+        Gson gson = new Gson();
+
+        try {
+            Type collectionType = new TypeToken<Collection<Library>>() {
+            }.getType();
+            Collection<Library> libs = gson
+                    .fromJson(Files.newReader(jsonFile, Charsets.UTF_8), collectionType);
+
+            // convert into the right format.
+            List<OptionalLibrary> optionalLibraries = Lists.newArrayListWithCapacity(libs.size());
+
+            File rootFolder = jsonFile.getParentFile();
+            for (Library lib : libs) {
+                optionalLibraries.add(new OptionalLibraryImpl(
+                        lib.name,
+                        new File(rootFolder, lib.jar),
+                        lib.name,
+                        lib.manifest));
+            }
+
+            return optionalLibraries;
+        } catch (FileNotFoundException e) {
+            // shouldn't happen since we've checked the file is here, but can happen in
+            // some cases (too many files open).
+            return Collections.emptyList();
+        }
+    }
 
     /** List of items in the platform to check when parsing it. These paths are relative to the
      * platform root folder. */
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java
index cd66b1c..281c64e 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java
@@ -26,7 +26,8 @@
 
 public class LocalPlatformToolPkgInfo extends LocalPkgInfo {
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalPlatformToolPkgInfo(@NonNull LocalSdk localSdk,
                                     @NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java
index d4ed505..d8159d6 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java
@@ -33,7 +33,8 @@
  */
 public class LocalSamplePkgInfo extends LocalPkgInfo {
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalSamplePkgInfo(@NonNull LocalSdk localSdk,
                               @NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java
index 04d298b..144ed5b 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java
@@ -22,11 +22,9 @@
 import com.android.annotations.VisibleForTesting;
 import com.android.annotations.VisibleForTesting.Visibility;
 import com.android.annotations.concurrency.GuardedBy;
-import com.android.sdklib.AndroidTargetHash;
-import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.*;
 import com.android.sdklib.AndroidVersion.AndroidVersionException;
-import com.android.sdklib.BuildToolInfo;
-import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.internal.androidTarget.MissingTarget;
 import com.android.sdklib.io.FileOp;
 import com.android.sdklib.io.IFileOp;
 import com.android.sdklib.repository.FullRevision;
@@ -37,21 +35,12 @@
 import com.android.sdklib.repository.descriptors.IdDisplay;
 import com.android.sdklib.repository.descriptors.PkgDescExtra;
 import com.android.sdklib.repository.descriptors.PkgType;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.TreeMultimap;
+import com.google.common.collect.*;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Properties;
+import java.util.*;
 
 /**
  * This class keeps information on the current locally installed SDK.
@@ -185,8 +174,8 @@
     private BuildToolInfo mLegacyBuildTools;
     /** Cache of targets from local sdk. See {@link #getTargets()}. */
     @GuardedBy(value="mLocalPackages")
-    private final List<IAndroidTarget> mCachedTargets = new ArrayList<IAndroidTarget>();
-    private boolean mReloadTargets = true;
+    private List<IAndroidTarget> mCachedTargets = null;
+    private Set<MissingTarget> mCachedMissingTargets = null;
 
     /**
      * Creates an initial LocalSdk instance with an unknown location.
@@ -270,11 +259,12 @@
                 mVisitedDirs.removeAll(filter);
                 mLocalPackages.removeAll(filter);
             }
-        }
 
-        // Clear the targets if the platforms or addons are being cleared
-        if (filters.contains(PkgType.PKG_PLATFORM) ||  filters.contains(PkgType.PKG_ADDON)) {
-          mReloadTargets = true;
+            // Clear the targets if the platforms or addons are being cleared
+            if (filters.contains(PkgType.PKG_PLATFORM) ||  filters.contains(PkgType.PKG_ADDON)) {
+                mCachedMissingTargets = null;
+                mCachedTargets = null;
+            }
         }
     }
 
@@ -453,14 +443,11 @@
      */
     @Nullable
     public LocalPkgInfo getPkgInfo(@NonNull PkgType filter) {
-
-        assert filter == PkgType.PKG_TOOLS ||
-               filter == PkgType.PKG_PLATFORM_TOOLS ||
-               filter == PkgType.PKG_DOC;
-
         if (filter != PkgType.PKG_TOOLS &&
             filter != PkgType.PKG_PLATFORM_TOOLS &&
-            filter != PkgType.PKG_DOC) {
+            filter != PkgType.PKG_DOC &&
+            filter != PkgType.PKG_NDK) {
+            assert false;
             return null;
         }
 
@@ -468,7 +455,7 @@
         synchronized (mLocalPackages) {
             Collection<LocalPkgInfo> existing = mLocalPackages.get(filter);
             assert existing.size() <= 1;
-            if (existing.size() > 0) {
+            if (!existing.isEmpty()) {
                 return existing.iterator().next();
             }
 
@@ -485,6 +472,8 @@
                 case PKG_DOC:
                     info = scanDoc(uniqueDir);
                     break;
+                case PKG_NDK:
+                    info = scanNdk(uniqueDir);
                 default:
                     break;
                 }
@@ -544,7 +533,8 @@
         for (PkgType filter : filters) {
             if (filter == PkgType.PKG_TOOLS ||
                     filter == PkgType.PKG_PLATFORM_TOOLS ||
-                    filter == PkgType.PKG_DOC) {
+                    filter == PkgType.PKG_DOC ||
+                    filter == PkgType.PKG_NDK) {
                 LocalPkgInfo info = getPkgInfo(filter);
                 if (info != null) {
                     list.add(info);
@@ -598,6 +588,7 @@
                         case PKG_TOOLS:
                         case PKG_PLATFORM_TOOLS:
                         case PKG_DOC:
+                        case PKG_NDK:
                             break;
                         default:
                             throw new IllegalArgumentException(
@@ -711,26 +702,71 @@
     @NonNull
     public IAndroidTarget[] getTargets() {
         synchronized (mLocalPackages) {
-            if (mReloadTargets) {
+            if (mCachedTargets == null) {
+                List<IAndroidTarget> result = Lists.newArrayList();
                 LocalPkgInfo[] pkgsInfos = getPkgsInfos(EnumSet.of(PkgType.PKG_PLATFORM,
                                                                    PkgType.PKG_ADDON));
-                int n = pkgsInfos.length;
-                mCachedTargets.clear();
-                for (int i = 0; i < n; i++) {
-                    LocalPkgInfo info = pkgsInfos[i];
+                for (LocalPkgInfo info : pkgsInfos) {
                     assert info instanceof LocalPlatformPkgInfo;
-                    if (info instanceof LocalPlatformPkgInfo) {
-                        IAndroidTarget target = ((LocalPlatformPkgInfo) info).getAndroidTarget();
-                        if (target != null) {
-                            mCachedTargets.add(target);
-                        }
+                    IAndroidTarget target = ((LocalPlatformPkgInfo) info).getAndroidTarget();
+                    if (target != null) {
+                        result.add(target);
                     }
                 }
+                mCachedTargets = result;
             }
             return mCachedTargets.toArray(new IAndroidTarget[mCachedTargets.size()]);
         }
     }
 
+    public IAndroidTarget[] getTargets(boolean includeMissing) {
+        IAndroidTarget[] result = getTargets();
+        if (includeMissing) {
+            result = ObjectArrays.concat(result, getMissingTargets(), IAndroidTarget.class);
+        }
+        return result;
+    }
+
+    @NonNull
+    public IAndroidTarget[] getMissingTargets() {
+        synchronized (mLocalPackages) {
+            if (mCachedMissingTargets == null) {
+                Map<MissingTarget, MissingTarget> result = Maps.newHashMap();
+                Set<ISystemImage> seen = Sets.newHashSet();
+                for (IAndroidTarget target : getTargets()) {
+                    Collections.addAll(seen, target.getSystemImages());
+                }
+                for (LocalPkgInfo local : getPkgsInfos(PkgType.PKG_ADDON_SYS_IMAGE)) {
+                    LocalAddonSysImgPkgInfo info = (LocalAddonSysImgPkgInfo)local;
+                    ISystemImage image = info.getSystemImage();
+                    if (!seen.contains(image)) {
+                        addOrphanedSystemImage(image, info.getDesc(), result);
+                    }
+                }
+                for (LocalPkgInfo local : getPkgsInfos(PkgType.PKG_SYS_IMAGE)) {
+                    LocalSysImgPkgInfo info = (LocalSysImgPkgInfo)local;
+                    ISystemImage image = info.getSystemImage();
+                    if (!seen.contains(image)) {
+                        addOrphanedSystemImage(image, info.getDesc(), result);
+                    }
+                }
+                mCachedMissingTargets = result.keySet();
+            }
+            return mCachedMissingTargets.toArray(new IAndroidTarget[mCachedMissingTargets.size()]);
+        }
+    }
+
+    private static void addOrphanedSystemImage(ISystemImage image, IPkgDesc desc, Map<MissingTarget, MissingTarget> targets) {
+        IdDisplay vendor = desc.getVendor();
+        MissingTarget target = new MissingTarget(vendor == null ? null : vendor.getDisplay(), desc.getTag().getDisplay(), desc.getAndroidVersion());
+        MissingTarget existing = targets.get(target);
+        if (existing == null) {
+            existing = target;
+            targets.put(target, target);
+        }
+        existing.addSystemImage(image);
+    }
+
     /**
      * Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}.
      *
@@ -740,7 +776,7 @@
     @Nullable
     public IAndroidTarget getTargetFromHashString(@Nullable String hash) {
         if (hash != null) {
-            IAndroidTarget[] targets = getTargets();
+            IAndroidTarget[] targets = getTargets(true);
             for (IAndroidTarget target : targets) {
                 if (target != null && hash.equals(AndroidTargetHash.getTargetHashString(target))) {
                     return target;
@@ -843,6 +879,23 @@
     }
 
     /**
+     * Try to find an NDK package at the given location.
+     * Returns null if not found.
+     */
+    @Nullable
+    private LocalNdkPkgInfo scanNdk(@NonNull File ndkFolder) {
+        // Can we find some properties?
+        Properties props = parseProperties(new File(ndkFolder, SdkConstants.FN_SOURCE_PROP));
+        FullRevision rev = PackageParserUtils.getPropertyFull(props, PkgProps.PKG_REVISION);
+        if (rev == null) {
+            return null;
+        }
+
+        return new LocalNdkPkgInfo(this, ndkFolder, props, rev);
+    }
+
+
+    /**
      * Helper used by scanXyz methods below to check whether a directory should be visited.
      * It can be skipped if it's not a directory or if it's already marked as visited in
      * mVisitedDirs for the given package type -- in which case the directory is added to
@@ -1167,7 +1220,7 @@
                 props.load(fis);
 
                 // To be valid, there must be at least one property in it.
-                if (props.size() > 0) {
+                if (!props.isEmpty()) {
                     return props;
                 }
             }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java
index 20ce2bc..eb822de 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java
@@ -32,7 +32,8 @@
  */
 public class LocalSourcePkgInfo extends LocalPkgInfo {
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalSourcePkgInfo(@NonNull LocalSdk localSdk,
                               @NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java
index fc52a2f..f9e8148 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java
@@ -16,17 +16,25 @@
 
 package com.android.sdklib.repository.local;
 
+import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
 import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.ISystemImage;
 import com.android.sdklib.SystemImage;
+import com.android.sdklib.io.FileOp;
+import com.android.sdklib.io.IFileOp;
 import com.android.sdklib.repository.MajorRevision;
 import com.android.sdklib.repository.PkgProps;
 import com.android.sdklib.repository.descriptors.IPkgDesc;
 import com.android.sdklib.repository.descriptors.IdDisplay;
 import com.android.sdklib.repository.descriptors.PkgDesc;
+import com.google.common.base.Objects;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.Properties;
 
@@ -39,15 +47,16 @@
 public class LocalSysImgPkgInfo extends LocalPkgInfo {
 
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalSysImgPkgInfo(@NonNull  LocalSdk localSdk,
-                              @NonNull  File localDir,
-                              @NonNull  Properties sourceProps,
-                              @NonNull  AndroidVersion version,
-                              @Nullable IdDisplay tag,
-                              @NonNull  String abi,
-                              @NonNull  MajorRevision revision) {
+      @NonNull  File localDir,
+      @NonNull  Properties sourceProps,
+      @NonNull  AndroidVersion version,
+      @Nullable IdDisplay tag,
+      @NonNull  String abi,
+      @NonNull  MajorRevision revision) {
         super(localSdk, localDir, sourceProps);
         mDesc = PkgDesc.Builder.newSysImg(version, tag, abi, revision).create();
     }
@@ -65,8 +74,7 @@
     @NonNull
     public static IdDisplay extractTagFromProps(Properties props) {
         if (props != null) {
-            String tagId   = props.getProperty(PkgProps.SYS_IMG_TAG_ID,
-                                               SystemImage.DEFAULT_TAG.getId());
+            String tagId   = props.getProperty(PkgProps.SYS_IMG_TAG_ID, SystemImage.DEFAULT_TAG.getId());
             String tagDisp = props.getProperty(PkgProps.SYS_IMG_TAG_DISPLAY, "");      //$NON-NLS-1$
             if (tagDisp == null || tagDisp.isEmpty()) {
                 tagDisp = tagIdToDisplay(tagId);
@@ -92,7 +100,7 @@
         name = name.replaceAll(" +", " ");                  //$NON-NLS-1$ //$NON-NLS-2$
         name = name.trim();
 
-        if (name.length() > 0) {
+        if (!name.isEmpty()) {
             char c = name.charAt(0);
             if (!Character.isUpperCase(c)) {
                 StringBuilder sb = new StringBuilder(name);
@@ -103,6 +111,25 @@
         return name;
     }
 
-    // TODO create package on demand if needed. This might not be needed
-    // since typically system-images are retrieved via IAndroidTarget.
+    public SystemImage getSystemImage() {
+        return getSystemImage(mDesc, getLocalDir(), getLocalSdk().getFileOp());
+    }
+
+    static SystemImage getSystemImage(IPkgDesc desc, File localDir, @NonNull IFileOp fileOp) {
+        final IdDisplay tag = desc.getTag();
+        final String abi = desc.getPath();
+        List<File> parsedSkins = PackageParserUtils.parseSkinFolder(new File(localDir, SdkConstants.FD_SKINS), fileOp);
+        File[] skins = FileOp.EMPTY_FILE_ARRAY;
+        if (!parsedSkins.isEmpty()) {
+            skins = parsedSkins.toArray(new File[parsedSkins.size()]);
+        }
+
+        return new SystemImage(
+          localDir,
+          ISystemImage.LocationType.IN_SYSTEM_IMAGE,
+          tag,
+          desc.getVendor(),
+          abi,
+          skins);
+    }
 }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java
index 94a4e95..91306ea 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java
@@ -26,7 +26,8 @@
 
 public class LocalToolPkgInfo extends LocalPkgInfo {
 
-    private final @NonNull IPkgDesc mDesc;
+    @NonNull
+    private final IPkgDesc mDesc;
 
     public LocalToolPkgInfo(@NonNull LocalSdk localSdk,
                             @NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java b/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java
index 09cecbb..e8cc4b2 100644
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java
@@ -15,13 +15,19 @@
  */
 package com.android.sdklib.repository.local;
 
+import com.android.SdkConstants;
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
+import com.android.sdklib.io.IFileOp;
 import com.android.sdklib.repository.FullRevision;
 import com.android.sdklib.repository.MajorRevision;
 import com.android.sdklib.repository.NoPreviewRevision;
 import com.android.sdklib.repository.PkgProps;
 
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Properties;
 
 /**
@@ -124,4 +130,37 @@
     }
     return props.getProperty(propKey, defaultValue);
   }
+
+
+  /**
+   * Parses the skin folder and builds the skin list.
+   * @param skinRootFolder The path to the skin root folder.
+   */
+  @NonNull
+  public static List<File> parseSkinFolder(@NonNull File skinRootFolder, @NonNull IFileOp fileOp) {
+    if (fileOp.isDirectory(skinRootFolder)) {
+      ArrayList<File> skinList = new ArrayList<File>();
+
+      File[] files = fileOp.listFiles(skinRootFolder);
+
+      for (File skinFolder : files) {
+        if (fileOp.isDirectory(skinFolder)) {
+          // check for layout file
+          File layout = new File(skinFolder, SdkConstants.FN_SKIN_LAYOUT);
+
+          if (fileOp.isFile(layout)) {
+            // for now we don't parse the content of the layout and
+            // simply add the directory to the list.
+            skinList.add(skinFolder);
+          }
+        }
+      }
+
+      Collections.sort(skinList);
+      return skinList;
+    }
+
+    return Collections.emptyList();
+  }
+
 }
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-11.xsd b/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-11.xsd
new file mode 100755
index 0000000..8e9f736
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-11.xsd
@@ -0,0 +1,680 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2014 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.
+-->
+<xsd:schema
+    targetNamespace="http://schemas.android.com/sdk/android/repository/11"
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+    xmlns:sdk="http://schemas.android.com/sdk/android/repository/11"
+    elementFormDefault="qualified"
+    attributeFormDefault="unqualified"
+    version="1">
+
+    <!-- The repository contains a collection of downloadable items known as
+         "packages". Each package has a type and various attributes and contains
+         a list of file "archives" that can be downloaded for specific OSes.
+
+         An Android SDK repository is a web site that contains a "repository.xml"
+         file that conforms to this XML Schema.
+
+         History:
+         - v1 is used by the SDK Updater in Tools r3 and r4.
+
+         - v2 is used by the SDK Updater in Tools r5:
+            - It introduces a new <sample> repository type. Previously samples
+              were included in the <platform> packages. Instead this package is used
+              and and the samples are installed in $SDK/samples.
+            - All repository types have a new <obsolete> node. It works as a marker
+              to indicate the package is obsolete and should not be selected by default.
+              The UI also hides these out by default.
+
+         - v3 is used by the SDK Updater in Tools r8:
+            - It introduces a new <platform-tool> repository type. Previously platform-specific
+              tools were included in the <platform> packages. Instead this package is used
+              and platform-specific tools are installed in $SDK/platform-tools
+            - There's a new element <min-platform-tools-rev> in <tool>. The tool package now
+              requires that at least some minimal version of <platform-tool> be installed.
+            - It removes the <addon> repository type, which is now in its own XML Schema.
+
+         - v4 is used by the SDK Updater in Tools r12:
+            - <extra> element now has a <project-files> element that contains 1 or
+              or more <path>, each indicating the relative path of a file that this package
+              can contribute to installed projects.
+            - <platform> element now has a mandatory <layoutlib> that indicates the API
+              and revision of that layout library for this particular platform.
+
+         - v5 is used by the SDK Manager in Tools r14:
+            - <extra> now has an <old-paths> element, a ;-separated list of old paths that
+              should be detected and migrated to the new <path> for that package.
+            - <platform> has a new optional <abi-included> that describes the ABI of the
+              system image included in the platform, if any.
+            - New <system-image> package type, to store system images outside of <platform>s.
+            - New <source> package type.
+
+         - v6 is used by the SDK Manager in Tools r18:
+            - <extra> packages are removed. They are served only by the addon XML.
+            - <platform>, <system-image>, <source>, <tool>, <platform-tool>, <doc>
+              and <sample> get a new optional field <beta-rc> which can be used to indicate
+              the package is a Beta Release Candidate and not a final release.
+
+         - v7 is used by the SDK Manager in Tools r20:
+            - For <tool> and <platform-tool> packages, the <revision> element becomes a
+              a "full revision" element with <major>, <minor>, <micro> and <preview> sub-elements.
+            - The <beta-rc> element is no longer supported, it is replaced by
+              <revision> -> <preview> and is only for <tool> and <platform-tool> packages.
+            - <min-tools-rev> and <min-platform-tools-rev> also become a full revision element.
+
+         - v8 is used by the SDK Manager in Tools r22.0:
+            - It introduces the new <build-tool> repository type, which contains build-specific
+              tools that were before placed in either <tool> or <platform-tool> (e.g. ant,
+              aapt, aidl, etc.)
+            - <tool> has a new "min-build-tool" attribute.
+
+         - v9 is used by the SDK Manager in Tools r22.6:
+            - It introduces a sub-tag for the <system-image> repository type.
+
+         - v10 is used by the SDK Manager in Tools r22.6.4:
+            - It introduces a <list-display> string for all package types.
+            - It removes the <system-image> type (system images are handled by sdk-sys-img-3.xsd)
+            - it changes <archive os=... arch=...> to sub-elements: <host-os>, <host-bits>,
+              <jvm-bits> and <min-jvm-version>.
+    -->
+
+    <xsd:element name="sdk-repository" type="sdk:repositoryType" />
+
+    <xsd:complexType name="repositoryType">
+        <xsd:annotation>
+            <xsd:documentation>
+                The repository contains a collection of downloadable packages.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:choice minOccurs="0" maxOccurs="unbounded">
+            <xsd:element name="platform"        type="sdk:platformType"     />
+            <xsd:element name="source"          type="sdk:sourceType"       />
+            <xsd:element name="tool"            type="sdk:toolType"         />
+            <xsd:element name="platform-tool"   type="sdk:platformToolType" />
+            <xsd:element name="build-tool"      type="sdk:buildToolType"    />
+            <xsd:element name="doc"             type="sdk:docType"          />
+            <xsd:element name="sample"          type="sdk:sampleType"       />
+            <xsd:element name="license"         type="sdk:licenseType"      />
+            <xsd:element name="ndk"             type="sdk:ndkType"      />
+        </xsd:choice>
+    </xsd:complexType>
+
+    <!-- The definition of an NDK package. -->
+
+    <xsd:complexType name="ndkType">
+        <xsd:annotation>
+            <xsd:documentation>An NDK package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"  type="xsd:positiveInteger" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"      type="sdk:archivesType" />
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"      type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+    <!-- The definition of an SDK platform package. -->
+
+    <xsd:complexType name="platformType">
+        <xsd:annotation>
+            <xsd:documentation>An SDK platform package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android platform version. It is string such as "1.0". -->
+            <xsd:element name="version"   type="xsd:normalizedString" />
+            <!-- The Android API Level for the platform. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this platform, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"  type="xsd:positiveInteger" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- Information on the layoutlib packaged in this platform. -->
+            <xsd:element name="layoutlib" type="sdk:layoutlibType" />
+
+            <!-- optional elements -->
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"      type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"   type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"      type="sdk:archivesType" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be a revision element. -->
+            <xsd:element name="min-tools-rev" type="sdk:revisionType" minOccurs="0" />
+
+            <!-- The ABI of the system image *included* in this platform, if any.
+                 When the field is present, it means the platform already embeds one
+                 system image. A platform can also have any number of external
+                 &lt;system-image&gt; associated with it.  -->
+            <xsd:element name="included-abi"  type="sdk:abiType" minOccurs="0" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"      type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a layout library used by a platform. -->
+
+    <xsd:complexType name="layoutlibType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Version information for a layoutlib included in a platform.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The layoutlib API level, an int > 0,
+                 incremented with each new incompatible lib. -->
+            <xsd:element name="api"          type="xsd:positiveInteger" />
+            <!-- The incremental minor revision for that API, e.g. in case of bug fixes.
+                 Optional. An int >= 0, assumed to be 0 if the element is missing. -->
+            <xsd:element name="revision"     type="xsd:nonNegativeInteger" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of the ABI supported by a platform's system image. -->
+
+    <xsd:simpleType name="abiType">
+        <xsd:annotation>
+            <xsd:documentation>The ABI of a platform's system image.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:enumeration value="armeabi"     />
+            <xsd:enumeration value="armeabi-v7a" />
+            <xsd:enumeration value="x86"         />
+            <xsd:enumeration value="mips"        />
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a source package. -->
+
+    <xsd:complexType name="sourceType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Sources for a platform.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- api-level + codename identifies the platform to which this source belongs. -->
+
+            <!-- The Android API Level for the platform. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this platform, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"  type="xsd:positiveInteger" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"      type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"   type="xsd:token"       minOccurs="0" />
+
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"  type="sdk:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK tool package. -->
+
+    <xsd:complexType name="toolType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK tool package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The full revision (major.minor.micro.preview), incremented each
+                 time a new package is generated. -->
+            <xsd:element name="revision"      type="sdk:revisionType" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+            <!-- The minimal revision of platform-tools required by this package.
+                 Mandatory. Must be a revision element. -->
+            <xsd:element name="min-platform-tools-rev" type="sdk:revisionType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK platform-tool package. -->
+
+    <xsd:complexType name="platformToolType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK platform-tool package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The full revision (major.minor.micro.preview), incremented each
+                 time a new package is generated. -->
+            <xsd:element name="revision"      type="sdk:revisionType" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+    <!-- The definition of an SDK build-tool package. -->
+
+    <xsd:complexType name="buildToolType">
+        <xsd:annotation>
+            <xsd:documentation>An SDK build-tool package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The full revision (major.minor.micro.preview), incremented each
+                 time a new package is generated. -->
+            <xsd:element name="revision"      type="sdk:revisionType" />
+
+            <!-- optional elements -->
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license"  type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"   type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"      type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"   type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"      type="sdk:archivesType" />
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"      type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK doc package. -->
+
+    <xsd:complexType name="docType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK doc package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android API Level for the documentation. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this doc, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of an SDK sample package. -->
+
+    <xsd:complexType name="sampleType" >
+        <xsd:annotation>
+            <xsd:documentation>An SDK sample package.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The Android API Level for the documentation. An int > 0. -->
+            <xsd:element name="api-level" type="xsd:positiveInteger"  />
+            <!-- The optional codename for this doc, if it's a preview. -->
+            <xsd:element name="codename"  type="xsd:string" minOccurs="0" />
+
+            <!-- The optional display list item. When missing, it is auto-computed. -->
+            <xsd:element name="list-display"   type="xsd:normalizedString" minOccurs="0" />
+
+            <!-- The revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="revision"     type="xsd:positiveInteger" />
+            <!-- The optional license of this package. If present, users will have
+                 to agree to it before downloading. -->
+            <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+            <!-- The optional description of this package. -->
+            <xsd:element name="description"  type="xsd:string"      minOccurs="0" />
+            <!-- The optional description URL of this package -->
+            <xsd:element name="desc-url"     type="xsd:token"       minOccurs="0" />
+            <!-- The optional release note for this package. -->
+            <xsd:element name="release-note" type="xsd:string"      minOccurs="0" />
+            <!-- The optional release note URL of this package -->
+            <xsd:element name="release-url"  type="xsd:token"       minOccurs="0" />
+            <!-- A list of file archives for this package. -->
+            <xsd:element name="archives"     type="sdk:archivesType" />
+            <!-- The minimal revision of tools required by this package.
+                 Optional. If present, must be a revision element. -->
+            <xsd:element name="min-tools-rev" type="sdk:revisionType" minOccurs="0" />
+
+
+            <!-- An optional element indicating the package is obsolete.
+                 The string content is however currently not defined and ignored. -->
+            <xsd:element name="obsolete"  type="xsd:string" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- The definition of a path segment used by the extra element. -->
+
+    <xsd:simpleType name="segmentType">
+        <xsd:annotation>
+            <xsd:documentation>
+                One path segment for the install path of an extra element.
+                It must be a single-segment path.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="segmentListType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A semi-colon separated list of a segmentTypes.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:pattern value="[a-zA-Z0-9_;]+"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a license to be referenced by the uses-license element. -->
+
+    <xsd:complexType name="licenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A license definition. Such a license must be used later as a reference
+                using a uses-license element in one of the package elements.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="xsd:string">
+                <xsd:attribute name="id"   type="xsd:ID" />
+                <xsd:attribute name="type" type="xsd:token" fixed="text" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+
+    <!-- Type describing the license used by a package.
+         The license MUST be defined using a license node and referenced
+         using the ref attribute of the license element inside a package.
+     -->
+
+    <xsd:complexType name="usesLicenseType">
+        <xsd:annotation>
+            <xsd:documentation>
+                Describes the license used by a package. The license MUST be defined
+                using a license node and referenced using the ref attribute of the
+                license element inside a package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:attribute name="ref" type="xsd:IDREF" />
+    </xsd:complexType>
+
+
+    <!-- A collection of files that can be downloaded for a given architecture.
+         The <archives> node is mandatory in the repository elements and the
+         collection must have at least one <archive> declared.
+         Each archive is a zip file that will be unzipped in a location that depends
+         on its package type.
+     -->
+
+    <xsd:complexType name="archivesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of files that can be downloaded for a given architecture.
+                The &lt;archives&gt; node is mandatory in the repository packages and the
+                collection must have at least one &lt;archive&gt; declared.
+                Each archive is a zip file that will be unzipped in a location that depends
+                on its package type.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One archive file -->
+            <xsd:element name="archive">
+                <xsd:complexType>
+                    <!-- Properties of the archive file -->
+                    <xsd:all>
+                        <!-- The size in bytes of the archive to download. -->
+                        <xsd:element name="size"     type="xsd:positiveInteger" />
+                        <!-- The checksum of the archive file. -->
+                        <xsd:element name="checksum" type="sdk:checksumType" />
+                        <!-- The URL is an absolute URL if it starts with http://, https://
+                             or ftp://. Otherwise it is relative to the parent directory that
+                             contains this repository.xml -->
+                        <xsd:element name="url"      type="xsd:token" />
+
+                        <xsd:element name="host-os"         type="sdk:osType"         minOccurs="0" />
+                        <xsd:element name="host-bits"       type="sdk:bitSizeType"    minOccurs="0" />
+                        <xsd:element name="jvm-bits"        type="sdk:bitSizeType"    minOccurs="0" />
+                        <xsd:element name="min-jvm-version" type="sdk:jvmVersionType" minOccurs="0" />
+                    </xsd:all>
+                </xsd:complexType>
+            </xsd:element>
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- A full revision, with a major.minor.micro and an optional preview number.
+         The major number is mandatory, the other elements are optional.
+     -->
+
+    <xsd:complexType name="revisionType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A full revision, with a major.minor.micro and an
+                optional preview number. The major number is mandatory.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:all>
+            <!-- The major revision, an int > 0, incremented each time a new
+                 package is generated. -->
+            <xsd:element name="major"      type="xsd:positiveInteger" />
+            <!-- The minor revision, an int >= 0, incremented each time a new
+                 minor package is generated. Assumed to be 0 if missing. -->
+            <xsd:element name="minor"     type="xsd:nonNegativeInteger" minOccurs="0" />
+            <!-- The micro revision, an int >= 0, incremented each time a new
+                 buf fix is generated. Assumed to be 0 if missing. -->
+            <xsd:element name="micro"     type="xsd:nonNegativeInteger" minOccurs="0" />
+            <!-- The preview/release candidate revision, an int > 0,
+                 incremented each time a new preview is generated.
+                 Not present for final releases. -->
+            <xsd:element name="preview"   type="xsd:positiveInteger" minOccurs="0" />
+        </xsd:all>
+    </xsd:complexType>
+
+
+    <!-- A collection of file paths available in an &lt;extra&gt; package
+         that can be installed in an Android project.
+         If present, the &lt;project-files&gt; collection must contain at least one path.
+         Each path is relative to the root directory of the package.
+     -->
+
+    <xsd:complexType name="projectFilesType">
+        <xsd:annotation>
+            <xsd:documentation>
+                A collection of file paths available in an &lt;extra&gt; package
+                that can be installed in an Android project.
+                If present, the &lt;project-files&gt; collection must contain at least one path.
+                Each path is relative to the root directory of the package.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+            <!-- One JAR Path, relative to the root folder of the package. -->
+            <xsd:element name="path" type="xsd:string" />
+        </xsd:sequence>
+    </xsd:complexType>
+
+
+    <!-- The definition of archive filters -->
+
+    <xsd:simpleType name="bitSizeType">
+        <xsd:annotation>
+            <xsd:documentation>A CPU bit size filter.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:enumeration value="32" />
+            <xsd:enumeration value="64" />
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="osType">
+        <xsd:annotation>
+            <xsd:documentation>A host OS filter.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:token">
+            <xsd:enumeration value="linux" />
+            <xsd:enumeration value="macosx" />
+            <xsd:enumeration value="windows" />
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="jvmVersionType">
+        <xsd:annotation>
+            <xsd:documentation>A JVM version number, e.g. "1" or "1.6" or "1.14.15".</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="([1-9](\.[1-9]{1,2}){0,2})"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+
+    <!-- The definition of a file checksum -->
+
+    <xsd:simpleType name="sha1Number">
+        <xsd:annotation>
+            <xsd:documentation>A SHA1 checksum.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="([0-9a-fA-F]){40}"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:complexType name="checksumType">
+        <xsd:annotation>
+            <xsd:documentation>A file checksum, currently only SHA1.</xsd:documentation>
+        </xsd:annotation>
+        <xsd:simpleContent>
+            <xsd:extension base="sdk:sha1Number">
+                <xsd:attribute name="type" type="xsd:token" fixed="sha1" />
+            </xsd:extension>
+        </xsd:simpleContent>
+    </xsd:complexType>
+
+</xsd:schema>
diff --git a/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java b/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java
index 7aaf224..28918da 100644
--- a/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java
+++ b/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java
@@ -510,7 +510,7 @@
      */
     public void printHelpAndExitForAction(String verb, String directObject,
             String errorFormat, Object... args) {
-        if (errorFormat != null && errorFormat.length() > 0) {
+        if (errorFormat != null && !errorFormat.isEmpty()) {
             stderr(errorFormat, args);
         }
 
@@ -587,7 +587,7 @@
                 } else {
                     if (arg.getDefaultValue() instanceof String[]) {
                         for (String v : (String[]) arg.getDefaultValue()) {
-                            if (value.length() > 0) {
+                            if (!value.isEmpty()) {
                                 value += ", ";
                             }
                             value += v;
@@ -598,7 +598,7 @@
                             value = v.toString();
                         }
                     }
-                    if (value.length() > 0) {
+                    if (!value.isEmpty()) {
                         value = " [Default: " + value + "]";
                     }
                 }
@@ -611,10 +611,10 @@
                 // where either the 1-letter arg or the long arg are optional.
                 String output = String.format(
                         "  %1$-2s %2$-" + longArgWidth + "s: %3$s%4$s%5$s", //$NON-NLS-1$ //$NON-NLS-2$
-                        arg.getShortArg().length() > 0 ?
+                        !arg.getShortArg().isEmpty() ?
                                 "-" + arg.getShortArg() :                              //$NON-NLS-1$
                                 "",                                                    //$NON-NLS-1$
-                        arg.getLongArg().length() > 0 ?
+                        !arg.getLongArg().isEmpty() ?
                                 "--" + arg.getLongArg() :                              //$NON-NLS-1$
                                 "",                                                    //$NON-NLS-1$
                         arg.getDescription(),
@@ -923,7 +923,7 @@
         // We should always have at least a short or long name, ideally both but never none.
         assert shortName != null;
         assert longName != null;
-        assert shortName.length() > 0 || longName.length()  > 0;
+        assert !shortName.isEmpty() || !longName.isEmpty();
 
         if (directObject == null) {
             directObject = NO_VERB_OBJECT;
diff --git a/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java b/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java
index 575f366..db90c56 100755
--- a/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java
@@ -169,12 +169,12 @@
                 LocationType.IN_IMAGES_SUBFOLDER,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI_V7A,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
         makeSystemImageFolder(new SystemImage(sdkman, t,
                 LocationType.IN_IMAGES_SUBFOLDER,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_INTEL_ATOM,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         sdkman.reloadSdk(getLog());
         assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -194,12 +194,12 @@
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
         makeSystemImageFolder(new SystemImage(sdkman, t,
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI_V7A,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         sdkman.reloadSdk(getLog());
         assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -217,7 +217,7 @@
                 LocationType.IN_SYSTEM_IMAGE,
                 new IdDisplay("tag-1", "My Tag 1"),
                 SdkConstants.ABI_ARMEABI_V7A,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         sdkman.reloadSdk(getLog());
         assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -251,7 +251,7 @@
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_INTEL_ATOM,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         sdkman.reloadSdk(getLog());
         assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -269,7 +269,7 @@
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
 
         sdkman.reloadSdk(getLog());
diff --git a/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java b/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java
index ae7f8cf..e3c945f 100755
--- a/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java
+++ b/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java
@@ -63,38 +63,48 @@
 import java.util.List;
 
 /**
- * Base Test case that allocates a temporary SDK, a temporary AVD base
- * folder with an SdkManager and an AvdManager that points to them.
- * <p/>
- * Also overrides the {@link AndroidLocation} to point to temp one.
+ * Base Test case that allocates a temporary SDK, a temporary AVD base folder with an SdkManager and
+ * an AvdManager that points to them. <p/> Also overrides the {@link AndroidLocation} to point to
+ * temp one.
  */
 public abstract class SdkManagerTestCase extends AndroidLocationTestCase {
 
     protected static final String TARGET_DIR_NAME_0 = "v0_0";
+
     private File mFakeSdk;
+
     private MockLog mLog;
+
     private SdkManager mSdkManager;
+
     private AvdManager mAvdManager;
+
     private int mRepoXsdLevel;
 
-    /** Returns the {@link MockLog} for this test case. */
+    /**
+     * Returns the {@link MockLog} for this test case.
+     */
     public MockLog getLog() {
         return mLog;
     }
 
-    /** Returns the {@link SdkManager} for this test case. */
+    /**
+     * Returns the {@link SdkManager} for this test case.
+     */
     public SdkManager getSdkManager() {
         return mSdkManager;
     }
 
-    /** Returns the {@link AvdManager} for this test case. */
+    /**
+     * Returns the {@link AvdManager} for this test case.
+     */
     public AvdManager getAvdManager() {
         return mAvdManager;
     }
 
     /**
-     * Sets up a {@link MockLog}, a fake SDK in a temporary directory
-     * and an AVD Manager pointing to an initially-empty AVD directory.
+     * Sets up a {@link MockLog}, a fake SDK in a temporary directory and an AVD Manager pointing to
+     * an initially-empty AVD directory.
      */
     public void setUp(int repoXsdLevel) throws Exception {
         super.setUp();
@@ -105,9 +115,9 @@
     }
 
     /**
-     * Recreate the SDK and AVD Managers from scratch even if they already existed.
-     * Useful for tests that want to reset their state without recreating the
-     * android-home or the fake SDK. The SDK will be reparsed.
+     * Recreate the SDK and AVD Managers from scratch even if they already existed. Useful for tests
+     * that want to reset their state without recreating the android-home or the fake SDK. The SDK
+     * will be reparsed.
      */
     protected void createSdkAvdManagers() throws AndroidLocationException {
         mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
@@ -131,13 +141,13 @@
                     return false;
                 }
 
-            };
+            }
         };
     }
 
     /**
-     * Sets up a {@link MockLog}, a fake SDK in a temporary directory
-     * and an AVD Manager pointing to an initially-empty AVD directory.
+     * Sets up a {@link MockLog}, a fake SDK in a temporary directory and an AVD Manager pointing to
+     * an initially-empty AVD directory.
      */
     @Override
     public void setUp() throws Exception {
@@ -154,10 +164,9 @@
     }
 
     /**
-     * Build enough of a skeleton SDK to make the tests pass.
-     * <p/>
-     * Ideally this wouldn't touch the file system but the current
-     * structure of the SdkManager and AvdManager makes this impossible.
+     * Build enough of a skeleton SDK to make the tests pass. <p/> Ideally this wouldn't touch the
+     * file system but the current structure of the SdkManager and AvdManager makes this
+     * impossible.
      */
     private void makeFakeSdk() throws IOException {
         // First we create a temp file to "reserve" the temp directory name we want to use.
@@ -204,42 +213,47 @@
      * Creates the system image folder and places a fake userdata.img in it.
      *
      * @param systemImage A system image with a valid location.
-     * @throws IOException if the file fails to be created.
      */
-    protected void makeSystemImageFolder(ISystemImage systemImage) throws Exception {
+    protected void makeSystemImageFolder(ISystemImage systemImage, String deviceId)
+            throws Exception {
         File sysImgDir = systemImage.getLocation();
-
+        String vendor = systemImage.getAddonVendor() == null ? null
+                : systemImage.getAddonVendor().getId();
         if (systemImage.getLocationType() == LocationType.IN_LEGACY_FOLDER) {
             // legacy mode. Path should look like SDK/platforms/platform-N/userdata.img
             makeFakeLegacySysImg(sysImgDir.getParentFile(), systemImage.getAbiType());
 
-        } else  if (systemImage.getLocationType() == LocationType.IN_IMAGES_SUBFOLDER) {
+        } else if (systemImage.getLocationType() == LocationType.IN_IMAGES_SUBFOLDER) {
             // not-so-legacy mode.
             // Path should look like SDK/platforms/platform-N/images/userdata.img
             makeFakeSysImgInternal(
                     sysImgDir,
                     systemImage.getTag().getId(),
-                    systemImage.getAbiType());
+                    systemImage.getAbiType(),
+                    deviceId,
+                    vendor);
 
-        } else  if (systemImage.getLocationType() == LocationType.IN_SYSTEM_IMAGE) {
+        } else if (systemImage.getLocationType() == LocationType.IN_SYSTEM_IMAGE) {
             // system-image folder mode.
             // Path should like SDK/system-images/platform-N/tag/abi/userdata.img+source.properties
             makeFakeSysImgInternal(
                     sysImgDir,
                     systemImage.getTag().getId(),
-                    systemImage.getAbiType());
+                    systemImage.getAbiType(),
+                    deviceId,
+                    vendor);
         }
     }
 
     /**
-     * Creates the system image folder and places a fake userdata.img in it.
-     * This must be called after {@link #setUp()} so that it can use the temp fake SDK folder,
-     * and consequently you do not need to specify the SDK root.
+     * Creates the system image folder and places a fake userdata.img in it. This must be called
+     * after {@link #setUp()} so that it can use the temp fake SDK folder, and consequently you do
+     * not need to specify the SDK root.
      *
-     * @param targetDir The targetDir segment of the sys-image folder.
-     *          Use {@link #TARGET_DIR_NAME_0} to match the default single platform.
-     * @param tagId An optional tag id. Use null for legacy no-tag system images.
-     * @param abiType The abi for the system image.
+     * @param targetDir The targetDir segment of the sys-image folder. Use {@link
+     *                  #TARGET_DIR_NAME_0} to match the default single platform.
+     * @param tagId     An optional tag id. Use null for legacy no-tag system images.
+     * @param abiType   The abi for the system image.
      * @return The directory of the system-image/tag/abi created.
      * @throws IOException if the file fails to be created.
      */
@@ -255,13 +269,13 @@
         }
         sysImgDir = new File(sysImgDir, abiType);
 
-        makeFakeSysImgInternal(sysImgDir, tagId, abiType);
+        makeFakeSysImgInternal(sysImgDir, tagId, abiType, null, null);
         return sysImgDir;
     }
 
     //----
 
-    private void createTextFile(File dir, String filepath, String...lines) throws IOException {
+    private void createTextFile(File dir, String filepath, String... lines) throws IOException {
         File file = new File(dir, filepath);
 
         File parent = file.getParentFile();
@@ -281,7 +295,9 @@
         }
     }
 
-    /** Utility used by {@link #makeFakeSdk()} to create a fake target with API 0, rev 0. */
+    /**
+     * Utility used by {@link #makeFakeSdk()} to create a fake target with API 0, rev 0.
+     */
     private File makeFakeTargetInternal(File platformsDir) throws IOException {
         File targetDir = new File(platformsDir, TARGET_DIR_NAME_0);
         targetDir.mkdirs();
@@ -296,17 +312,16 @@
                 PkgProps.LAYOUTLIB_REV, "2");
 
         createFileProps(SdkConstants.FN_BUILD_PROP, targetDir,
-                LocalPlatformPkgInfo.PROP_VERSION_RELEASE,  "0.0",
-                LocalPlatformPkgInfo.PROP_VERSION_SDK,      "0",
+                LocalPlatformPkgInfo.PROP_VERSION_RELEASE, "0.0",
+                LocalPlatformPkgInfo.PROP_VERSION_SDK, "0",
                 LocalPlatformPkgInfo.PROP_VERSION_CODENAME, "REL");
 
         return targetDir;
     }
 
     /**
-     * Utility to create a fake *legacy* sys image in a platform folder.
-     * Legacy system images follow that path pattern:
-     *   $SDK/platforms/platform-N/images/userdata.img
+     * Utility to create a fake *legacy* sys image in a platform folder. Legacy system images follow
+     * that path pattern: $SDK/platforms/platform-N/images/userdata.img
      *
      * They have no source.properties file in that directory.
      */
@@ -322,20 +337,18 @@
      * Utility to create a fake sys image in the system-images folder.
      *
      * "modern" (as in "not legacy") system-images follow that path pattern:
-     *   $SDK/system-images/platform-N/abi/source.properties
-     *   $SDK/system-images/platform-N/abi/userdata.img
-     * or
-     *   $SDK/system-images/platform-N/tag/abi/source.properties
-     *   $SDK/system-images/platform-N/tag/abi/userdata.img
+     * $SDK/system-images/platform-N/abi/source.properties $SDK/system-images/platform-N/abi/userdata.img
+     * or $SDK/system-images/platform-N/tag/abi/source.properties $SDK/system-images/platform-N/tag/abi/userdata.img
      *
-     * The tag id is optional and was only introduced in API 20 / Tools 22.6.
-     * The platform-N and the tag folder names are irrelevant as the info from
-     * source.properties matters most.
+     * The tag id is optional and was only introduced in API 20 / Tools 22.6. The platform-N and the
+     * tag folder names are irrelevant as the info from source.properties matters most.
      */
     private void makeFakeSysImgInternal(
             @NonNull File sysImgDir,
             @Nullable String tagId,
-            @NonNull String abiType) throws Exception {
+            @NonNull String abiType,
+            @Nullable String deviceId,
+            @Nullable String deviceMfg) throws Exception {
         sysImgDir.mkdirs();
         new File(sysImgDir, "userdata.img").createNewFile();
 
@@ -344,7 +357,7 @@
                     PkgProps.PKG_REVISION, "0",
                     PkgProps.VERSION_API_LEVEL, "0",
                     PkgProps.SYS_IMG_ABI, abiType);
-       } else {
+        } else {
             String tagDisplay = LocalSysImgPkgInfo.tagIdToDisplay(tagId);
             createSourceProps(sysImgDir,
                     PkgProps.PKG_REVISION, "0",
@@ -352,14 +365,15 @@
                     PkgProps.SYS_IMG_TAG_ID, tagId,
                     PkgProps.SYS_IMG_TAG_DISPLAY, tagDisplay,
                     PkgProps.SYS_IMG_ABI, abiType,
-                    PkgProps.PKG_LIST_DISPLAY, "Sys-Img v0 for (" + tagDisplay + ", " + abiType + ")");
+                    PkgProps.PKG_LIST_DISPLAY,
+                    "Sys-Img v0 for (" + tagDisplay + ", " + abiType + ")");
 
             // create a devices.xml file
             List<Device> devices = new ArrayList<Device>();
             Builder b = new Device.Builder();
             b.setName("Mock " + tagDisplay + " Device Name");
-            b.setId("MockDevice-" + tagId);
-            b.setManufacturer("Mock " + tagDisplay + " OEM");
+            b.setId(deviceId == null ? "MockDevice-" + tagId : deviceId);
+            b.setManufacturer(deviceMfg == null ? "Mock " + tagDisplay + " OEM" : deviceMfg);
 
             Software sw = new Software();
             sw.setGlVersion("4.2");
@@ -410,10 +424,12 @@
             FileOutputStream fos = new FileOutputStream(f);
             DeviceWriter.writeToXml(fos, devices);
             fos.close();
-         }
+        }
     }
 
-    /** Utility to make a fake skin for the given target */
+    /**
+     * Utility to make a fake skin for the given target
+     */
     protected void makeFakeSkin(File targetDir, String skinName) throws IOException {
         File skinFolder = FileOp.append(targetDir, "skins", skinName);
         skinFolder.mkdirs();
@@ -425,7 +441,9 @@
         out.close();
     }
 
-    /** Utility to create a fake source with a few files in the given sdk folder. */
+    /**
+     * Utility to create a fake source with a few files in the given sdk folder.
+     */
     private void makeFakeSourceInternal(File sdkDir) throws IOException {
         File sourcesDir = FileOp.append(sdkDir, SdkConstants.FD_PKG_SOURCES, "android-0");
         sourcesDir.mkdirs();
@@ -450,7 +468,7 @@
     }
 
     private void makeBuildTools(File sdkDir) throws IOException {
-        for (String revision : new String[] { "3.0.0", "3.0.1", "18.3.4 rc5" }) {
+        for (String revision : new String[]{"3.0.0", "3.0.1", "18.3.4 rc5"}) {
             createFakeBuildTools(sdkDir, "ANY", revision);
         }
     }
@@ -458,10 +476,9 @@
     /**
      * Adds a new fake build tools to the SDK In the given SDK/build-tools folder.
      *
-     * @param sdkDir The SDK top folder. Must already exist.
-     * @param os The OS. One of HostOs#toString() or "ANY".
+     * @param sdkDir   The SDK top folder. Must already exist.
+     * @param os       The OS. One of HostOs#toString() or "ANY".
      * @param revision The "x.y.z rc r" revision number from {@link FullRevision#toShortString()}.
-     * @throws IOException
      */
     protected void createFakeBuildTools(File sdkDir, String os, String revision)
             throws IOException {
@@ -476,46 +493,47 @@
 
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.AAPT,             SdkConstants.FN_AAPT);
+                BuildToolInfo.PathId.AAPT, SdkConstants.FN_AAPT);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.AIDL,             SdkConstants.FN_AIDL);
+                BuildToolInfo.PathId.AIDL, SdkConstants.FN_AIDL);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.DX,               SdkConstants.FN_DX);
+                BuildToolInfo.PathId.DX, SdkConstants.FN_DX);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.DX_JAR,           SdkConstants.FD_LIB + File.separator +
-                SdkConstants.FN_DX_JAR);
+                BuildToolInfo.PathId.DX_JAR, SdkConstants.FD_LIB + File.separator +
+                        SdkConstants.FN_DX_JAR);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.LLVM_RS_CC,       SdkConstants.FN_RENDERSCRIPT);
+                BuildToolInfo.PathId.LLVM_RS_CC, SdkConstants.FN_RENDERSCRIPT);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.ANDROID_RS,       SdkConstants.OS_FRAMEWORK_RS + File.separator +
-                     "placeholder.txt");
+                BuildToolInfo.PathId.ANDROID_RS, SdkConstants.OS_FRAMEWORK_RS + File.separator +
+                        "placeholder.txt");
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.ANDROID_RS_CLANG, SdkConstants.OS_FRAMEWORK_RS_CLANG + File.separator +
-                    "placeholder.txt");
+                BuildToolInfo.PathId.ANDROID_RS_CLANG,
+                SdkConstants.OS_FRAMEWORK_RS_CLANG + File.separator +
+                        "placeholder.txt");
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.BCC_COMPAT,       SdkConstants.FN_BCC_COMPAT);
+                BuildToolInfo.PathId.BCC_COMPAT, SdkConstants.FN_BCC_COMPAT);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.LD_ARM,       SdkConstants.FN_LD_ARM);
+                BuildToolInfo.PathId.LD_ARM, SdkConstants.FN_LD_ARM);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.LD_MIPS,       SdkConstants.FN_LD_MIPS);
+                BuildToolInfo.PathId.LD_MIPS, SdkConstants.FN_LD_MIPS);
         createFakeBuildToolsFile(
                 buildToolsDir, fullRevision,
-                BuildToolInfo.PathId.LD_X86,       SdkConstants.FN_LD_X86);
+                BuildToolInfo.PathId.LD_X86, SdkConstants.FN_LD_X86);
     }
 
     private void createFakeBuildToolsFile(@NonNull File dir,
-                                          @NonNull FullRevision buildToolsRevision,
-                                          @NonNull BuildToolInfo.PathId pathId,
-                                          @NonNull String filepath)
+            @NonNull FullRevision buildToolsRevision,
+            @NonNull BuildToolInfo.PathId pathId,
+            @NonNull String filepath)
             throws IOException {
 
         if (pathId.isPresentIn(buildToolsRevision)) {
@@ -524,11 +542,12 @@
     }
 
 
-    protected void createSourceProps(File parentDir, String...paramValuePairs) throws IOException {
+    protected void createSourceProps(File parentDir, String... paramValuePairs) throws IOException {
         createFileProps(SdkConstants.FN_SOURCE_PROP, parentDir, paramValuePairs);
     }
 
-    protected void createFileProps(String fileName, File parentDir, String...paramValuePairs) throws IOException {
+    protected void createFileProps(String fileName, File parentDir, String... paramValuePairs)
+            throws IOException {
         File sourceProp = new File(parentDir, fileName);
         parentDir = sourceProp.getParentFile();
         if (!parentDir.isDirectory()) {
@@ -539,9 +558,9 @@
         }
         FileWriter out = new FileWriter(sourceProp);
         int n = paramValuePairs.length;
-        assertTrue("paramValuePairs must have an even length, format [param=value]+", n %2 == 0);
+        assertTrue("paramValuePairs must have an even length, format [param=value]+", n % 2 == 0);
         for (int i = 0; i < n; i += 2) {
-            out.write(paramValuePairs[i] + '=' + paramValuePairs[i+1] + '\n');
+            out.write(paramValuePairs[i] + '=' + paramValuePairs[i + 1] + '\n');
         }
         out.close();
 
diff --git a/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java b/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java
index 36595f5..5d919c6 100755
--- a/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java
@@ -24,8 +24,6 @@
 import static com.android.sdklib.SdkVersionInfo.getVersion;
 import static com.android.sdklib.SdkVersionInfo.underlinesToCamelCase;
 
-import com.android.sdklib.SdkVersionInfo;
-
 import junit.framework.TestCase;
 
 public class SdkVersionInfoTest extends TestCase {
diff --git a/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java b/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java
index 22935f1..9bc9140 100755
--- a/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java
@@ -18,20 +18,22 @@
 
 import com.android.resources.Keyboard;
 import com.android.resources.Navigation;
+import com.android.sdklib.ISystemImage;
 import com.android.sdklib.SdkManagerTestCase;
+import com.android.sdklib.SystemImage;
 import com.android.sdklib.devices.Device.Builder;
 import com.android.sdklib.devices.DeviceManager.DeviceFilter;
 import com.android.sdklib.devices.DeviceManager.DeviceStatus;
 import com.android.sdklib.mock.MockLog;
+import com.android.sdklib.repository.descriptors.IdDisplay;
 
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
 
 public class DeviceManagerTest extends SdkManagerTestCase {
 
     private DeviceManager dm;
+
     private MockLog log;
 
     @Override
@@ -47,19 +49,28 @@
         return DeviceManager.createInstance(sdkLocation, log);
     }
 
-    /** Returns a list of just the devices' display names, for unit test comparisons. */
+    /**
+     * Returns a list of just the devices' display names, for unit test comparisons.
+     */
     private static String listDisplayName(Device device) {
-        if (device == null) return null;
+        if (device == null) {
+            return null;
+        }
         return device.getDisplayName();
     }
 
-    /** Returns a list of just the devices' display names, for unit test comparisons. */
+    /**
+     * Returns a list of just the devices' display names, for unit test comparisons.
+     */
     private static List<String> listDisplayNames(Collection<Device> devices) {
-        if (devices == null) return null;
+        if (devices == null) {
+            return null;
+        }
         List<String> names = new ArrayList<String>();
         for (Device d : devices) {
             names.add(listDisplayName(d));
         }
+        Collections.sort(names);
         return names;
     }
 
@@ -80,11 +91,12 @@
         // this list comes from devices.xml bundled in the JAR
         // cf /sdklib/src/main/java/com/android/sdklib/devices/devices.xml
         assertEquals(
-                "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
-                 "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
-                 "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
-                 "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet)]",
-                 listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
+                "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, " +
+                        "3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, " +
+                        "3.7\" FWVGA slider, 3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), " +
+                        "4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, " +
+                        "7\" WSVGA (Tablet)]",
+                listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
         assertEquals("", log.toString());
 
         assertEquals("2.7\" QVGA",
@@ -92,25 +104,26 @@
 
         // this list comes from the nexus.xml bundled in the JAR
         // cf /sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
-        assertEquals(
-                "[Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
-                 "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, " +
-                 "Android Wear Round, Android TV (1080p), Android TV (720p)]",
-                 listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
+        assertEquals("[Android TV (1080p), Android TV (720p), Android Wear Round, " +
+                        "Android Wear Round Chin, Android Wear Square, " +
+                        "Galaxy Nexus, Nexus 10, Nexus 4, Nexus 5, Nexus 6, Nexus 7, " +
+                        "Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+                listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
         assertEquals("", log.toString());
 
         assertEquals("Nexus One",
                 listDisplayName(dm.getDevice("Nexus One", "Google")));
 
         assertEquals(
-                "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
-                 "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
-                 "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
-                 "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet), " +
-                 "Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
-                 "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, Android Wear Round, " +
-                 "Android TV (1080p), Android TV (720p)]",
-                 listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
+                "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+                        "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+                        "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+                        "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet), Android TV (1080p), "
+                        +
+                        "Android TV (720p), Android Wear Round, Android Wear Round Chin, " +
+                        "Android Wear Square, Galaxy Nexus, Nexus 10, Nexus 4, Nexus 5, " +
+                        "Nexus 6, Nexus 7, Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+                listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
         assertEquals("", log.toString());
     }
 
@@ -152,43 +165,43 @@
 
         // 1 user device defined in the test's custom .android home folder
         assertEquals("[My Custom Tablet]",
-                     listDisplayNames(dm2.getDevices(DeviceFilter.USER)).toString());
+                listDisplayNames(dm2.getDevices(DeviceFilter.USER)).toString());
         assertEquals("", log.toString());
 
         // no system-images devices defined in the SDK by default
         assertEquals("[]",
-                     listDisplayNames(dm2.getDevices(DeviceFilter.SYSTEM_IMAGES)).toString());
+                listDisplayNames(dm2.getDevices(DeviceFilter.SYSTEM_IMAGES)).toString());
         assertEquals("", log.toString());
 
         // this list comes from devices.xml bundled in the JAR
         // cf /sdklib/src/main/java/com/android/sdklib/devices/devices.xml
         assertEquals(
-                "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
-                 "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
-                 "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
-                 "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet)]",
-                 listDisplayNames(dm2.getDevices(DeviceFilter.DEFAULT)).toString());
+                "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+                        "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+                        "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+                        "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet)]",
+                listDisplayNames(dm2.getDevices(DeviceFilter.DEFAULT)).toString());
         assertEquals("", log.toString());
 
         // this list comes from the nexus.xml bundled in the JAR
         // cf /sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
-        assertEquals(
-                "[Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
-                 "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, " +
-                 "Android Wear Round, Android TV (1080p), Android TV (720p)]",
-                 listDisplayNames(dm2.getDevices(DeviceFilter.VENDOR)).toString());
+        assertEquals("[Android TV (1080p), Android TV (720p), Android Wear Round, " +
+                        "Android Wear Round Chin, Android Wear Square, Galaxy Nexus, " +
+                        "Nexus 10, Nexus 4, Nexus 5, Nexus 6, Nexus 7, Nexus 7 (2012), " +
+                        "Nexus 9, Nexus One, Nexus S]",
+                listDisplayNames(dm2.getDevices(DeviceFilter.VENDOR)).toString());
         assertEquals("", log.toString());
 
         assertEquals(
-                "[My Custom Tablet, " +
-                 "2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
-                 "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
-                 "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
-                 "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet), " +
-                 "Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
-                 "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, Android Wear Round, " +
-                 "Android TV (1080p), Android TV (720p)]",
-                 listDisplayNames(dm2.getDevices(DeviceManager.ALL_DEVICES)).toString());
+                "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+                        "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+                        "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+                        "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet), Android TV (1080p), "
+                        +
+                        "Android TV (720p), Android Wear Round, Android Wear Round Chin, " +
+                        "Android Wear Square, Galaxy Nexus, My Custom Tablet, Nexus 10, " +
+                        "Nexus 4, Nexus 5, Nexus 6, Nexus 7, Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+                listDisplayNames(dm2.getDevices(DeviceManager.ALL_DEVICES)).toString());
         assertEquals("", log.toString());
     }
 
@@ -212,47 +225,50 @@
         // this list comes from devices.xml bundled in the JAR
         // cf /sdklib/src/main/java/com/android/sdklib/devices/devices.xml
         assertEquals(
-                "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
-                 "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
-                 "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
-                 "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet)]",
-                 listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
+                "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+                        "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+                        "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+                        "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet)]",
+                listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
         assertEquals("", log.toString());
 
         // this list comes from the nexus.xml bundled in the JAR
         // cf /sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
         assertEquals(
-                "[Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
-                 "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, " +
-                 "Android Wear Round, Android TV (1080p), Android TV (720p)]",
-                 listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
+                "[Android TV (1080p), Android TV (720p), Android Wear Round, Android Wear Round Chin, "
+                        +
+                        "Android Wear Square, Galaxy Nexus, Nexus 10, Nexus 4, Nexus 5, Nexus 6, Nexus 7, "
+                        +
+                        "Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+                listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
         assertEquals("", log.toString());
 
         assertEquals(
-                "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
-                 "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
-                 "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
-                 "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet), " +
-                 "Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
-                 "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, Android Wear Round, " +
-                 "Android TV (1080p), Android TV (720p), " +
-                 "Mock Tag 1 Device Name]",
-                 listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
+                "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+                        "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+                        "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+                        "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet), Android TV (1080p), "
+                        +
+                        "Android TV (720p), Android Wear Round, Android Wear Round Chin, " +
+                        "Android Wear Square, Galaxy Nexus, Mock Tag 1 Device Name, Nexus 10, Nexus 4, "
+                        +
+                        "Nexus 5, Nexus 6, Nexus 7, Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+                listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
         assertEquals("", log.toString());
     }
 
     public final void testGetDeviceStatus() {
         // get a definition from the bundled devices.xml file
         assertEquals(DeviceStatus.EXISTS,
-                     dm.getDeviceStatus("7in WSVGA (Tablet)", "Generic"));
+                dm.getDeviceStatus("7in WSVGA (Tablet)", "Generic"));
 
         // get a definition from the bundled oem file
         assertEquals(DeviceStatus.EXISTS,
-                     dm.getDeviceStatus("Nexus One", "Google"));
+                dm.getDeviceStatus("Nexus One", "Google"));
 
         // try a device that does not exist
         assertEquals(DeviceStatus.MISSING,
-                     dm.getDeviceStatus("My Device", "Custom OEM"));
+                dm.getDeviceStatus("My Device", "Custom OEM"));
     }
 
     public final void testHasHardwarePropHashChanged_Generic() {
@@ -264,31 +280,25 @@
                         "invalid"));
 
         assertEquals(null,
-                DeviceManager.hasHardwarePropHashChanged(
-                        d1,
-                        "MD5:750a657019b49e621c42ce9a20c2cc30"));
+                DeviceManager.hasHardwarePropHashChanged(d1, "MD5:750a657019b49e621c42ce9a20c2cc30"));
 
         // change the device hardware props, this should change the hash
         d1.getDefaultHardware().setNav(Navigation.TRACKBALL);
 
         assertEquals("MD5:9c4dd5018987da51f7166f139f4361a2",
-                DeviceManager.hasHardwarePropHashChanged(
-                        d1,
-                        "MD5:750a657019b49e621c42ce9a20c2cc30"));
+                     DeviceManager.hasHardwarePropHashChanged(d1, "MD5:750a657019b49e621c42ce9a20c2cc30"));
 
         // change the property back, should revert its hash to the previous one
         d1.getDefaultHardware().setNav(Navigation.NONAV);
 
         assertEquals(null,
-                DeviceManager.hasHardwarePropHashChanged(
-                        d1,
-                        "MD5:750a657019b49e621c42ce9a20c2cc30"));
+                DeviceManager.hasHardwarePropHashChanged(d1, "MD5:750a657019b49e621c42ce9a20c2cc30"));
     }
 
     public final void testHasHardwarePropHashChanged_Oem() {
         final Device d2 = dm.getDevice("Nexus One", "Google");
 
-        assertEquals("MD5:d886364fc30320c0518f51002d0ef22d",
+        assertEquals("MD5:36362a51e6c830c2ab515a312c9ecbff",
                 DeviceManager.hasHardwarePropHashChanged(
                         d2,
                         "invalid"));
@@ -296,15 +306,15 @@
         assertEquals(null,
                 DeviceManager.hasHardwarePropHashChanged(
                         d2,
-                        "MD5:d886364fc30320c0518f51002d0ef22d"));
+                        "MD5:36362a51e6c830c2ab515a312c9ecbff"));
 
         // change the device hardware props, this should change the hash
         d2.getDefaultHardware().setKeyboard(Keyboard.QWERTY);
 
-        assertEquals("MD5:db682e0a58e74a8614e43e6e15c05176",
+        assertEquals("MD5:f8f4b390755f2f58dfeb7d3020cd87db",
                 DeviceManager.hasHardwarePropHashChanged(
                         d2,
-                        "MD5:d886364fc30320c0518f51002d0ef22d"));
+                        "MD5:36362a51e6c830c2ab515a312c9ecbff"));
 
         // change the property back, should revert its hash to the previous one
         d2.getDefaultHardware().setKeyboard(Keyboard.NOKEY);
@@ -312,6 +322,44 @@
         assertEquals(null,
                 DeviceManager.hasHardwarePropHashChanged(
                         d2,
-                        "MD5:d886364fc30320c0518f51002d0ef22d"));
+                        "MD5:36362a51e6c830c2ab515a312c9ecbff"));
+    }
+
+    public final void testDeviceOverrides() throws Exception {
+        try {
+            File location = getSdkManager().getLocalSdk().getLocation();
+            SystemImage imageWithDevice = new SystemImage(
+              new File(location, "system-images/android-22/android-wear/x86"),
+              ISystemImage.LocationType.IN_SYSTEM_IMAGE,
+              new IdDisplay("android-wear", "android-wear"), new IdDisplay("Google", "Google1"),
+              "x86", new File[]{});
+            DeviceManager manager = DeviceManager.createInstance(location, log);
+            int count = manager.getDevices(EnumSet.allOf(DeviceFilter.class)).size();
+            Device d = manager.getDevice("wear_round", "Google");
+            assertEquals(d.getDisplayName(), "Android Wear Round");
+
+            makeSystemImageFolder(imageWithDevice, "wear_round");
+            manager = DeviceManager.createInstance(location, log);
+
+            d = manager.getDevice("wear_round", "Google");
+            assertEquals(d.getDisplayName(), "Mock Android wear Device Name");
+
+            Device d1 = dm.getDevice("wear_round", "Google");
+
+            Builder b = new Device.Builder(d1);
+            b.setName("Custom");
+
+            Device d2 = b.build();
+
+            manager.addUserDevice(d2);
+            manager.saveUserDevices();
+
+            d = manager.getDevice("wear_round", "Google");
+            assertEquals(d.getDisplayName(), "Custom");
+            assertEquals(count, manager.getDevices(EnumSet.allOf(DeviceFilter.class)).size());
+        }
+        finally {
+            super.setUp();
+        }
     }
 }
diff --git a/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java b/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java
index 263a8b5..2493f46 100644
--- a/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java
@@ -24,10 +24,12 @@
 import com.android.resources.NavigationState;
 import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
 import com.android.resources.TouchScreen;
 import com.android.sdklib.devices.Storage.Unit;
 
+import com.google.common.collect.Table;
 import junit.framework.TestCase;
 
 import org.xml.sax.SAXParseException;
@@ -44,11 +46,11 @@
     public void testValidDevicesMinimal() throws Exception {
         InputStream stream = DeviceSchemaTest.class.getResourceAsStream("devices_minimal.xml");
         try {
-            List<Device> devices = DeviceParser.parse(stream);
+            Table<String, String, Device> devices = DeviceParser.parse(stream);
             assertEquals("Parsing devices_minimal.xml produces the wrong number of devices",
                     1, devices.size());
 
-            Device device = devices.get(0);
+            Device device = devices.get("Galaxy Nexus", "Samsung");
             assertEquals("Galaxy Nexus", device.getDisplayName());
             assertEquals("Samsung", device.getManufacturer());
 
@@ -154,23 +156,23 @@
     public void testValidDevicesFull_v1() throws Exception {
         InputStream stream = DeviceSchemaTest.class.getResourceAsStream("devices.xml");
         try {
-            List<Device> devices = DeviceParser.parse(stream);
+            Table<String, String, Device> devices = DeviceParser.parse(stream);
             assertEquals("Parsing devices.xml produces the wrong number of devices",
-                    3, devices.size());
+                    4, devices.size());
 
-            Device device0 = devices.get(0);
+            Device device0 = devices.get("galaxy_nexus", "Samsung");
             assertEquals(null, device0.getTagId());
             assertEquals("{}", device0.getBootProps().toString());
             assertEquals("OMAP 4460", device0.getDefaultHardware().getCpu());
             assertEquals("[armeabi, armeabi-v7a]", device0.getDefaultHardware().getSupportedAbis().toString());
 
-            Device device1 = devices.get(1);
+            Device device1 = devices.get("Droid", "Motorola");
             assertEquals(null, device1.getTagId());
             assertEquals("{}", device1.getBootProps().toString());
             assertEquals("OMAP 3430", device1.getDefaultHardware().getCpu());
             assertEquals("[armeabi, armeabi-v7a]", device1.getDefaultHardware().getSupportedAbis().toString());
 
-            Device device2 = devices.get(2);
+            Device device2 = devices.get("Nexus 5", "Google");
             assertEquals("tag-1", device2.getTagId());
             assertEquals("{ro-myservice-port=1234, " +
                           "ro.RAM.Size=1024 MiB, " +
@@ -178,6 +180,15 @@
                          device2.getBootProps().toString());
             assertEquals("Snapdragon 800 (MSM8974)", device2.getDefaultHardware().getCpu());
             assertEquals("[armeabi, armeabi-v7a]", device2.getDefaultHardware().getSupportedAbis().toString());
+            assertEquals(device2.getChinSize(), 0);
+            assertFalse(device2.isScreenRound());
+
+            Device device3 = devices.get("wear_round_chin", "Google");
+            assertEquals("android-wear", device3.getTagId());
+            assertEquals(device3.getDefaultHardware().getScreen().getChin(), 30);
+            assertEquals(device3.getChinSize(), 30);
+            assertTrue(device3.isScreenRound());
+            assertEquals(device3.getDefaultHardware().getScreen().getScreenRound(), ScreenRound.ROUND);
         } finally {
             stream.close();
         }
@@ -186,17 +197,17 @@
     public void testValidDevicesFull_v2() throws Exception {
         InputStream stream = DeviceSchemaTest.class.getResourceAsStream("devices_v2.xml");
         try {
-            List<Device> devices = DeviceParser.parse(stream);
+            Table<String, String, Device> devices = DeviceParser.parse(stream);
             assertEquals("Parsing devices.xml produces the wrong number of devices",
-                    3, devices.size());
+                    4, devices.size());
 
-            Device device0 = devices.get(0);
+            Device device0 = devices.get("galaxy_64", "Gnusmas");
             assertEquals(null, device0.getTagId());
             assertEquals("{}", device0.getBootProps().toString());
             assertEquals("arm64", device0.getDefaultHardware().getCpu());
             assertEquals("[arm64-v8a]", device0.getDefaultHardware().getSupportedAbis().toString());
 
-            Device device1 = devices.get(1);
+            Device device1 = devices.get("Droid X86", "Letni");
             assertEquals("tag-1", device1.getTagId());
             assertEquals("{ro-myservice-port=1234, " +
                           "ro.RAM.Size=1024 MiB, " +
@@ -205,7 +216,7 @@
             assertEquals("Intel Atom 64", device1.getDefaultHardware().getCpu());
             assertEquals("[x86_64]", device1.getDefaultHardware().getSupportedAbis().toString());
 
-            Device device2 = devices.get(2);
+            Device device2 = devices.get("Mips 64", "Mips");
             assertEquals("tag-2", device2.getTagId());
             assertEquals("{ro-myservice-port=1234, " +
                           "ro.RAM.Size=1024 MiB, " +
@@ -213,6 +224,16 @@
                          device2.getBootProps().toString());
             assertEquals("MIPS32+64", device2.getDefaultHardware().getCpu());
             assertEquals("[mips, mips64]", device2.getDefaultHardware().getSupportedAbis().toString());
+
+            assertEquals(device2.getChinSize(), 0);
+            assertFalse(device2.isScreenRound());
+
+            Device device3 = devices.get("wear_round_chin", "Google");
+            assertEquals("android-wear", device3.getTagId());
+            assertEquals(device3.getDefaultHardware().getScreen().getChin(), 30);
+            assertEquals(device3.getChinSize(), 30);
+            assertTrue(device3.isScreenRound());
+            assertEquals(device3.getDefaultHardware().getScreen().getScreenRound(), ScreenRound.ROUND);
         } finally {
             stream.close();
         }
@@ -223,27 +244,27 @@
         replacements.put("api-level", "1-");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
         try {
-            List<Device> devices = DeviceParser.parse(stream);
+            Table<String, String, Device> devices = DeviceParser.parse(stream);
             assertEquals(1, devices.size());
-            Device device = devices.get(0);
+            Device device = devices.get("Galaxy Nexus", "Samsung");
             assertTrue(device.getSoftware(1) != null);
             assertTrue(device.getSoftware(2) != null);
             assertTrue(device.getSoftware(0) == null);
             replacements.put("api-level", "-2");
             stream = DeviceSchemaTest.getReplacedStream(replacements);
-            device = DeviceParser.parse(stream).get(0);
+            device = DeviceParser.parse(stream).get("Galaxy Nexus", "Samsung");
             assertTrue(device.getSoftware(2) != null);
             assertTrue(device.getSoftware(3) == null);
             replacements.put("api-level", "1-2");
             stream = DeviceSchemaTest.getReplacedStream(replacements);
-            device = DeviceParser.parse(stream).get(0);
+            device = DeviceParser.parse(stream).get("Galaxy Nexus", "Samsung");
             assertTrue(device.getSoftware(0) == null);
             assertTrue(device.getSoftware(1) != null);
             assertTrue(device.getSoftware(2) != null);
             assertTrue(device.getSoftware(3) == null);
             replacements.put("api-level", "-");
             stream = DeviceSchemaTest.getReplacedStream(replacements);
-            device = DeviceParser.parse(stream).get(0);
+            device = DeviceParser.parse(stream).get("Galaxy Nexus", "Samsung");
             assertTrue(device.getSoftware(0) != null);
             assertTrue(device.getSoftware(15) != null);
         } finally {
@@ -256,9 +277,9 @@
         replacements.put("networking", "NFD");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
         try {
-            List<Device> devices = DeviceParser.parse(stream);
+            Table<String, String, Device> devices = DeviceParser.parse(stream);
             assertEquals(1, devices.size());
-            assertEquals(0, devices.get(0).getDefaultHardware().getNetworking().size());
+            assertEquals(0, devices.get("Galaxy Nexus", "Samsung").getDefaultHardware().getNetworking().size());
             fail();
         } catch (SAXParseException e) {
             assertTrue(e.getMessage().startsWith("cvc-enumeration-valid: Value 'NFD'"));
@@ -271,11 +292,11 @@
         InputStream stream = DeviceSchemaTest.class.getResourceAsStream(
                 "devices_minimal.xml");
         try {
-            List<Device> devices = DeviceParser.parse(stream);
+            Table<String, String, Device> devices = DeviceParser.parse(stream);
             assertEquals("Parsing devices_minimal.xml produces the wrong number of devices", 1,
                     devices.size());
 
-            Device device = devices.get(0);
+            Device device = devices.get("Galaxy Nexus", "Samsung");
             assertEquals("Galaxy Nexus", device.getDisplayName());
 
             assertEquals(new Dimension(1280, 720), device.getScreenSize(ScreenOrientation.LANDSCAPE));
@@ -284,5 +305,4 @@
             stream.close();
         }
     }
-
 }
diff --git a/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java b/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java
index e0bcf66..285ef9a 100644
--- a/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java
@@ -18,13 +18,13 @@
 
 import com.android.dvlib.DeviceSchemaTest;
 
+import com.google.common.collect.Table;
 import junit.framework.TestCase;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
@@ -33,41 +33,29 @@
     public void testWriteIsValid_Minimal() throws Exception {
         InputStream devicesFile =
             DeviceSchemaTest.class.getResourceAsStream("devices_minimal.xml");
-        List<Device> devices = DeviceParser.parse(devicesFile);
+        Table<String, String, Device> devices = DeviceParser.parse(devicesFile);
         assertEquals("Parsed devices contained an un expected number of devices",
                 1, devices.size());
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
+        DeviceWriter.writeToXml(baos, devices.values());
         String written = baos.toString();
-        List<Device> writtenDevices = DeviceParser.parse(
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
                 new ByteArrayInputStream(written.getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(
-                    "Device " + i + " differs in XML " + written,
-                    "\n" + devices.get(i), "\n" + writtenDevices.get(i));
-        }
+        assertEquals(devices, writtenDevices);
     }
 
     public void testWriteIsValid_Full() throws Exception {
         InputStream devicesFile =
             DeviceSchemaTest.class.getResourceAsStream("devices.xml");
-        List<Device> devices = DeviceParser.parse(devicesFile);
+        Table<String, String, Device> devices = DeviceParser.parse(devicesFile);
         assertEquals("Parsed devices contained an unexpected number of devices",
-                3, devices.size());
+                4, devices.size());
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
+        DeviceWriter.writeToXml(baos, devices.values());
         String written = baos.toString();
-        List<Device> writtenDevices = DeviceParser.parse(
-                new ByteArrayInputStream(written.getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(
-                    "Device " + i + " differs in XML " + written,
-                    "\n" + devices.get(i), "\n" + writtenDevices.get(i));
-        }
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
+          new ByteArrayInputStream(written.getBytes()));
+        assertEquals(devices, writtenDevices);
     }
 
     public void testLocale() throws Exception {
@@ -76,11 +64,11 @@
             Locale.setDefault(Locale.FRANCE);
             InputStream devicesFile =
                 DeviceSchemaTest.class.getResourceAsStream("devices.xml");
-            List<Device> devices = DeviceParser.parse(devicesFile);
+            Table<String, String, Device> devices = DeviceParser.parse(devicesFile);
             assertEquals("Parsed devices contained an unexpected number of devices",
-                    3, devices.size());
+                    4, devices.size());
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            DeviceWriter.writeToXml(baos, devices);
+            DeviceWriter.writeToXml(baos, devices.values());
             String xml = baos.toString();
             assertTrue(xml.contains(".00"));
             assertFalse(xml.contains(",00"));
@@ -93,78 +81,58 @@
         Map<String, String> replacements = new HashMap<String, String>();
         replacements.put("api-level", "1-");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
-        List<Device> devices = DeviceParser.parse(stream);
+        Table<String, String, Device> devices = DeviceParser.parse(stream);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
-        List<Device> writtenDevices = DeviceParser.parse(
+        DeviceWriter.writeToXml(baos, devices.values());
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
                 new ByteArrayInputStream(baos.toString().getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(devices.get(i), writtenDevices.get(i));
-        }
+        assertEquals(devices, writtenDevices);
     }
 
     public void testApiUpperBound() throws Exception {
         Map<String, String> replacements = new HashMap<String, String>();
         replacements.put("api-level", "-10");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
-        List<Device> devices = DeviceParser.parse(stream);
+        Table<String, String, Device> devices = DeviceParser.parse(stream);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
-        List<Device> writtenDevices = DeviceParser.parse(
+        DeviceWriter.writeToXml(baos, devices.values());
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
                 new ByteArrayInputStream(baos.toString().getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(devices.get(i), writtenDevices.get(i));
-        }
+        assertEquals(devices, writtenDevices);
     }
 
     public void testApiNeitherBound() throws Exception {
         Map<String, String> replacements = new HashMap<String, String>();
         replacements.put("api-level", "-");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
-        List<Device> devices = DeviceParser.parse(stream);
+        Table<String, String, Device> devices = DeviceParser.parse(stream);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
-        List<Device> writtenDevices = DeviceParser.parse(
+        DeviceWriter.writeToXml(baos, devices.values());
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
                 new ByteArrayInputStream(baos.toString().getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(devices.get(i), writtenDevices.get(i));
-        }
+        assertEquals(devices, writtenDevices);
     }
 
     public void testApiBothBound() throws Exception {
         Map<String, String> replacements = new HashMap<String, String>();
         replacements.put("api-level", "9-10");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
-        List<Device> devices = DeviceParser.parse(stream);
+        Table<String, String, Device> devices = DeviceParser.parse(stream);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
-        List<Device> writtenDevices = DeviceParser.parse(
+        DeviceWriter.writeToXml(baos, devices.values());
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
                 new ByteArrayInputStream(baos.toString().getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(devices.get(i), writtenDevices.get(i));
-        }
+        assertEquals(devices, writtenDevices);
     }
     public void testApiSingle() throws Exception {
         Map<String, String> replacements = new HashMap<String, String>();
         replacements.put("api-level", "10");
         InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
-        List<Device> devices = DeviceParser.parse(stream);
+        Table<String, String, Device> devices = DeviceParser.parse(stream);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        DeviceWriter.writeToXml(baos, devices);
-        List<Device> writtenDevices = DeviceParser.parse(
+        DeviceWriter.writeToXml(baos, devices.values());
+        Table<String, String, Device> writtenDevices = DeviceParser.parse(
                 new ByteArrayInputStream(baos.toString().getBytes()));
-        assertEquals("Writing and reparsing returns a different number of devices",
-                devices.size(), writtenDevices.size());
-        for (int i = 0; i < devices.size(); i++) {
-            assertEquals(devices.get(i), writtenDevices.get(i));
-        }
+        assertEquals(devices, writtenDevices);
     }
 }
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java
index 0184d48..d10bb4f 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java
@@ -27,6 +27,7 @@
 import com.android.sdklib.SystemImage;
 import com.android.sdklib.io.FileOp;
 import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.collect.ImmutableList;
 
 import java.io.File;
 import java.util.List;
@@ -42,7 +43,7 @@
     private final int mRevision;
     private final String mName;
     private ISystemImage[] mSystemImages;
-    private IOptionalLibrary[] mOptionalLibraries;
+    private ImmutableList<OptionalLibrary> mOptionalLibraries = ImmutableList.of();
 
     public MockAddonTarget(String name, IAndroidTarget parentTarget, int revision) {
         mName = name;
@@ -103,12 +104,19 @@
         return "/sdk/add-ons/addon-" + mName;
     }
 
+    @NonNull
     @Override
-    public IOptionalLibrary[] getOptionalLibraries() {
+    public List<OptionalLibrary> getAdditionalLibraries() {
         return mOptionalLibraries;
     }
 
-    public void setOptionalLibraries(IOptionalLibrary[] libraries) {
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getOptionalLibraries() {
+        return ImmutableList.of();
+    }
+
+    public void setOptionalLibraries(ImmutableList<OptionalLibrary> libraries) {
         mOptionalLibraries = libraries;
     }
 
@@ -167,6 +175,7 @@
         return mRevision;
     }
 
+    @NonNull
     @Override
     public File[] getSkins() {
         return FileOp.EMPTY_FILE_ARRAY;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java
index b3319d0..37cd184 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java
@@ -27,6 +27,7 @@
 import com.android.sdklib.SystemImage;
 import com.android.sdklib.io.FileOp;
 import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.collect.ImmutableList;
 
 import java.io.File;
 import java.util.List;
@@ -100,9 +101,16 @@
         return "/sdk/platforms/android-" + getVersion().getApiString();
     }
 
+    @NonNull
     @Override
-    public IOptionalLibrary[] getOptionalLibraries() {
-        return null;
+    public List<OptionalLibrary> getOptionalLibraries() {
+        return ImmutableList.of();
+    }
+
+    @NonNull
+    @Override
+    public List<OptionalLibrary> getAdditionalLibraries() {
+        return ImmutableList.of();
     }
 
     @Override
@@ -160,6 +168,7 @@
         return mRevision;
     }
 
+    @NonNull
     @Override
     public File[] getSkins() {
         return FileOp.EMPTY_FILE_ARRAY;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java
index 7e95e78..672d69d 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java
@@ -114,12 +114,12 @@
                 LocationType.IN_IMAGES_SUBFOLDER,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI_V7A,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
         makeSystemImageFolder(new SystemImage(mSdkMan, t,
                 LocationType.IN_IMAGES_SUBFOLDER,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_INTEL_ATOM,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         mSdkMan.reloadSdk(getLog());
         t = mSdkMan.getTargets()[0];
@@ -142,12 +142,12 @@
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
         makeSystemImageFolder(new SystemImage(mSdkMan, t,
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_ARMEABI_V7A,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         mSdkMan.reloadSdk(getLog());
 
@@ -171,7 +171,7 @@
                 LocationType.IN_SYSTEM_IMAGE,
                 SystemImage.DEFAULT_TAG,
                 SdkConstants.ABI_INTEL_ATOM,
-                FileOp.EMPTY_FILE_ARRAY));
+                FileOp.EMPTY_FILE_ARRAY), null);
 
         assertEquals(
                 "[Android SDK Tools, revision 1.0.1, " +
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java
index 78e5b85..eb832f2 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java
@@ -18,9 +18,6 @@
 
 import com.android.annotations.NonNull;
 import com.android.annotations.Nullable;
-import com.android.sdklib.internal.repository.CanceledByUserException;
-import com.android.sdklib.internal.repository.DownloadCache;
-import com.android.sdklib.internal.repository.ITaskMonitor;
 import com.android.utils.Pair;
 
 import org.apache.http.Header;
@@ -57,7 +54,7 @@
 
     private Strategy mOverrideStrategy;
 
-    public final static int THROW_FNF = -1;
+    public static final int THROW_FNF = -1;
 
     /**
      * Creates a download cache with a {@code DIRECT} strategy and
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java
index 9074769..134dc7d 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java
@@ -17,7 +17,6 @@
 package com.android.sdklib.internal.repository.packages;
 
 import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.SystemImage;
 import com.android.sdklib.AndroidVersion.AndroidVersionException;
 import com.android.sdklib.internal.repository.archives.Archive;
 import com.android.sdklib.repository.PkgProps;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java
index 05e0198..052292f 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java
@@ -16,7 +16,6 @@
 
 package com.android.sdklib.internal.repository.packages;
 
-import com.android.sdklib.internal.repository.packages.BrokenPackage;
 import com.android.sdklib.repository.FullRevision;
 import com.android.sdklib.repository.descriptors.PkgDesc;
 
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java
index 998c578..b3233ea 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java
@@ -60,7 +60,7 @@
     public void testInstallId() throws Exception {
         Properties props1 = createExpectedProps(true);
         BuildToolPackage p1 = new BuildToolPackageFakeArchive(props1);
-        assertEquals("build-tools-1.2.3_rc4", p1.installId());
+        assertEquals("build-tools-1.2.3-preview", p1.installId());
 
         Properties props2 = createExpectedProps(false);
         BuildToolPackage p2 = new BuildToolPackageFakeArchive(props2);
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java
index 51f9e35..37173b6 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java
@@ -16,7 +16,6 @@
 
 package com.android.sdklib.internal.repository.packages;
 
-import com.android.sdklib.internal.repository.packages.BrokenPackage;
 import com.android.sdklib.repository.FullRevision;
 import com.android.sdklib.repository.descriptors.PkgDesc;
 
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java
index d5785ce..115b1a1 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java
@@ -17,8 +17,6 @@
 package com.android.sdklib.internal.repository.packages;
 
 import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.internal.repository.packages.SourcePackage;
-import com.android.sdklib.internal.repository.packages.SystemImagePackage;
 import com.android.sdklib.internal.repository.sources.SdkSource;
 
 
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java
index 3c7baec..c09c081 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java
@@ -16,7 +16,6 @@
 
 package com.android.sdklib.internal.repository.packages;
 
-import com.android.sdklib.internal.repository.packages.SystemImagePackage;
 import com.android.sdklib.internal.repository.sources.SdkSource;
 
 
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java
index 5c4b5a1..b07d422 100644
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java
@@ -18,7 +18,7 @@
 
 import com.android.sdklib.repository.PreciseRevision;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 import junit.framework.TestCase;
 
 import java.util.Arrays;
@@ -37,7 +37,7 @@
         assertEquals(PreciseRevision.IMPLICIT_MINOR_REV, p.getMinor());
         assertEquals(PreciseRevision.IMPLICIT_MICRO_REV, p.getMicro());
         assertEquals(PreciseRevision.NOT_A_PREVIEW, p.getPreview());
-        assertFalse (p.isPreview());
+        assertFalse(p.isPreview());
         assertEquals("5", p.toShortString());
         assertEquals(p, PreciseRevision.parseRevision("5"));
         assertEquals("5", p.toString());
@@ -106,6 +106,8 @@
         assertFalse(p.isPreview());
         assertEquals("10.11.12", p.toShortString());
         assertEquals("10.11.12", p.toString());
+        assertEquals("[10, 11, 12]",    Arrays.toString(p.toIntArray(false /*includePreview*/)));
+        assertEquals("[10, 11, 12, 0]", Arrays.toString(p.toIntArray(true  /*includePreview*/)));
 
         p = new PreciseRevision(10, 11, 12, 13);
         assertEquals(10, p.getMajor());
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java
index f03aed9..d05c6ee 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java
@@ -24,7 +24,6 @@
 import com.android.sdklib.internal.repository.packages.ExtraPackage;
 import com.android.sdklib.internal.repository.packages.IMinToolsDependency;
 import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.sources.SdkAddonSource;
 import com.android.sdklib.repository.SdkAddonConstants;
 import com.android.utils.Pair;
 
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java
index 596e2d5..b8190db 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java
@@ -29,7 +29,6 @@
 import com.android.sdklib.internal.repository.packages.SourcePackage;
 import com.android.sdklib.internal.repository.packages.SystemImagePackage;
 import com.android.sdklib.internal.repository.packages.ToolPackage;
-import com.android.sdklib.internal.repository.sources.SdkRepoSource;
 import com.android.sdklib.repository.SdkRepoConstants;
 import com.android.utils.Pair;
 
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java
index 6313e69..1336bfc 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java
@@ -17,8 +17,6 @@
 package com.android.sdklib.internal.repository.sources;
 
 
-import com.android.sdklib.internal.repository.sources.SdkSourceProperties;
-
 import junit.framework.TestCase;
 
 public class SdkSourcePropertiesTest extends TestCase {
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java
index adfb0e9..63b774a 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java
@@ -51,7 +51,7 @@
         m.loadSettings();
         Settings s = m.getSettings();
         assertFalse(s.getAskBeforeAdbRestart());
-        assertFalse(s.getEnablePreviews());
+        assertTrue(s.getEnablePreviews());
         assertFalse(s.getForceHttp());
         assertTrue (s.getShowUpdateOnly());
         assertTrue (s.getUseDownloadCache());
@@ -63,14 +63,14 @@
 
         Settings s1 = m.getSettings();
         assertFalse(s1.getAskBeforeAdbRestart());
-        assertFalse(s1.getEnablePreviews());
+        assertTrue (s1.getEnablePreviews());
         assertFalse(s1.getForceHttp());
         assertTrue (s1.getShowUpdateOnly());
         assertTrue (s1.getUseDownloadCache());
         assertEquals(-1, s1.getMonitorDensity());
 
         m.setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, true);
-        m.setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, true);
+        m.setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, false);
         m.setSetting(ISettingsPage.KEY_FORCE_HTTP, true);
         m.setShowUpdateOnly(false);
         m.setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, false);
@@ -79,7 +79,7 @@
         Settings s2 = m.getSettings();
         assertSame(s2, s1);
         assertTrue (s2.getAskBeforeAdbRestart());
-        assertTrue (s2.getEnablePreviews());
+        assertFalse(s2.getEnablePreviews());
         assertTrue (s2.getForceHttp());
         assertFalse(s2.getShowUpdateOnly());
         assertFalse(s2.getUseDownloadCache());
@@ -94,7 +94,7 @@
         Settings s3 = m3.getSettings();
         assertNotSame(s3, s1);
         assertTrue (s3.getAskBeforeAdbRestart());
-        assertTrue (s3.getEnablePreviews());
+        assertFalse(s3.getEnablePreviews());
         assertTrue (s3.getForceHttp());
         assertFalse(s3.getShowUpdateOnly());
         assertFalse(s3.getUseDownloadCache());
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java
index 4a2bf9b..9dfe12e 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java
@@ -100,7 +100,7 @@
         public void clear() {
             mLog.clear();
         }
-    };
+    }
 
     public final void testAcceptLicenses_Empty() {
         SdkManager sdkman = getSdkManager();
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java b/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java
index dccec59..0cc7a48 100755
--- a/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java
+++ b/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.sdklib.repository;
 
+import static org.junit.Assert.fail;
+
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
@@ -43,13 +45,13 @@
      * Also fails the unit test if any error was generated.
      */
     public void verify() {
-        if (mWarnings.length() > 0) {
+        if (!mWarnings.isEmpty()) {
             System.err.println(mWarnings);
         }
 
-        if (mErrors.length() > 0) {
+        if (!mErrors.isEmpty()) {
             System.err.println(mErrors);
-            junit.framework.Assert.fail(mErrors);
+            fail(mErrors);
         }
     }
 
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java b/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java
index 42557c3..e01b81a 100755
--- a/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java
@@ -139,6 +139,11 @@
                 PkgDesc.Builder.newTool(new FullRevision(1, 2, 3, 1), min5671).create();
         assertFalse(p1231.isUpdateFor(f122));
         assertFalse(f122 .isUpdateFor(p1231));
+        assertFalse(p1231.isUpdateFor(f122, FullRevision.PreviewComparison.COMPARE_NUMBER));
+        assertFalse(p1231.isUpdateFor(f122, FullRevision.PreviewComparison.COMPARE_TYPE));
+        // ...unless we ignore them explicitly
+        assertTrue(p1231.isUpdateFor(f122, FullRevision.PreviewComparison.IGNORE));
+
         // but previews are used for comparisons
         assertTrue (p1231.compareTo(f122 ) > 0);
         assertTrue (f123 .compareTo(p1231) > 0);
@@ -283,12 +288,12 @@
         assertFalse(p.hasMinPlatformToolsRev());
         assertNull(p.getMinPlatformToolsRev());
 
-        assertEquals("doc-19", p.getInstallId());
+        assertEquals("doc", p.getInstallId());
         assertEquals(FileOp.append(mRoot, "docs"),
                 p.getCanonicalInstallFolder(mRoot));
 
         assertEquals("<PkgDesc Type=doc Android=API 19 MajorRev=1>", p.toString());
-        assertEquals("Documentation for Android SDK 19", p.getListDescription());
+        assertEquals("Documentation for Android SDK", p.getListDescription());
     }
 
     public final void testPkgDescDoc_Update() throws Exception {
@@ -371,8 +376,8 @@
         assertFalse(p.hasMinPlatformToolsRev());
         assertNull (p.getMinPlatformToolsRev());
 
-        assertEquals("build-tools-1.2.3_rc4", p.getInstallId());
-        assertEquals(FileOp.append(mRoot, "build-tools", "build-tools-1.2.3_rc4"),
+        assertEquals("build-tools-1.2.3-preview", p.getInstallId());
+        assertEquals(FileOp.append(mRoot, "build-tools", "build-tools-1.2.3-preview"),
                 p.getCanonicalInstallFolder(mRoot));
 
         assertEquals("<PkgDesc Type=build_tools FullRev=1.2.3 rc4>", p.toString());
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/local/LocalPlatformPkgInfoTest.java b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalPlatformPkgInfoTest.java
new file mode 100644
index 0000000..c161e40
--- /dev/null
+++ b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalPlatformPkgInfoTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 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.sdklib.repository.local;
+
+import static org.junit.Assert.*;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.IAndroidTarget;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class LocalPlatformPkgInfoTest {
+
+    @Test
+    public void testOptionalLibWith1() throws Exception {
+        String content =
+                "[\n" +
+                        "  {\n" +
+                        "    \"name\": \"org.apache.http.legacy\",\n" +
+                        "    \"jar\": \"org.apache.http.legacy.jar\",\n" +
+                        "    \"manifest\": false\n" +
+                        "  }\n" +
+                        "]\n";
+
+        File json = getJsonFile(content);
+
+        List<IAndroidTarget.OptionalLibrary> libs = LocalPlatformPkgInfo.getLibsFromJson(json);
+
+        assertEquals(1, libs.size());
+        IAndroidTarget.OptionalLibrary lib = libs.get(0);
+        assertEquals("org.apache.http.legacy", lib.getName());
+        assertEquals(new File(json.getParentFile(), "org.apache.http.legacy.jar"), lib.getJar());
+        assertEquals(false, lib.isManifestEntryRequired());
+    }
+
+    @NonNull
+    private static File getJsonFile(String content) throws IOException {
+        File json = File.createTempFile("testGetLibsFromJson", "");
+        json.deleteOnExit();
+
+        Files.write(content, json, Charsets.UTF_8);
+        return json;
+    }
+
+
+}
\ No newline at end of file
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java
index b2edf28..15c6b37 100755
--- a/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java
@@ -147,7 +147,7 @@
         assertEquals(null, pi.getLoadError());
         assertEquals(new MajorRevision(2), pi.getDesc().getMajorRevision());
         assertEquals("<LocalDocPkgInfo <PkgDesc Type=doc Android=API 18 MajorRev=2>>", pi.toString());
-        assertEquals("Documentation for Android SDK 18, rev 2", pi.getListDescription());
+        assertEquals("Documentation for Android SDK", pi.getListDescription());
         assertSame(pi, mLS.getPkgInfo(pi.getDesc()));
     }
 
diff --git a/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java b/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java
index 6c6c405..3fd1fa5 100755
--- a/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java
@@ -413,7 +413,7 @@
                     throw new PatchException("Failed to read control data") ;
                 }
                 ctrl[i] = offtin(buf, 0);
-            };
+            }
 
             /* Sanity-check */
             if (newpos + ctrl[0] > newsize) {
diff --git a/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java b/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java
index 213efc2..414c74d 100755
--- a/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java
@@ -16,8 +16,6 @@
 
 package com.android.sdklib.util;
 
-import com.android.sdklib.util.LineUtil;
-
 import junit.framework.TestCase;
 
 
diff --git a/templates/activities/AlwaysOnWearActivity/globals.xml.ftl b/templates/activities/AlwaysOnWearActivity/globals.xml.ftl
new file mode 100644
index 0000000..4bf836f
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/globals.xml.ftl
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/activities/AlwaysOnWearActivity/recipe.xml.ftl b/templates/activities/AlwaysOnWearActivity/recipe.xml.ftl
new file mode 100644
index 0000000..278df15
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/recipe.xml.ftl
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<recipe>
+
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+    <merge from="res/values/strings.xml.ftl"
+             to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
+
+    <merge from="build.gradle.ftl"
+             to="${escapeXmlAttribute(projectOut)}/build.gradle" />
+
+    <instantiate from="res/layout/blank_activity.xml.ftl"
+            to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+
+    <instantiate from="src/app_package/BlankActivity.java.ftl"
+                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+
+    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+    <open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+</recipe>
diff --git a/templates/activities/AlwaysOnWearActivity/root/AndroidManifest.xml.ftl b/templates/activities/AlwaysOnWearActivity/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..a0e3c80
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/AndroidManifest.xml.ftl
@@ -0,0 +1,24 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <application>
+        <uses-library android:name="com.google.android.wearable" android:required="false" />
+        <activity android:name="${relativePackage}.${activityClass}"
+            <#if isNewProject>
+            android:label="@string/app_name"
+            <#else>
+            android:label="@string/title_${activityToLayout(activityClass)}"
+            </#if>
+            android:theme="@android:style/Theme.DeviceDefault.Light"
+            >
+            <#if isLauncher>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            </#if>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/templates/activities/AlwaysOnWearActivity/root/build.gradle.ftl b/templates/activities/AlwaysOnWearActivity/root/build.gradle.ftl
new file mode 100644
index 0000000..d62125c
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/build.gradle.ftl
@@ -0,0 +1,3 @@
+dependencies {
+  provided 'com.google.android.wearable:wearable:1.0.0'
+}
diff --git a/templates/activities/AlwaysOnWearActivity/root/res/layout/blank_activity.xml.ftl b/templates/activities/AlwaysOnWearActivity/root/res/layout/blank_activity.xml.ftl
new file mode 100644
index 0000000..7ae5edf
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/res/layout/blank_activity.xml.ftl
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.wearable.view.BoxInsetLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/container"
+    tools:context="${relativePackage}.${activityClass}"
+    tools:deviceIds="wear">
+
+    <TextView
+        android:id="@+id/text"
+        app:layout_box="all"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/hello_world" />
+
+    <TextView
+        android:id="@+id/clock"
+        app:layout_box="all"
+        android:layout_gravity="bottom|start"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@android:color/white" />
+
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/templates/activities/AlwaysOnWearActivity/root/res/values/strings.xml.ftl b/templates/activities/AlwaysOnWearActivity/root/res/values/strings.xml.ftl
new file mode 100644
index 0000000..304117e
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/res/values/strings.xml.ftl
@@ -0,0 +1,6 @@
+<resources>
+    <#if !isNewProject>
+    <string name="title_${activityToLayout(activityClass)}">${escapeXmlString(activityClass)}</string>
+    </#if>
+    <string name="hello_world">Hello World!</string>
+</resources>
diff --git a/templates/activities/AlwaysOnWearActivity/root/src/app_package/BlankActivity.java.ftl b/templates/activities/AlwaysOnWearActivity/root/src/app_package/BlankActivity.java.ftl
new file mode 100644
index 0000000..7e6673c
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/src/app_package/BlankActivity.java.ftl
@@ -0,0 +1,64 @@
+package ${packageName};
+
+import android.os.Bundle;
+import android.support.wearable.activity.WearableActivity;
+import android.support.wearable.view.BoxInsetLayout;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class ${activityClass} extends WearableActivity {
+
+    private static final SimpleDateFormat AMBIENT_DATE_FORMAT =
+            new SimpleDateFormat("HH:mm", Locale.US);
+
+    private BoxInsetLayout mContainerView;
+    private TextView mTextView;
+    private TextView mClockView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.${layoutName});
+        setAmbientEnabled();
+
+        mContainerView = (BoxInsetLayout) findViewById(R.id.container);
+        mTextView = (TextView) findViewById(R.id.text);
+        mClockView = (TextView) findViewById(R.id.clock);
+    }
+
+    @Override
+    public void onEnterAmbient(Bundle ambientDetails) {
+        super.onEnterAmbient(ambientDetails);
+        updateDisplay();
+    }
+
+    @Override
+    public void onUpdateAmbient() {
+        super.onUpdateAmbient();
+        updateDisplay();
+    }
+
+    @Override
+    public void onExitAmbient() {
+        updateDisplay();
+        super.onExitAmbient();
+    }
+
+    private void updateDisplay() {
+        if (isAmbient()) {
+            mContainerView.setBackgroundColor(getResources().getColor(android.R.color.black));
+            mTextView.setTextColor(getResources().getColor(android.R.color.white));
+            mClockView.setVisibility(View.VISIBLE);
+
+            mClockView.setText(AMBIENT_DATE_FORMAT.format(new Date()));
+        } else {
+            mContainerView.setBackground(null);
+            mTextView.setTextColor(getResources().getColor(android.R.color.black));
+            mClockView.setVisibility(View.GONE);
+        }
+    }
+}
diff --git a/templates/activities/AlwaysOnWearActivity/template.xml b/templates/activities/AlwaysOnWearActivity/template.xml
new file mode 100644
index 0000000..d3aeeb1
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/template.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<template
+    format="4"
+    revision="1"
+    name="Always On Wear Activity"
+    minApi="20"
+    minBuildApi="20"
+    description="Creates an always on activity for Android Wear">
+
+    <category value="Activity" />
+    <formfactor value="Wear" />
+
+    <parameter
+        id="activityClass"
+        name="Activity Name"
+        type="string"
+        constraints="class|unique|nonempty"
+        suggest="${layoutToActivity(layoutName)}"
+        default="MainActivity"
+        help="The name of the activity class to create" />
+
+    <parameter
+        id="layoutName"
+        name="Layout Name"
+        type="string"
+        constraints="layout|unique|nonempty"
+        suggest="${activityToLayout(activityClass)}"
+        default="activity_main"
+        help="The name of the layout to create for the activity" />
+
+    <parameter
+        id="isLauncher"
+        name="Launcher Activity"
+        type="boolean"
+        default="true"
+        help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it the default launchable activity" />
+
+    <parameter
+        id="packageName"
+        name="Package name"
+        type="string"
+        constraints="package"
+        default="com.mycompany.myapp" />
+
+    <!-- 128x128 thumbnails relative to template.xml -->
+    <thumbs>
+        <!-- default thumbnail is required -->
+        <thumb>template_thumb.png</thumb>
+    </thumbs>
+
+    <globals file="globals.xml.ftl" />
+    <execute file="recipe.xml.ftl" />
+
+</template>
diff --git a/templates/activities/AlwaysOnWearActivity/template_thumb.png b/templates/activities/AlwaysOnWearActivity/template_thumb.png
new file mode 100644
index 0000000..42ebcf2
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/template_thumb.png
Binary files differ
diff --git a/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl b/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl
index 3b1d90a..3cae7ae 100644
--- a/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl
+++ b/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl
@@ -106,11 +106,19 @@
             for (int j = 0; j < NUM_COLS; j++) {
                 listRowAdapter.add(list.get(j % 5));
             }
-            HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i], null);
+            <#if buildApi gte 22>
+                HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i]);
+            <#else>
+                HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i], null);
+            </#if>
             mRowsAdapter.add(new ListRow(header, listRowAdapter));
         }
 
-        HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES", null);
+        <#if buildApi gte 22>
+            HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES");
+        <#else>
+            HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES", null);
+        </#if>
 
         GridItemPresenter mGridPresenter = new GridItemPresenter();
         ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
diff --git a/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl b/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl
index eb7adf1..31ef35c 100644
--- a/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl
+++ b/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl
@@ -313,7 +313,11 @@
         for (Movie movie : mItems) {
             listRowAdapter.add(movie);
         }
-        HeaderItem header = new HeaderItem(0, getString(R.string.related_movies), null);
+        <#if buildApi gte 22>
+            HeaderItem header = new HeaderItem(0, getString(R.string.related_movies));
+        <#else>
+            HeaderItem header = new HeaderItem(0, getString(R.string.related_movies), null);
+        </#if>
         mRowsAdapter.add(new ListRow(header, listRowAdapter));
 
     }
diff --git a/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl b/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl
index 0f90a45..1d3d2a7 100644
--- a/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl
+++ b/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl
@@ -154,7 +154,12 @@
                 listRowAdapter.add(list.get(j % 5));
             }
 
-            HeaderItem header = new HeaderItem(0, subcategories[0], null);
+            <#if buildApi gte 22>
+                HeaderItem header = new HeaderItem(0, subcategories[0]);
+            <#else>
+                HeaderItem header = new HeaderItem(0, subcategories[0], null);
+            </#if>
+
             adapter.add(new ListRow(header, listRowAdapter));
 
             setAdapter(adapter);
diff --git a/templates/activities/BlankActivity/globals.xml.ftl b/templates/activities/BlankActivity/globals.xml.ftl
index 5f60e5c..09e5c5e 100644
--- a/templates/activities/BlankActivity/globals.xml.ftl
+++ b/templates/activities/BlankActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
 <?xml version="1.0"?>
 <globals>
     <global id="manifestOut" value="${manifestDir}" />
-    <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="resOut" value="${resDir}" />
     <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
diff --git a/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl
index 1b8d938..d6aeee3 100644
--- a/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl
+++ b/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl
@@ -1,12 +1,14 @@
 package ${packageName};
 
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuItem;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity {
+public class ${activityClass} extends ${superClass} {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/templates/activities/BlankActivityWithFragment/globals.xml.ftl b/templates/activities/BlankActivityWithFragment/globals.xml.ftl
index 7678b9b..06453f7 100644
--- a/templates/activities/BlankActivityWithFragment/globals.xml.ftl
+++ b/templates/activities/BlankActivityWithFragment/globals.xml.ftl
@@ -2,7 +2,15 @@
 <globals>
     <global id="fragmentClass" value="${activityClass}Fragment" />
     <global id="manifestOut" value="${manifestDir}" />
-    <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="resOut" value="${resDir}" />
     <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
diff --git a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl
index e18ad91..ec2c685 100644
--- a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl
+++ b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl
@@ -1,13 +1,14 @@
 package ${packageName};
 
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuItem;
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
-public class ${activityClass} extends ${appCompat?string('ActionBar','')}Activity {
+public class ${activityClass} extends ${superClass} {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl
index bacabb4..b45f76d 100644
--- a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl
+++ b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl
@@ -5,8 +5,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * A placeholder fragment containing a simple view.
diff --git a/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl b/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
index d8db3c2..4364a41 100644
--- a/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
+++ b/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
@@ -13,7 +13,9 @@
 import android.view.MenuItem;
 import android.support.v4.app.NavUtils;
 </#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * An example full-screen activity that shows and hides the system UI (i.e.
diff --git a/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl b/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl
index 5f60e5c..09e5c5e 100644
--- a/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl
+++ b/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
 <?xml version="1.0"?>
 <globals>
     <global id="manifestOut" value="${manifestDir}" />
-    <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="resOut" value="${resDir}" />
     <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
diff --git a/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl b/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl
index 5256fc7..12b954d 100644
--- a/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl
+++ b/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl
@@ -22,11 +22,6 @@
     <instantiate from="src/app_package/SimpleActivity.java.ftl"
              to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
 
-    <#if adFormat == "interstitial">
-    <instantiate from="res/layout/fragment_interstitial.xml.ftl"
-             to="${escapeXmlAttribute(resOut)}/layout/fragment_interstitial.xml" />
-    </#if>
-
     <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
     <open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
 </recipe>
diff --git a/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl
index 3c81789..14246ed 100644
--- a/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl
+++ b/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl
@@ -10,7 +10,7 @@
 </#if>
 
 import android.os.Bundle;
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
 import android.view.Menu;
 import android.view.MenuItem;
 <#if adFormat == "interstitial">
@@ -19,10 +19,11 @@
 import android.widget.TextView;
 </#if>
 import android.widget.Toast;
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity {
+public class ${activityClass} extends ${superClass} {
     // Remove the below line after defining your own ad unit ID.
     private static final String TOAST_TEXT = "Test ads are being shown. "
             + "To show live ads, replace the ad unit ID in res/values/strings.xml with your own ad unit ID.";
@@ -62,25 +63,7 @@
         mLevel = START_LEVEL;
 
         // Create the InterstitialAd and set the adUnitId (defined in values/strings.xml).
-        mInterstitialAd = new InterstitialAd(this);
-        mInterstitialAd.setAdUnitId(getString(R.string.interstitial_ad_unit_id));
-        mInterstitialAd.setAdListener(new AdListener() {
-            @Override
-            public void onAdLoaded() {
-                mNextLevelButton.setEnabled(true);
-            }
-
-            @Override
-            public void onAdFailedToLoad(int errorCode) {
-                mNextLevelButton.setEnabled(true);
-            }
-
-            @Override
-            public void onAdClosed() {
-                // Proceed to the next level.
-                goToNextLevel();
-            }
-        });
+        mInterstitialAd = newInterstitialAd();
         loadInterstitial();
         </#if>
 
@@ -111,6 +94,29 @@
     }
 
     <#if adFormat == "interstitial">
+    private InterstitialAd newInterstitialAd() {
+        InterstitialAd interstitialAd = new InterstitialAd(this);
+        interstitialAd.setAdUnitId(getString(R.string.interstitial_ad_unit_id));
+        interstitialAd.setAdListener(new AdListener() {
+            @Override
+            public void onAdLoaded() {
+                mNextLevelButton.setEnabled(true);
+            }
+
+            @Override
+            public void onAdFailedToLoad(int errorCode) {
+                mNextLevelButton.setEnabled(true);
+            }
+
+            @Override
+            public void onAdClosed() {
+                // Proceed to the next level.
+                goToNextLevel();
+            }
+        });
+        return interstitialAd;
+    }
+
     private void showInterstitial() {
         // Show the ad if it's ready. Otherwise toast and reload the ad.
         if (mInterstitialAd != null && mInterstitialAd.isLoaded()) {
@@ -132,6 +138,7 @@
     private void goToNextLevel() {
         // Show the next level and reload the ad to prepare for the level after.
         mLevelTextView.setText("Level " + (++mLevel));
+        mInterstitialAd = newInterstitialAd();
         loadInterstitial();
     }
     </#if>
diff --git a/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl b/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl
index 6041dc2..7c3e9e8 100644
--- a/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl
@@ -1,16 +1,22 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" >
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
-    <!-- The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
-         Google Maps Android API v2, but are recommended. -->
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+      <!-- 
+         The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
+         Google Maps Android API v2, but you must specify either coarse or fine
+         location permissions for the 'MyLocation' functionality. 
+     -->
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 
     <application>
-        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
-        <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="@string/google_maps_key"/>
+
+        <!-- 
+             The API key for Google Maps-based APIs is defined as a string resource.
+             (See the file "res/values/google_maps_api.xml").
+             Note that the API key is linked to the encryption key used to sign the APK.
+             You need a different API key for each encryption key, including the release key that is used to
+             sign the APK for publishing.
+             You can define the keys for the debug and release targets in src/debug/ and src/release/. 
+         -->
+        <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"/>
 
         <activity android:name="${relativePackage}.${activityClass}"
             android:label="@string/title_${simpleName}">
diff --git a/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl
index cff17a8..59df0e3 100644
--- a/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl
@@ -4,11 +4,14 @@
 
     To get one, follow this link, follow the directions and press "Create" at the end:
 
-https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=${debugKeystoreSha1}%3B${packageName}
+    https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=${debugKeystoreSha1}%3B${packageName}
 
     You can also add your credentials to an existing key, using this line:
     ${debugKeystoreSha1};${packageName}
 
+    Alternatively, follow the directions here:
+    https://developers.google.com/maps/documentation/android/start#get-key
+
     Once you have your key (it starts with "AIza"), replace the "google_maps_key"
     string in this file.
     -->
diff --git a/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl
index a16ec96..f960b28 100644
--- a/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl
@@ -5,9 +5,13 @@
     To do this, you can either add your release key credentials to your existing
     key, or create a new key.
 
+    Note that this file specifies the API key for the release build target.
+    If you have previously set up a key for the debug target with the debug signing certificate,
+    you will also need to set up a key for your release certificate.
+
     Follow the directions here:
 
-https://developers.google.com/maps/documentation/android/start#get_an_android_certificate_and_the_google_maps_api_key
+    https://developers.google.com/maps/documentation/android/signup
 
     Once you have your key (it starts with "AIza"), replace the "google_maps_key"
     string in this file.
diff --git a/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl b/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl
index 3b6c9b8..a19166b 100644
--- a/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl
@@ -1,5 +1,6 @@
 <fragment xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    xmlns:map="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:id="@+id/map"
diff --git a/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl b/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl
index ce90b52..518c0dc 100644
--- a/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl
+++ b/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl
@@ -3,63 +3,44 @@
 import android.support.v4.app.FragmentActivity;
 import android.os.Bundle;
 
+import com.google.android.gms.maps.CameraUpdateFactory;
 import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
 import com.google.android.gms.maps.SupportMapFragment;
 import com.google.android.gms.maps.model.LatLng;
 import com.google.android.gms.maps.model.MarkerOptions;
 
-public class ${activityClass} extends FragmentActivity {
+public class ${activityClass} extends FragmentActivity implements OnMapReadyCallback {
 
-    private GoogleMap mMap; // Might be null if Google Play services APK is not available.
+    private GoogleMap mMap;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.${layoutName});
-        setUpMapIfNeeded();
+        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
+        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.map);
+        mapFragment.getMapAsync(this);
     }
 
+
+    /**
+     * Manipulates the map once available.
+     * This callback is triggered when the map is ready to be used.
+     * This is where we can add markers or lines, add listeners or move the camera. In this case,
+     * we just add a marker near Sydney, Australia.
+     * If Google Play services is not installed on the device, the user will be prompted to install
+     * it inside the SupportMapFragment. This method will only be triggered once the user has
+     * installed Google Play services and returned to the app.
+     */
     @Override
-    protected void onResume() {
-        super.onResume();
-        setUpMapIfNeeded();
-    }
+    public void onMapReady(GoogleMap googleMap) {
+        mMap = googleMap;
 
-    /**
-     * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
-     * installed) and the map has not already been instantiated.. This will ensure that we only ever
-     * call {@link #setUpMap()} once when {@link #mMap} is not null.
-     * <p>
-     * If it isn't installed {@link SupportMapFragment} (and
-     * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
-     * install/update the Google Play services APK on their device.
-     * <p>
-     * A user can return to this FragmentActivity after following the prompt and correctly
-     * installing/updating/enabling the Google Play services. Since the FragmentActivity may not
-     * have been completely destroyed during this process (it is likely that it would only be
-     * stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
-     * method in {@link #onResume()} to guarantee that it will be called.
-     */
-    private void setUpMapIfNeeded() {
-        // Do a null check to confirm that we have not already instantiated the map.
-        if (mMap == null) {
-            // Try to obtain the map from the SupportMapFragment.
-            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
-                    .getMap();
-            // Check if we were successful in obtaining the map.
-            if (mMap != null) {
-                setUpMap();
-            }
-        }
-    }
-
-    /**
-     * This is where we can add markers or lines, add listeners or move the camera. In this case, we
-     * just add a marker near Africa.
-     * <p>
-     * This should only be called once and when we are sure that {@link #mMap} is not null.
-     */
-    private void setUpMap() {
-        mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
+        // Add a marker in Sydney and move the camera
+        LatLng sydney = new LatLng(-34, 151);
+        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
+        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
     }
 }
diff --git a/templates/activities/GoogleMapsWearActivity/globals.xml.ftl b/templates/activities/GoogleMapsWearActivity/globals.xml.ftl
new file mode 100644
index 0000000..58b383e
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/globals.xml.ftl
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="projectOut" value="." />
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="debugResOut" value="${projectOut}/src/debug/res" />
+    <global id="releaseResOut" value="${projectOut}/src/release/res" />
+    <global id="resOut" value="${resDir}" />
+    <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/activities/GoogleMapsWearActivity/recipe.xml.ftl b/templates/activities/GoogleMapsWearActivity/recipe.xml.ftl
new file mode 100644
index 0000000..b02fd4a
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/recipe.xml.ftl
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<recipe>
+
+    <dependency mavenUrl="com.google.android.gms:play-services-wearable:7.5.0" />
+    <dependency mavenUrl="com.google.android.gms:play-services-maps:7.5.0" />
+    <dependency mavenUrl="com.google.android.support:wearable:1.2.0" />
+
+
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+    <instantiate from="res/layout/activity_map.xml.ftl"
+                   to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+
+    <merge from="res/values/strings.xml.ftl"
+             to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
+
+    <instantiate from="res/layout/activity_map.xml.ftl"
+            to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+
+    <instantiate from="src/app_package/MapActivity.java.ftl"
+                   to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+
+    <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+
+    <merge from="debugRes/values/google_maps_api.xml.ftl"
+             to="${escapeXmlAttribute(debugResOut)}/values/google_maps_api.xml" />
+
+    <merge from="releaseRes/values/google_maps_api.xml.ftl"
+             to="${escapeXmlAttribute(releaseResOut)}/values/google_maps_api.xml" />
+
+    <!-- Display the API key instructions. -->
+    <open file="${escapeXmlAttribute(debugResOut)}/values/google_maps_api.xml" />
+</recipe>
diff --git a/templates/activities/GoogleMapsWearActivity/root/AndroidManifest.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..64e82fe
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/AndroidManifest.xml.ftl
@@ -0,0 +1,38 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <!--
+         The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
+         Google Maps Android API v2, but you must specify either coarse or fine
+         location permissions for the 'MyLocation' functionality.
+     -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+
+    <application>
+
+        <!--
+             The API key for Google Maps-based APIs is defined as a string resource.
+             (See the file "res/values/google_maps_api.xml").
+             Note that the API key is linked to the encryption key used to sign the APK.
+             You need a different API key for each encryption key, including the release key that is used to
+             sign the APK for publishing.
+             You can define the keys for the debug and release targets in src/debug/ and src/release/.
+         -->
+        <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"/>
+
+        <activity android:name="${relativePackage}.${activityClass}"
+            <#if isNewProject>
+            android:label="@string/app_name"
+            <#else>
+            android:label="@string/title_${activityToLayout(activityClass)}"
+            </#if>
+            >
+            <#if isLauncher>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            </#if>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/templates/activities/GoogleMapsWearActivity/root/debugRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/debugRes/values/google_maps_api.xml.ftl
new file mode 100644
index 0000000..59df0e3
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/debugRes/values/google_maps_api.xml.ftl
@@ -0,0 +1,20 @@
+<resources>
+    <!--
+    TODO: Before you run your application, you need a Google Maps API key.
+
+    To get one, follow this link, follow the directions and press "Create" at the end:
+
+    https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=${debugKeystoreSha1}%3B${packageName}
+
+    You can also add your credentials to an existing key, using this line:
+    ${debugKeystoreSha1};${packageName}
+
+    Alternatively, follow the directions here:
+    https://developers.google.com/maps/documentation/android/start#get-key
+
+    Once you have your key (it starts with "AIza"), replace the "google_maps_key"
+    string in this file.
+    -->
+    <#-- Always preserve the existing key. -->
+    <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">YOUR_KEY_HERE</string>
+</resources>
diff --git a/templates/activities/GoogleMapsWearActivity/root/releaseRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/releaseRes/values/google_maps_api.xml.ftl
new file mode 100644
index 0000000..f44f789
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/releaseRes/values/google_maps_api.xml.ftl
@@ -0,0 +1,21 @@
+<resources>
+    <!--
+    TODO: Before you release your application, you need a Google Maps API key.
+
+    To do this, you can either add your release key credentials to your existing
+    key, or create a new key.
+
+    Note that this file specifies the API key for the release build target.
+    If you have previously set up a key for the debug target with the debug signing certificate,
+    you will also need to set up a key for your release certificate.
+
+    Follow the directions here under 'release certificate':
+
+    https://developers.google.com/maps/documentation/android/signup
+
+    Once you have your key (it starts with "AIza"), replace the "google_maps_key"
+    string in this file.
+    -->
+    <#-- Always preserve the existing key. -->
+    <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">YOUR_KEY_HERE</string>
+</resources>
diff --git a/templates/activities/GoogleMapsWearActivity/root/res/layout/activity_map.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/res/layout/activity_map.xml.ftl
new file mode 100644
index 0000000..dbe2d7c
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/res/layout/activity_map.xml.ftl
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        xmlns:map="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/root_container"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        tools:context="${relativePackage}.${activityClass}">
+
+    <FrameLayout
+            android:id="@+id/map_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+        <fragment
+                android:id="@+id/map"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:name="com.google.android.gms.maps.MapFragment"/>
+
+    </FrameLayout>
+
+    <android.support.wearable.view.DismissOverlayView
+            android:id="@+id/dismiss_overlay"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"/>
+</FrameLayout>
diff --git a/templates/activities/GoogleMapsWearActivity/root/res/values/strings.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/res/values/strings.xml.ftl
new file mode 100644
index 0000000..68d93fb
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/res/values/strings.xml.ftl
@@ -0,0 +1,6 @@
+<resources>
+    <#if !isNewProject>
+    <string name="title_${activityToLayout(activityClass)}">${escapeXmlString(activityClass)}</string>
+    </#if>
+    <string name="intro_text">Long click to exit the map.</string>
+</resources>
diff --git a/templates/activities/GoogleMapsWearActivity/root/src/app_package/MapActivity.java.ftl b/templates/activities/GoogleMapsWearActivity/root/src/app_package/MapActivity.java.ftl
new file mode 100644
index 0000000..4247d59
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/src/app_package/MapActivity.java.ftl
@@ -0,0 +1,97 @@
+package ${packageName};
+
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.MapFragment;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.wearable.view.DismissOverlayView;
+import android.view.View;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+
+public class ${activityClass} extends Activity implements OnMapReadyCallback,
+        GoogleMap.OnMapLongClickListener {
+
+    /**
+     * Overlay that shows a short help text when first launched. It also provides an option to
+     * exit the app.
+     */
+    private DismissOverlayView mDismissOverlay;
+
+    /**
+     * The map. It is initialized when the map has been fully loaded and is ready to be used.
+     *
+     * @see #onMapReady(com.google.android.gms.maps.GoogleMap)
+     */
+    private GoogleMap mMap;
+
+    public void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        // Set the layout. It only contains a MapFragment and a DismissOverlay.
+        setContentView(R.layout.${layoutName});
+
+        // Retrieve the containers for the root of the layout and the map. Margins will need to be
+        // set on them to account for the system window insets.
+        final FrameLayout topFrameLayout = (FrameLayout) findViewById(R.id.root_container);
+        final FrameLayout mapFrameLayout = (FrameLayout) findViewById(R.id.map_container);
+
+        // Set the system view insets on the containers when they become available.
+        topFrameLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+            @Override
+            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+                // Call through to super implementation and apply insets
+                insets = topFrameLayout.onApplyWindowInsets(insets);
+
+                FrameLayout.LayoutParams params =
+                        (FrameLayout.LayoutParams) mapFrameLayout.getLayoutParams();
+
+                // Add Wearable insets to FrameLayout container holding map as margins
+                params.setMargins(
+                        insets.getSystemWindowInsetLeft(),
+                        insets.getSystemWindowInsetTop(),
+                        insets.getSystemWindowInsetRight(),
+                        insets.getSystemWindowInsetBottom());
+                mapFrameLayout.setLayoutParams(params);
+
+                return insets;
+            }
+        });
+
+        // Obtain the DismissOverlayView and display the introductory help text.
+        mDismissOverlay = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
+        mDismissOverlay.setIntroText(R.string.intro_text);
+        mDismissOverlay.showIntroIfNecessary();
+
+        // Obtain the MapFragment and set the async listener to be notified when the map is ready.
+        MapFragment mapFragment =
+                (MapFragment) getFragmentManager().findFragmentById(R.id.map);
+        mapFragment.getMapAsync(this);
+
+    }
+
+    @Override
+    public void onMapReady(GoogleMap googleMap) {
+        // Map is ready to be used.
+        mMap = googleMap;
+
+        // Set the long click listener as a way to exit the map.
+        mMap.setOnMapLongClickListener(this);
+
+        // Add a marker in Sydney, Australia and move the camera.
+        LatLng sydney = new LatLng(-34, 151);
+        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
+        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
+    }
+
+    @Override
+    public void onMapLongClick(LatLng latLng) {
+        // Display the dismiss overlay with a button to exit this activity.
+        mDismissOverlay.show();
+    }
+}
diff --git a/templates/activities/GoogleMapsWearActivity/template.xml b/templates/activities/GoogleMapsWearActivity/template.xml
new file mode 100644
index 0000000..0c30f2b
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/template.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<template
+    format="4"
+    revision="1"
+    name="Google Maps Wear Activity"
+    minApi="20"
+    minBuildApi="20"
+    description="Creates an new activity with a Google Map for Android Wear">
+
+    <category value="Google" />
+    <formfactor value="Wear" />
+
+    <parameter
+        id="activityClass"
+        name="Activity Name"
+        type="string"
+        constraints="class|unique|nonempty"
+        default="MapsActivity"
+        help="The name of the activity class to create" />
+
+    <parameter
+        id="layoutName"
+        name="Layout Name"
+        type="string"
+        constraints="layout|unique|nonempty"
+        suggest="${activityToLayout(activityClass)}"
+        default="activity_map"
+        help="The name of the layout to create for the activity" />
+
+    <parameter
+        id="isLauncher"
+        name="Launcher Activity"
+        type="boolean"
+        default="true"
+        help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it the default launchable activity" />
+
+    <parameter
+        id="packageName"
+        name="Package name"
+        type="string"
+        constraints="package"
+        default="com.mycompany.myapp" />
+
+    <!-- 128x128 thumbnails relative to template.xml -->
+    <thumbs>
+        <!-- default thumbnail is required -->
+        <thumb>template_thumb.png</thumb>
+    </thumbs>
+
+    <globals file="globals.xml.ftl" />
+    <execute file="recipe.xml.ftl" />
+
+</template>
diff --git a/templates/activities/GoogleMapsWearActivity/template_thumb.png b/templates/activities/GoogleMapsWearActivity/template_thumb.png
new file mode 100644
index 0000000..4153368
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/template_thumb.png
Binary files differ
diff --git a/templates/activities/LoginActivity/globals.xml.ftl b/templates/activities/LoginActivity/globals.xml.ftl
index f60259b..c77e9ac 100644
--- a/templates/activities/LoginActivity/globals.xml.ftl
+++ b/templates/activities/LoginActivity/globals.xml.ftl
@@ -1,5 +1,14 @@
 <?xml version="1.0"?>
 <globals>
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <global id="manifestOut" value="${manifestDir}" />
     <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
     <global id="resOut" value="${resDir}" />
diff --git a/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl b/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
index 0bcfb4b..0571d4b 100644
--- a/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
+++ b/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
@@ -32,7 +32,9 @@
 </#if>
 import java.util.ArrayList;
 import java.util.List;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * A login screen that offers login via email/password<#if includeGooglePlus> and via Google+ sign in</#if>.
diff --git a/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl b/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl
index 45ea9a9..943d562 100644
--- a/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl
+++ b/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl
@@ -1,22 +1,23 @@
 package ${packageName};
 
+import ${superClassFqcn};
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
-<#if minApiLevel lt 14>import android.support.v7.app.ActionBarActivity;</#if>
-<#if minApiLevel gte 14>import android.app.Activity;</#if>
 import android.util.Log;
 
 import com.google.android.gms.common.ConnectionResult;
 import com.google.android.gms.common.GooglePlayServicesClient;
 import com.google.android.gms.common.Scopes;
 import com.google.android.gms.plus.PlusClient;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * A base class to wrap communication with the Google Play Services PlusClient.
  */
-public abstract class PlusBaseActivity extends <#if minApiLevel lt 14>ActionBar</#if>Activity
+public abstract class PlusBaseActivity extends ${superClass}
         implements GooglePlayServicesClient.ConnectionCallbacks,
         GooglePlayServicesClient.OnConnectionFailedListener {
 
diff --git a/templates/activities/MasterDetailFlow/globals.xml.ftl b/templates/activities/MasterDetailFlow/globals.xml.ftl
index 9031f64..3ab716f 100644
--- a/templates/activities/MasterDetailFlow/globals.xml.ftl
+++ b/templates/activities/MasterDetailFlow/globals.xml.ftl
@@ -1,6 +1,14 @@
 <?xml version="1.0"?>
 <globals>
-    <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <global id="Support" value="${(hasDependency('com.android.support:appcompat-v7'))?string('Support','')}" />
     <global id="manifestOut" value="${manifestDir}" />
     <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
index 6dc3409..7eca5ce 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
@@ -1,11 +1,13 @@
 package ${packageName};
 
+import ${superClassFqcn};
 import android.content.Intent;
 import android.os.Bundle;
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
 <#if minApiLevel lt 16>import android.support.v4.app.NavUtils;</#if>
 import android.view.MenuItem;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * An activity representing a single ${objectKind} detail screen. This
@@ -16,7 +18,7 @@
  * This activity is mostly just a 'shell' activity containing nothing
  * more than a {@link ${DetailName}Fragment}.
  */
-public class ${DetailName}Activity extends ${appCompat?string('ActionBar','')}Activity {
+public class ${DetailName}Activity extends ${superClass} {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl
index 27331cd..fd4dbf9 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl
@@ -6,8 +6,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 import ${packageName}.dummy.DummyContent;
 
 /**
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
index 8e8ac58..477d718 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
@@ -5,7 +5,9 @@
 import <#if appCompat>android.support.v4.app.FragmentActivity<#else>android.app.Activity</#if>;
 <#if (parentActivityClass != "" && minApiLevel lt 16)>import android.support.v4.app.NavUtils;</#if>
 <#if parentActivityClass != "">import android.view.MenuItem;</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * An activity representing a list of ${objectKindPlural}. This activity
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl
index 9e34f3e..93fd1ac 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl
@@ -6,8 +6,9 @@
 import android.view.View;
 import android.widget.ArrayAdapter;
 import android.widget.ListView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 import ${packageName}.dummy.DummyContent;
 
 /**
diff --git a/templates/activities/NavigationDrawerActivity/globals.xml.ftl b/templates/activities/NavigationDrawerActivity/globals.xml.ftl
index 8dd7ff2..f9e6aa8 100644
--- a/templates/activities/NavigationDrawerActivity/globals.xml.ftl
+++ b/templates/activities/NavigationDrawerActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
 <?xml version="1.0"?>
 <globals>
     <global id="manifestOut" value="${manifestDir}" />
-    <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <!-- e.g. getSupportActionBar vs. getActionBar -->
     <global id="Support" value="${(hasDependency('com.android.support:appcompat-v7'))?string('Support','')}" />
     <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
diff --git a/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl b/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl
index 261d090..0ee2754 100644
--- a/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl
+++ b/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl
@@ -1,7 +1,7 @@
 package ${packageName};
 
 import android.app.Activity;
-<#if appCompat>import android.support.v7.app.ActionBarActivity;</#if>
+<#if appCompat>import ${superClassFqcn};</#if>
 import android.<#if appCompat>support.v7.</#if>app.ActionBar;
 import android.<#if appCompat>support.v4.</#if>app.Fragment;
 import android.<#if appCompat>support.v4.</#if>app.FragmentManager;
@@ -17,9 +17,11 @@
 import android.support.v4.widget.DrawerLayout;
 import android.widget.ArrayAdapter;
 import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity
+public class ${activityClass} extends ${superClass}
         implements NavigationDrawerFragment.NavigationDrawerCallbacks {
 
     /**
diff --git a/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl b/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
index cb0f2c5..8485499 100644
--- a/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
+++ b/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
@@ -1,6 +1,6 @@
 package ${packageName};
 
-<#if appCompat>import android.support.v7.app.ActionBarActivity;</#if>
+<#if appCompat>import ${superClassFqcn};</#if>
 import android.app.Activity;
 import android.<#if appCompat>support.v7.</#if>app.ActionBar;
 import android.<#if appCompat>support.v4.</#if>app.Fragment;
@@ -267,7 +267,7 @@
     }
 
     private ActionBar getActionBar() {
-        return <#if appCompat>((ActionBarActivity) getActivity()).getSupportActionBar();<#else>getActivity().getActionBar();</#if>
+        return <#if appCompat>((${superClass}) getActivity()).getSupportActionBar();<#else>getActivity().getActionBar();</#if>
     }
 
     /**
diff --git a/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl b/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
index e562f92..8e8ced6 100644
--- a/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
+++ b/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
@@ -20,8 +20,9 @@
 import android.view.MenuItem;
 import android.support.v4.app.NavUtils;
 </#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 import java.util.List;
 
 <#assign includeSimple=(minApiLevel?? && minApiLevel lt 10)>
diff --git a/templates/activities/TabbedActivity/globals.xml.ftl b/templates/activities/TabbedActivity/globals.xml.ftl
index bb93c2c..ca4effb 100644
--- a/templates/activities/TabbedActivity/globals.xml.ftl
+++ b/templates/activities/TabbedActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
 <?xml version="1.0"?>
 <globals>
     <global id="manifestOut" value="${manifestDir}" />
-    <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+    <global id="appCompat" type="boolean" value="true" />
+    <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+    <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+    <global id="appCompat" type="boolean" value="false" />
+    <global id="superClass" type="string" value="Activity"/>
+    <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
     <!-- e.g. getSupportActionBar vs. getActionBar -->
     <global id="Support" value="${(hasDependency('com.android.support:appcompat-v7'))?string('Support','')}" />
     <global id="hasViewPager" type="boolean" value="${(features == 'pager' || features == 'tabs')?string}" />
diff --git a/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl b/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl
index 8f579a4..050457c 100644
--- a/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl
+++ b/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl
@@ -1,6 +1,6 @@
 package ${packageName};
 
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
 import android.<#if appCompat>support.v7.</#if>app.ActionBar;
 import android.<#if appCompat>support.v4.</#if>app.Fragment;
 import android.content.Context;
@@ -14,9 +14,11 @@
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity implements ActionBar.OnNavigationListener {
+public class ${activityClass} extends ${superClass} implements ActionBar.OnNavigationListener {
 
     /**
      * The serialization (saved instance state) Bundle key representing the
diff --git a/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl b/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl
index dfa4786..a135605 100644
--- a/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl
+++ b/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl
@@ -2,7 +2,7 @@
 
 import java.util.Locale;
 
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
 import android.<#if appCompat>support.v7.</#if>app.ActionBar;
 import android.<#if appCompat>support.v4.</#if>app.Fragment;
 import android.<#if appCompat>support.v4.</#if>app.FragmentManager;
@@ -17,9 +17,11 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity<#if features == 'tabs'> implements ActionBar.TabListener</#if> {
+public class ${activityClass} extends ${superClass}<#if features == 'tabs'> implements ActionBar.TabListener</#if> {
 
     /**
      * The {@link android.support.v4.view.PagerAdapter} that will provide
diff --git a/templates/gradle-projects/NewAndroidAutoProject/globals.xml.ftl b/templates/gradle-projects/NewAndroidAutoProject/globals.xml.ftl
new file mode 100644
index 0000000..da1c39a
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/globals.xml.ftl
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="topOut" value="." />
+    <global id="mavenUrl" value="mavenCentral" />
+</globals>
diff --git a/templates/gradle-projects/NewAndroidAutoProject/recipe.xml.ftl b/templates/gradle-projects/NewAndroidAutoProject/recipe.xml.ftl
new file mode 100644
index 0000000..316065f
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/recipe.xml.ftl
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<recipe>
+    <instantiate from="build.gradle.ftl"
+                   to="${escapeXmlAttribute(topOut)}/build.gradle" />
+
+<#if makeIgnore>
+    <copy from="project_ignore"
+            to="${escapeXmlAttribute(topOut)}/.gitignore" />
+</#if>
+
+    <instantiate from="settings.gradle.ftl"
+                   to="${escapeXmlAttribute(topOut)}/settings.gradle" />
+
+    <instantiate from="gradle.properties.ftl"
+                   to="${escapeXmlAttribute(topOut)}/gradle.properties" />
+
+    <copy from="${templateRoot}/gradle/wrapper"
+        to="${escapeXmlAttribute(topOut)}/" />
+
+<#if sdkDir??>
+  <instantiate from="local.properties.ftl"
+           to="${escapeXmlAttribute(topOut)}/local.properties" />
+</#if>
+</recipe>
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/build.gradle.ftl
new file mode 100644
index 0000000..5ac9a20
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/build.gradle.ftl
@@ -0,0 +1,29 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+<#if mavenUrl != "mavenCentral">
+        maven {
+            url '${mavenUrl}'
+        }
+</#if>
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:${gradlePluginVersion}'
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+<#if mavenUrl != "mavenCentral">
+        maven {
+            url '${mavenUrl}'
+        }
+</#if>
+    }
+}
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/gradle.properties.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/gradle.properties.ftl
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/gradle.properties.ftl
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/local.properties.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/local.properties.ftl
new file mode 100644
index 0000000..9dcbf9b
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/local.properties.ftl
@@ -0,0 +1,10 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=${escapePropertyValue(sdkDir)}
\ No newline at end of file
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/project_ignore b/templates/gradle-projects/NewAndroidAutoProject/root/project_ignore
new file mode 100644
index 0000000..9c4de58
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/project_ignore
@@ -0,0 +1,7 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/settings.gradle.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/settings.gradle.ftl
new file mode 100644
index 0000000..b12004b
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/settings.gradle.ftl
@@ -0,0 +1 @@
+include ':${projectName}'
diff --git a/templates/gradle-projects/NewAndroidAutoProject/template.xml b/templates/gradle-projects/NewAndroidAutoProject/template.xml
new file mode 100644
index 0000000..1bf59ca
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/template.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<template
+    format="4"
+    revision="1"
+    name="Android Auto Project"
+    description="Creates a new Android Auto project.">
+
+    <category value="Application" />
+    <formfactor value="Car" />
+
+    <thumbs>
+        <thumb>template_new_project.png</thumb>
+    </thumbs>
+
+    <parameter
+        id="makeIgnore"
+        name="Create .gitignore file"
+        type="boolean"
+        default="true" />
+
+    <globals file="globals.xml.ftl" />
+    <execute file="recipe.xml.ftl" />
+
+</template>
diff --git a/templates/gradle-projects/NewAndroidAutoProject/template_new_project.png b/templates/gradle-projects/NewAndroidAutoProject/template_new_project.png
new file mode 100644
index 0000000..92e8556
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/template_new_project.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidModule/globals.xml.ftl b/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
index ba085ec..32a9164 100644
--- a/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
+++ b/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
@@ -12,4 +12,6 @@
     <global id="mavenUrl" value="mavenCentral" />
     <global id="buildToolsVersion" value="18.0.1" />
     <global id="gradlePluginVersion" value="0.6.+" />
+    <global id="junitVersion" value="4.12" />
+    <global id="unitTestsSupported" type="boolean" value="${(compareVersions(gradlePluginVersion, '1.1.0') >= 0)?string}" />
 </globals>
diff --git a/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl b/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
index 6ce95fd..d960f16 100644
--- a/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
+++ b/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
@@ -50,6 +50,9 @@
     <instantiate from="test/app_package/ApplicationTest.java.ftl"
                    to="${testOut}/ApplicationTest.java" />
 
+<#if unitTestsSupported>
     <instantiate from="test/app_package/ExampleUnitTest.java.ftl"
                    to="${unitTestOut}/ExampleUnitTest.java" />
+</#if>
+
 </recipe>
diff --git a/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
index 07a96fe..e59f426 100644
--- a/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
+++ b/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
@@ -72,5 +72,7 @@
     wearApp project(':${WearprojectName}')
     compile 'com.google.android.gms:play-services:+'
 </#if>
-    testCompile 'junit:junit:4.12'
+<#if unitTestsSupported>
+    testCompile 'junit:junit:${junitVersion}'
+</#if>
 }
diff --git a/templates/gradle-projects/NewAndroidModule/root/res/mipmap-anydpi/test.svg b/templates/gradle-projects/NewAndroidModule/root/res/mipmap-anydpi/test.svg
new file mode 100644
index 0000000..b89a0d0
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidModule/root/res/mipmap-anydpi/test.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></svg>
\ No newline at end of file
diff --git a/templates/other/AndroidAutoMediaService/globals.xml.ftl b/templates/other/AndroidAutoMediaService/globals.xml.ftl
new file mode 100644
index 0000000..4bf836f
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/globals.xml.ftl
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/other/AndroidAutoMediaService/recipe.xml.ftl b/templates/other/AndroidAutoMediaService/recipe.xml.ftl
new file mode 100644
index 0000000..061d7ba
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/recipe.xml.ftl
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<recipe>
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+    <#if useCustomTheme>
+      <merge from="res/values-v21/styles.xml.ftl"
+             to="${escapeXmlAttribute(resOut)}/values-v21/styles.xml" />
+    </#if>
+
+    <copy from="res/xml/automotive_app_desc.xml"
+          to="${escapeXmlAttribute(resOut)}/xml/automotive_app_desc.xml" />
+
+    <instantiate from="src/app_package/MusicService.java.ftl"
+                   to="${escapeXmlAttribute(srcOut)}/${mediaBrowserServiceName}.java" />
+
+    <open file="${escapeXmlAttribute(srcOut)}/${mediaBrowserServiceName}.java" />
+
+    <#if useCustomTheme>
+      <open file="${escapeXmlAttribute(resOut)}/values-v21/styles.xml" />
+    </#if>
+</recipe>
diff --git a/templates/other/AndroidAutoMediaService/root/AndroidManifest.xml.ftl b/templates/other/AndroidAutoMediaService/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..c26e543
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/AndroidManifest.xml.ftl
@@ -0,0 +1,34 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <application>
+
+        <meta-data
+            android:name="com.google.android.gms.car.application"
+            android:resource="@xml/automotive_app_desc" />
+
+        <#if useCustomTheme>
+        <!--
+             Use this meta data to override the theme from which Android Auto will
+             look for colors. If you don't set this, Android Auto will look
+             for color attributes in your application theme.
+        -->
+        <meta-data
+            android:name="com.google.android.gms.car.application.theme"
+            android:resource="@style/${escapeXmlAttribute(customThemeName)}" />
+
+        </#if>
+
+        <!-- Main music service, provides media browsing and media playback services to
+         consumers through MediaBrowserService and MediaSession. Consumers connect to it through
+         MediaBrowser (for browsing) and MediaController (for playback control) -->
+        <service
+            android:name="${relativePackage}.${mediaBrowserServiceName}"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.media.browse.MediaBrowserService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/templates/other/AndroidAutoMediaService/root/res/values-v21/styles.xml.ftl b/templates/other/AndroidAutoMediaService/root/res/values-v21/styles.xml.ftl
new file mode 100644
index 0000000..6ba61a3
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/res/values-v21/styles.xml.ftl
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <style name="${customThemeName}" parent="android:Theme.Material.Light">
+        <!-- colorPrimaryDark is currently used in Android Auto for:
+             - App background
+             - Drawer right side ("more" custom actions) background
+             - Notification icon badge tinting
+             - Overview “now playing” icon tinting
+         -->
+        <item name="android:colorPrimaryDark">#ffbf360c</item>
+
+        <!-- colorAccent is used in Android Auto for:
+             - Spinner
+             - progress bar
+             - floating action button background (Play/Pause in media apps)
+         -->
+        <item name="android:colorAccent">#00B8D4</item>
+    </style>
+</resources>
diff --git a/templates/other/AndroidAutoMediaService/root/res/xml/automotive_app_desc.xml b/templates/other/AndroidAutoMediaService/root/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..a3d9fda
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/res/xml/automotive_app_desc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<automotiveApp>
+    <uses name="media"/>
+</automotiveApp>
diff --git a/templates/other/AndroidAutoMediaService/root/src/app_package/MusicService.java.ftl b/templates/other/AndroidAutoMediaService/root/src/app_package/MusicService.java.ftl
new file mode 100644
index 0000000..2d79289
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/src/app_package/MusicService.java.ftl
@@ -0,0 +1,138 @@
+package ${packageName};
+
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
+ * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
+ * exposes it through its MediaSession.Token, which allows the client to create a MediaController
+ * that connects to and send control commands to the MediaSession remotely. This is useful for
+ * user interfaces that need to interact with your media session, like Android Auto. You can
+ * (should) also use the same service from your app's UI, which gives a seamless playback
+ * experience to the user.
+ *
+ * To implement a MediaBrowserService, you need to:
+ *
+ * <ul>
+ *
+ * <li> Extend {@link android.service.media.MediaBrowserService}, implementing the media browsing
+ *      related methods {@link android.service.media.MediaBrowserService#onGetRoot} and
+ *      {@link android.service.media.MediaBrowserService#onLoadChildren};
+ * <li> In onCreate, start a new {@link android.media.session.MediaSession} and notify its parent
+ *      with the session's token {@link android.service.media.MediaBrowserService#setSessionToken};
+ *
+ * <li> Set a callback on the
+ *      {@link android.media.session.MediaSession#setCallback(android.media.session.MediaSession.Callback)}.
+ *      The callback will receive all the user's actions, like play, pause, etc;
+ *
+ * <li> Handle all the actual music playing using any method your app prefers (for example,
+ *      {@link android.media.MediaPlayer})
+ *
+ * <li> Update playbackState, "now playing" metadata and queue, using MediaSession proper methods
+ *      {@link android.media.session.MediaSession#setPlaybackState(android.media.session.PlaybackState)}
+ *      {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata)} and
+ *      {@link android.media.session.MediaSession#setQueue(java.util.List)})
+ *
+ * <li> Declare and export the service in AndroidManifest with an intent receiver for the action
+ *      android.media.browse.MediaBrowserService
+ *
+ * </ul>
+ *
+ * To make your app compatible with Android Auto, you also need to:
+ *
+ * <ul>
+ *
+ * <li> Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+ *      with a &lt;automotiveApp&gt; root element. For a media app, this must include
+ *      an &lt;uses name="media"/&gt; element as a child.
+ *      For example, in AndroidManifest.xml:
+ *          &lt;meta-data android:name="com.google.android.gms.car.application"
+ *              android:resource="@xml/automotive_app_desc"/&gt;
+ *      And in res/values/automotive_app_desc.xml:
+ *          &lt;automotiveApp&gt;
+ *              &lt;uses name="media"/&gt;
+ *          &lt;/automotiveApp&gt;
+ *
+ * </ul>
+
+ * @see <a href="README.md">README.md</a> for more details.
+ *
+ */
+public class ${mediaBrowserServiceName} extends MediaBrowserService {
+
+    private MediaSession mSession;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mSession = new MediaSession(this, "${mediaBrowserServiceName}");
+        setSessionToken(mSession.getSessionToken());
+        mSession.setCallback(new MediaSessionCallback());
+        mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
+                MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+    }
+
+    @Override
+    public void onDestroy() {
+        mSession.release();
+    }
+
+    @Override
+    public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+        return new BrowserRoot("root", null);
+    }
+
+    @Override
+    public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+        result.sendResult(new ArrayList<MediaItem>());
+    }
+
+    private final class MediaSessionCallback extends MediaSession.Callback {
+        @Override
+        public void onPlay() {
+        }
+
+        @Override
+        public void onSkipToQueueItem(long queueId) {
+        }
+
+        @Override
+        public void onSeekTo(long position) {
+        }
+
+        @Override
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+        }
+
+        @Override
+        public void onPause() {
+        }
+
+        @Override
+        public void onStop() {
+        }
+
+        @Override
+        public void onSkipToNext() {
+        }
+
+        @Override
+        public void onSkipToPrevious() {
+        }
+
+        @Override
+        public void onCustomAction(String action, Bundle extras) {
+        }
+
+        @Override
+        public void onPlayFromSearch(final String query, final Bundle extras) {
+        }
+    }
+}
diff --git a/templates/other/AndroidAutoMediaService/template.xml b/templates/other/AndroidAutoMediaService/template.xml
new file mode 100644
index 0000000..08d6fa3
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/template.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<template
+    format="4"
+    revision="1"
+    name="Media service"
+    description="Create a MediaBrowserService and adds the required metadata for Android Auto"
+    minApi="21">
+
+    <category value="Android Auto" />
+    <formfactor value="Car" />
+
+    <parameter
+        id="mediaBrowserServiceName"
+        name="Class name"
+        type="string"
+        constraints="class|unique|nonempty"
+        default="MyMusicService"
+        help="The name of the service that will extend MediaBrowserService and contain the logic to browse and playback media" />
+
+    <parameter
+        id="packageName"
+        name="Package name"
+        type="string"
+        constraints="package"
+        help="The package where the media service will be created" />
+
+    <parameter
+        id="useCustomTheme"
+        name="Use a custom theme for Android Auto colors?"
+        type="boolean"
+        constraints="package"
+        help="Android Auto apps can define a different set of colors that will be used exclusively when running on Android Auto" />
+
+    <parameter
+        id="customThemeName"
+        name="Android Auto custom theme name"
+        type="String"
+        default="CarTheme"
+        visibility="useCustomTheme"
+        constraints="nonempty"/>
+
+    <globals file="globals.xml.ftl" />
+    <execute file="recipe.xml.ftl" />
+
+    <thumbs>
+        <thumb>templates-mediaService-Auto.png</thumb>
+    </thumbs>
+
+</template>
diff --git a/templates/other/AndroidAutoMediaService/templates-mediaService-Auto.png b/templates/other/AndroidAutoMediaService/templates-mediaService-Auto.png
new file mode 100644
index 0000000..48c4ef5
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/templates-mediaService-Auto.png
Binary files differ
diff --git a/templates/other/AndroidAutoMessagingService/globals.xml.ftl b/templates/other/AndroidAutoMessagingService/globals.xml.ftl
new file mode 100644
index 0000000..4bf836f
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/globals.xml.ftl
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<globals>
+    <global id="manifestOut" value="${manifestDir}" />
+    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+    <global id="resOut" value="${resDir}" />
+    <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/other/AndroidAutoMessagingService/recipe.xml.ftl b/templates/other/AndroidAutoMessagingService/recipe.xml.ftl
new file mode 100644
index 0000000..dddb1e5
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/recipe.xml.ftl
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<recipe>
+    <dependency mavenUrl="com.android.support:support-v4:21.0.2"/>
+    <dependency mavenUrl="com.android.support:support-v13:21.0.2"/>
+    <merge from="AndroidManifest.xml.ftl"
+             to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+    <copy from="res/xml/automotive_app_desc.xml"
+          to="${escapeXmlAttribute(resOut)}/xml/automotive_app_desc.xml" />
+
+    <instantiate from="src/app_package/MessagingService.java.ftl"
+                   to="${escapeXmlAttribute(srcOut)}/${serviceName}.java" />
+
+    <instantiate from="src/app_package/MessageReadReceiver.java.ftl"
+                   to="${escapeXmlAttribute(srcOut)}/${readReceiverName}.java" />
+
+    <instantiate from="src/app_package/MessageReplyReceiver.java.ftl"
+                   to="${escapeXmlAttribute(srcOut)}/${replyReceiverName}.java" />
+
+    <open file="${escapeXmlAttribute(srcOut)}/${serviceName}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${readReceiverName}.java" />
+    <open file="${escapeXmlAttribute(srcOut)}/${replyReceiverName}.java" />
+</recipe>
diff --git a/templates/other/AndroidAutoMessagingService/root/AndroidManifest.xml.ftl b/templates/other/AndroidAutoMessagingService/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..e6c133d
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/AndroidManifest.xml.ftl
@@ -0,0 +1,26 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <application>
+
+        <meta-data
+            android:name="com.google.android.gms.car.application"
+            android:resource="@xml/automotive_app_desc" />
+
+        <service android:name="${relativePackage}.${serviceName}">
+        </service>
+
+        <receiver android:name="${relativePackage}.${readReceiverName}">
+            <intent-filter>
+                <action android:name="${packageName}.ACTION_MESSAGE_READ"/>
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="${relativePackage}.${replyReceiverName}">
+            <intent-filter>
+                <action android:name="${packageName}.ACTION_MESSAGE_REPLY"/>
+            </intent-filter>
+        </receiver>
+
+    </application>
+
+</manifest>
diff --git a/templates/other/AndroidAutoMessagingService/root/res/xml/automotive_app_desc.xml b/templates/other/AndroidAutoMessagingService/root/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..6d31de8
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/res/xml/automotive_app_desc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<automotiveApp>
+    <uses name="notification"/>
+</automotiveApp>
diff --git a/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReadReceiver.java.ftl b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReadReceiver.java.ftl
new file mode 100644
index 0000000..a175baa
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReadReceiver.java.ftl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 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 ${packageName};
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+public class ${readReceiverName} extends BroadcastReceiver {
+    private static final String TAG = ${readReceiverName}.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (${serviceName}.READ_ACTION.equals(intent.getAction())) {
+          int conversationId = intent.getIntExtra(${serviceName}.CONVERSATION_ID, -1);
+          if (conversationId != -1) {
+            Log.d(TAG, "Conversation " + conversationId + " was read");
+            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+            notificationManager.cancel(conversationId);
+          }
+        }
+    }
+}
diff --git a/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReplyReceiver.java.ftl b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReplyReceiver.java.ftl
new file mode 100644
index 0000000..021e4e4
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReplyReceiver.java.ftl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 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 ${packageName};
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.RemoteInput;
+import android.util.Log;
+
+/**
+ * A receiver that gets called when a reply is sent to a given conversationId
+ */
+public class ${replyReceiverName} extends BroadcastReceiver {
+
+    private static final String TAG = ${replyReceiverName}.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (${serviceName}.REPLY_ACTION.equals(intent.getAction())) {
+            int conversationId = intent.getIntExtra(${serviceName}.CONVERSATION_ID, -1);
+            CharSequence reply = getMessageText(intent);
+            Log.d(TAG, "Got reply (" + reply + ") for ConversationId " + conversationId);
+        }
+    }
+
+    /**
+     * Get the message text from the intent.
+     * Note that you should call {@code RemoteInput#getResultsFromIntent(intent)} to process
+     * the RemoteInput.
+     */
+    private CharSequence getMessageText(Intent intent) {
+        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+        if (remoteInput != null) {
+            return remoteInput.getCharSequence(${serviceName}.EXTRA_VOICE_REPLY);
+        }
+        return null;
+    }
+}
diff --git a/templates/other/AndroidAutoMessagingService/root/src/app_package/MessagingService.java.ftl b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessagingService.java.ftl
new file mode 100644
index 0000000..bcdebed
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessagingService.java.ftl
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 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 ${packageName};
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationCompat.CarExtender;
+import android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.RemoteInput;
+
+public class ${serviceName} extends Service {
+    private static final String TAG = ${serviceName}.class.getSimpleName();
+
+    public static final String READ_ACTION =
+            "${packageName}.ACTION_MESSAGE_READ";
+    public static final String REPLY_ACTION =
+            "${packageName}.ACTION_MESSAGE_REPLY";
+    public static final String CONVERSATION_ID = "conversation_id";
+    public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
+
+    private NotificationManagerCompat mNotificationManager;
+
+    private final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * Handler of incoming messages from clients.
+     */
+    class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            sendNotification(1, "This is a sample message", "John Doe",
+                    System.currentTimeMillis());
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        mNotificationManager = NotificationManagerCompat.from(getApplicationContext());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return START_STICKY;
+    }
+
+    private Intent createIntent(int conversationId, String action) {
+        return new Intent()
+                .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+                .setAction(action)
+                .putExtra(CONVERSATION_ID, conversationId);
+    }
+
+    private void sendNotification(int conversationId, String message,
+                                  String participant, long timestamp) {
+        // A pending Intent for reads
+        PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
+                conversationId,
+                createIntent(conversationId, READ_ACTION),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Build a RemoteInput for receiving voice input in a Car Notification
+        RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
+                .setLabel("Reply by voice")
+                .build();
+
+        // Building a Pending Intent for the reply action to trigger
+        PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(),
+                conversationId,
+                createIntent(conversationId, REPLY_ACTION),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Create the UnreadConversation and populate it with the participant name,
+        // read and reply intents.
+        UnreadConversation.Builder unreadConvBuilder =
+                new UnreadConversation.Builder(participant)
+                .setLatestTimestamp(timestamp)
+                .setReadPendingIntent(readPendingIntent)
+                .setReplyAction(replyIntent, remoteInput);
+
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
+                // Set the application notification icon:
+                //.setSmallIcon(R.drawable.notification_icon)
+
+                // Set the large icon, for example a picture of the other recipient of the message
+                //.setLargeIcon(personBitmap)
+
+                .setContentText(message)
+                .setWhen(timestamp)
+                .setContentTitle(participant)
+                .setContentIntent(readPendingIntent)
+                .extend(new CarExtender()
+                        .setUnreadConversation(unreadConvBuilder.build()));
+
+        mNotificationManager.notify(conversationId, builder.build());
+    }
+}
diff --git a/templates/other/AndroidAutoMessagingService/template.xml b/templates/other/AndroidAutoMessagingService/template.xml
new file mode 100644
index 0000000..3412a8e
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/template.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<template
+    format="4"
+    revision="1"
+    name="Messaging service"
+    description="Create a service that sends notifications compatible with Android Auto"
+    minApi="21">
+
+    <category value="Android Auto" />
+    <formfactor value="Car" />
+
+    <parameter
+        id="serviceName"
+        name="Service class name"
+        type="string"
+        constraints="class|unique|nonempty"
+        default="MyMessagingService"
+        help="The name of the service that will handle incoming messages and send corresponding notifications." />
+
+    <parameter
+        id="readReceiverName"
+        name="Read receiver class name"
+        type="string"
+        constraints="class|unique|nonempty"
+        default="MessageReadReceiver"
+        help="The broadcast receiver that will handle Read notifications." />
+
+    <parameter
+        id="replyReceiverName"
+        name="Reply receiver class name"
+        type="string"
+        constraints="class|unique|nonempty"
+        default="MessageReplyReceiver"
+        help="The broadcast receiver that will handle Reply notifications." />
+
+    <parameter
+        id="packageName"
+        name="Package name"
+        type="string"
+        constraints="package"
+        help="The package where the messaging service will be created." />
+
+    <globals file="globals.xml.ftl" />
+    <execute file="recipe.xml.ftl" />
+
+    <thumbs>
+        <thumb>templates-messagingService-Auto.png</thumb>
+    </thumbs>
+
+</template>
diff --git a/templates/other/AndroidAutoMessagingService/templates-messagingService-Auto.png b/templates/other/AndroidAutoMessagingService/templates-messagingService-Auto.png
new file mode 100644
index 0000000..02f611a
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/templates-messagingService-Auto.png
Binary files differ
diff --git a/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl b/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl
index 60a1e08..d273f19 100644
--- a/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl
+++ b/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl
@@ -4,7 +4,9 @@
 import android.appwidget.AppWidgetProvider;
 import android.content.Context;
 import android.widget.RemoteViews;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * Implementation of App Widget functionality.
diff --git a/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl b/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl
index eed95b5..c51f367 100644
--- a/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl
+++ b/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl
@@ -8,7 +8,9 @@
 import android.os.Bundle;
 import android.view.View;
 import android.widget.EditText;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * The configuration screen for the {@link ${className} ${className}} AppWidget.
diff --git a/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl b/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl
index 6e33197..322f37f 100644
--- a/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl
+++ b/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl
@@ -8,7 +8,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 <#if !includeLayout>import android.widget.TextView;</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * A simple {@link Fragment} subclass.
diff --git a/templates/other/CustomView/root/src/app_package/CustomView.java.ftl b/templates/other/CustomView/root/src/app_package/CustomView.java.ftl
index a989a30..fe5c9ec 100644
--- a/templates/other/CustomView/root/src/app_package/CustomView.java.ftl
+++ b/templates/other/CustomView/root/src/app_package/CustomView.java.ftl
@@ -9,7 +9,9 @@
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.view.View;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * TODO: document your custom view class.
diff --git a/templates/other/Daydream/root/src/app_package/DreamService.java.ftl b/templates/other/Daydream/root/src/app_package/DreamService.java.ftl
index 0dc51ca..81738bd 100644
--- a/templates/other/Daydream/root/src/app_package/DreamService.java.ftl
+++ b/templates/other/Daydream/root/src/app_package/DreamService.java.ftl
@@ -15,7 +15,9 @@
 import android.view.ViewPropertyAnimator;
 import android.view.animation.LinearInterpolator;
 import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * This class is a sample implementation of a DreamService. When activated, a
diff --git a/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl b/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl
index cd1b4c8..a7bc420 100644
--- a/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl
+++ b/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl
@@ -5,7 +5,9 @@
 import android.os.Bundle;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceFragment;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * A settings Activity for {@link ${className}}.
diff --git a/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl b/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl
index d5a4615..18e750e 100644
--- a/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl
+++ b/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl
@@ -7,7 +7,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.widget.Toast;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 public class ${receiverClass} extends BroadcastReceiver {
     public static final String CONTENT_KEY = "contentText";
@@ -20,7 +22,7 @@
         Intent displayIntent = new Intent(context, ${displayActivityClass}.class);
         String text = intent.getStringExtra(CONTENT_KEY);
         Notification notification = new Notification.Builder(context)
-                .setSmallIcon(R.drawable.ic_launcher)
+                .setSmallIcon(R.mipmap.ic_launcher)
                 .setContentTitle(text)
                 .extend(new Notification.WearableExtender()
                         .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
@@ -30,4 +32,4 @@
 
         Toast.makeText(context, context.getString(R.string.notification_posted), Toast.LENGTH_SHORT).show();
     }
-}
\ No newline at end of file
+}
diff --git a/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl b/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl
index dad7cdd..13ac120 100644
--- a/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl
+++ b/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl
@@ -3,7 +3,9 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 public class ${displayActivityClass} extends Activity {
 
@@ -15,4 +17,4 @@
         setContentView(R.layout.${displayActivityLayout});
         mTextView = (TextView) findViewById(R.id.text);
     }
-}
\ No newline at end of file
+}
diff --git a/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl b/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl
index 9a1f5b5..7367ce2 100644
--- a/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl
+++ b/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl
@@ -3,7 +3,9 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * Example shell activity which simply broadcasts to our receiver and exits.
diff --git a/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl b/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl
index 9c11374..01379d2 100644
--- a/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl
+++ b/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl
@@ -21,8 +21,9 @@
 <#else>
 import android.widget.ListView;
 </#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 import ${packageName}.dummy.DummyContent;
 
 /**
diff --git a/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl b/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl
index 6c39bfe..28074f1 100644
--- a/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl
+++ b/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl
@@ -17,7 +17,9 @@
 import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 </#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 
 /**
  * Helper class for showing and canceling ${display_title?lower_case}
@@ -203,4 +205,4 @@
             nm.cancel(NOTIFICATION_TAG.hashCode());
         }
     }
-}
\ No newline at end of file
+}
diff --git a/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl b/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl
index 659bc45..8351bcb 100644
--- a/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl
+++ b/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl
@@ -7,8 +7,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
 import com.google.android.gms.plus.PlusOneButton;
 
 /**
diff --git a/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl b/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl
index 32d4e8d..81395cb 100644
--- a/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl
+++ b/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl
@@ -32,6 +32,7 @@
 import android.text.format.Time;
 import android.view.SurfaceHolder;
 
+import java.lang.ref.WeakReference;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
@@ -46,36 +47,23 @@
      */
     private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
 
+    /**
+     * Handler message id for updating the time periodically in interactive mode.
+     */
+    private static final int MSG_UPDATE_TIME = 0;
+
     @Override
     public Engine onCreateEngine() {
         return new Engine();
     }
 
     private class Engine extends CanvasWatchFaceService.Engine {
-        static final int MSG_UPDATE_TIME = 0;
-
         Paint mBackgroundPaint;
         Paint mHandPaint;
         boolean mAmbient;
         Time mTime;
 
-        /** Handler to update the time once a second in interactive mode. */
-        final Handler mUpdateTimeHandler = new Handler() {
-            @Override
-            public void handleMessage(Message message) {
-                switch (message.what) {
-                    case MSG_UPDATE_TIME:
-                        invalidate();
-                        if (shouldTimerBeRunning()) {
-                            long timeMs = System.currentTimeMillis();
-                            long delayMs = INTERACTIVE_UPDATE_RATE_MS
-                                    - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
-                            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
-                        }
-                        break;
-                }
-            }
-        };
+        final Handler mUpdateTimeHandler = new EngineHandler(this);
 
         final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
             @Override
@@ -244,5 +232,38 @@
         private boolean shouldTimerBeRunning() {
             return isVisible() && !isInAmbientMode();
         }
+
+        /**
+         * Handle updating the time periodically in interactive mode.
+         */
+        private void handleUpdateTimeMessage() {
+            invalidate();
+            if (shouldTimerBeRunning()) {
+                long timeMs = System.currentTimeMillis();
+                long delayMs = INTERACTIVE_UPDATE_RATE_MS
+                        - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
+                mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+            }
+        }
+    }
+
+    private static class EngineHandler extends Handler {
+        private final WeakReference<${serviceClass}.Engine> mWeakReference;
+
+        public EngineHandler(${serviceClass}.Engine reference) {
+            mWeakReference = new WeakReference<>(reference);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            ${serviceClass}.Engine engine = mWeakReference.get();
+            if (engine != null) {
+                switch (msg.what) {
+                    case MSG_UPDATE_TIME:
+                        engine.handleUpdateTimeMessage();
+                        break;
+                }
+            }
+        }
     }
 }
diff --git a/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl b/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl
index 4835d37..ac00af2 100644
--- a/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl
+++ b/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl
@@ -34,6 +34,7 @@
 import android.view.SurfaceHolder;
 import android.view.WindowInsets;
 
+import java.lang.ref.WeakReference;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 
@@ -51,33 +52,18 @@
      */
     private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
 
+    /**
+     * Handler message id for updating the time periodically in interactive mode.
+     */
+    private static final int MSG_UPDATE_TIME = 0;
+
     @Override
     public Engine onCreateEngine() {
         return new Engine();
     }
 
     private class Engine extends CanvasWatchFaceService.Engine {
-        static final int MSG_UPDATE_TIME = 0;
-
-        /**
-         * Handler to update the time periodically in interactive mode.
-         */
-        final Handler mUpdateTimeHandler = new Handler() {
-            @Override
-            public void handleMessage(Message message) {
-                switch (message.what) {
-                    case MSG_UPDATE_TIME:
-                        invalidate();
-                        if (shouldTimerBeRunning()) {
-                            long timeMs = System.currentTimeMillis();
-                            long delayMs = INTERACTIVE_UPDATE_RATE_MS
-                                    - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
-                            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
-                        }
-                        break;
-                }
-            }
-        };
+        final Handler mUpdateTimeHandler = new EngineHandler(this);
 
         final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
             @Override
@@ -250,5 +236,38 @@
         private boolean shouldTimerBeRunning() {
             return isVisible() && !isInAmbientMode();
         }
+
+        /**
+         * Handle updating the time periodically in interactive mode.
+         */
+        private void handleUpdateTimeMessage() {
+            invalidate();
+            if (shouldTimerBeRunning()) {
+                long timeMs = System.currentTimeMillis();
+                long delayMs = INTERACTIVE_UPDATE_RATE_MS
+                        - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
+                mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+            }
+        }
+    }
+
+    private static class EngineHandler extends Handler {
+        private final WeakReference<${serviceClass}.Engine> mWeakReference;
+
+        public EngineHandler(${serviceClass}.Engine reference) {
+            mWeakReference = new WeakReference<>(reference);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            ${serviceClass}.Engine engine = mWeakReference.get();
+            if (engine != null) {
+                switch (msg.what) {
+                    case MSG_UPDATE_TIME:
+                        engine.handleUpdateTimeMessage();
+                        break;
+                }
+            }
+        }
     }
 }
diff --git a/testutils/build.gradle b/testutils/build.gradle
index 67ec14d..b69a4fb 100644
--- a/testutils/build.gradle
+++ b/testutils/build.gradle
@@ -1,4 +1,5 @@
 apply plugin: 'java'
+apply plugin: 'jacoco'
 
 group = 'com.android.tools'
 archivesBaseName = 'testutils'
@@ -14,3 +15,10 @@
     main.resources.srcDir 'src/main/java'
     test.resources.srcDir 'src/test/java'
 }
+
+project.ext.pomName = 'Android Tools Test Utilities'
+project.ext.pomDesc = 'API used by lint testing infrastructure'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+apply from: "$rootDir/buildSrc/base/javadoc.gradle"
diff --git a/testutils/src/main/java/com/android/testutils/SdkTestCase.java b/testutils/src/main/java/com/android/testutils/SdkTestCase.java
index f85873f..75877a5 100644
--- a/testutils/src/main/java/com/android/testutils/SdkTestCase.java
+++ b/testutils/src/main/java/com/android/testutils/SdkTestCase.java
@@ -17,7 +17,6 @@
 
 import com.android.SdkConstants;
 import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
 import com.google.common.base.Charsets;
 import com.google.common.collect.Sets;
 import com.google.common.io.ByteStreams;
@@ -184,7 +183,7 @@
             // cannot happen
         }
 
-        assertTrue(xml.length() > 0);
+        assertTrue(!xml.isEmpty());
 
         // Remove any references to the project name such that we are isolated from
         // that in golden file.