DO NOT MERGE - Merge Android 10 into master

Bug: 139893257
Change-Id: I6cf6c323b06b1b2f50112d30b162d1530ef816cf
diff --git a/.gitignore b/.gitignore
index a2b0c33..d886ce1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 bin
-/.idea/workspace.xml
 /out
 /bridge/out
-/.idea/kotlinc.xml
\ No newline at end of file
+/.idea/kotlinc.xml
+/.idea/shelf
+/.idea/workspace.xml
diff --git a/.idea/runConfigurations/All_in_bridge.xml b/.idea/runConfigurations/All_in_bridge.xml
index 0b22717..07e39cd 100644
--- a/.idea/runConfigurations/All_in_bridge.xml
+++ b/.idea/runConfigurations/All_in_bridge.xml
@@ -1,25 +1,18 @@
 <component name="ProjectRunConfigurationManager">
   <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true">
-    <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
     <module name="bridge" />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-    <option name="ALTERNATIVE_JRE_PATH" value="" />
+    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
+    <option name="ALTERNATIVE_JRE_PATH" value="1.8.0_60-b23" />
     <option name="PACKAGE_NAME" value="" />
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
     <option name="TEST_OBJECT" value="package" />
-    <option name="VM_PARAMETERS" value="-ea" />
     <option name="PARAMETERS" value="" />
-    <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
-    <option name="ENV_VARIABLES" />
-    <option name="PASS_PARENT_ENVS" value="true" />
-    <option name="TEST_SEARCH_SCOPE">
-      <value defaultName="singleModule" />
-    </option>
-    <envs />
-    <patterns />
+    <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
     <RunnerSettings RunnerId="Run" />
     <ConfigurationWrapper RunnerId="Run" />
-    <method />
+    <method v="2">
+      <option name="Make" enabled="true" />
+    </method>
   </configuration>
 </component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/Create.xml b/.idea/runConfigurations/Create.xml
index 0e7a6c4..a3fd3a1 100644
--- a/.idea/runConfigurations/Create.xml
+++ b/.idea/runConfigurations/Create.xml
@@ -4,7 +4,7 @@
     <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
     <option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" />
     <module name="create" />
-    <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes-jarjar.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes-jarjar.jar" />
+    <option name="PROGRAM_PARAMETERS" value="--create-stub out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes.jar" />
     <option name="VM_PARAMETERS" value="-ea" />
     <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../.." />
     <RunnerSettings RunnerId="Debug">
diff --git a/Android.bp b/Android.bp
index 99b156e..565a709 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,5 +32,5 @@
         ":icu4j-icudata-jarjar{.jar}", // HOST
         ":icu4j-icutzdata-jarjar{.jar}", // HOST
     ],
-    cmd: "rm -f $(out) && $(location layoutlib_create) $(out) $(in)",
+    cmd: "rm -f $(out) && $(location layoutlib_create) --create-stub $(out) $(in)",
 }
diff --git a/bridge/.classpath b/bridge/.classpath
deleted file mode 100644
index 9c4160c..0000000
--- a/bridge/.classpath
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry excluding="org/kxml2/io/" kind="src" path="src"/>
-	<classpathentry kind="src" path="tests/src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/layoutlib_api/layoutlib_api-prebuilt.jar" sourcepath="/ANDROID_SRC/tools/base/layoutlib-api/src/main"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/tools-common/tools-common-prebuilt.jar"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/icu4j/icu4j.jar"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/sdk-common/sdk-common.jar"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/guavalib_intermediates/javalib.jar"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/bridge/.project b/bridge/.project
deleted file mode 100644
index e36e71b..0000000
--- a/bridge/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>layoutlib_bridge</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/bridge/Android.bp b/bridge/Android.bp
index 2d75058..f0958dd 100644
--- a/bridge/Android.bp
+++ b/bridge/Android.bp
@@ -18,11 +18,13 @@
     name: "layoutlib",
 
     srcs: ["src/**/*.java"],
+    java_version: "1.8",
     java_resource_dirs: ["resources"],
 
     libs: [
         "layoutlib_api-prebuilt",
         "tools-common-prebuilt",
+        "guava",
     ],
 
     static_libs: [
@@ -35,3 +37,28 @@
         targets: ["layoutlib"],
     },
 }
+
+java_library_host {
+    name: "layoutlib-no-framework",
+
+    srcs: ["src/**/*.java"],
+    java_version: "1.8",
+    java_resource_dirs: ["resources"],
+
+    libs: [
+        "temp_layoutlib",
+        "guava",
+    ],
+
+    static_libs: [
+        "layoutlib_create",
+        "layoutlib_api-prebuilt",
+        "tools-common-prebuilt",
+        "ninepatch-prebuilt",
+        "layoutlib-common",
+    ],
+
+    dist: {
+        targets: ["layoutlib"],
+    },
+}
diff --git a/bridge/bridge.iml b/bridge/bridge.iml
index 90de760..8a65f99 100644
--- a/bridge/bridge.iml
+++ b/bridge/bridge.iml
@@ -63,14 +63,14 @@
         </SOURCES>
       </library>
     </orderEntry>
-    <orderEntry type="module-library" scope="TEST">
+    <orderEntry type="module-library">
       <library name="guava">
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES>
-          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0-sources.jar!/" />
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0-sources.jar!/" />
         </SOURCES>
       </library>
     </orderEntry>
diff --git a/bridge/resources/bars/status_bar.xml b/bridge/resources/bars/status_bar.xml
index 04571e1..920be50 100644
--- a/bridge/resources/bars/status_bar.xml
+++ b/bridge/resources/bars/status_bar.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <TextView
+    <!-- Spacer -->
+    <View
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"/>
diff --git a/bridge/resources/bars/v28/hdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/hdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..aa9f6d4
--- /dev/null
+++ b/bridge/resources/bars/v28/hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/hdpi/ic_sysbar_back_quick_step.png b/bridge/resources/bars/v28/hdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..eea819a
--- /dev/null
+++ b/bridge/resources/bars/v28/hdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/hdpi/ic_sysbar_home.png b/bridge/resources/bars/v28/hdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..613afce
--- /dev/null
+++ b/bridge/resources/bars/v28/hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/bridge/resources/bars/v28/hdpi/ic_sysbar_home_quick_step.png b/bridge/resources/bars/v28/hdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..8e7d8cb
--- /dev/null
+++ b/bridge/resources/bars/v28/hdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/hdpi/ic_sysbar_recent.png b/bridge/resources/bars/v28/hdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..eb80426
--- /dev/null
+++ b/bridge/resources/bars/v28/hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/bridge/resources/bars/v28/ldrtl-hdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/ldrtl-hdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..2fcfdde
--- /dev/null
+++ b/bridge/resources/bars/v28/ldrtl-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/ldrtl-mdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/ldrtl-mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..48708a5
--- /dev/null
+++ b/bridge/resources/bars/v28/ldrtl-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/ldrtl-xhdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/ldrtl-xhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..3d73184
--- /dev/null
+++ b/bridge/resources/bars/v28/ldrtl-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/ldrtl-xxhdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/ldrtl-xxhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..786935d
--- /dev/null
+++ b/bridge/resources/bars/v28/ldrtl-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/mdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..34a11df
--- /dev/null
+++ b/bridge/resources/bars/v28/mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/mdpi/ic_sysbar_back_quick_step.png b/bridge/resources/bars/v28/mdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..d4e5a94
--- /dev/null
+++ b/bridge/resources/bars/v28/mdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/mdpi/ic_sysbar_home.png b/bridge/resources/bars/v28/mdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..7c25fc5
--- /dev/null
+++ b/bridge/resources/bars/v28/mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/bridge/resources/bars/v28/mdpi/ic_sysbar_home_quick_step.png b/bridge/resources/bars/v28/mdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..0757799
--- /dev/null
+++ b/bridge/resources/bars/v28/mdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/mdpi/ic_sysbar_recent.png b/bridge/resources/bars/v28/mdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..1ee9cf5
--- /dev/null
+++ b/bridge/resources/bars/v28/mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/bridge/resources/bars/v28/status_bar.xml b/bridge/resources/bars/v28/status_bar.xml
new file mode 100644
index 0000000..2c768f4
--- /dev/null
+++ b/bridge/resources/bars/v28/status_bar.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="5dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginRight="5dp"
+        android:gravity="center_vertical"
+        android:textSize="16dp"
+        android:fontFamily="sans-serif-medium"/>
+    <!-- Spacer -->
+    <View
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"/>
+    <!-- The exact size of the wifi icon is specified in order to scale it properly.
+    Without scaling, it appeared huge. This is currently, 70% of the actual size. -->
+    <ImageView
+            android:layout_height="22.4dp"
+            android:layout_width="20.65dp"
+            android:layout_marginTop="1dp"/>
+    <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginLeft="3dp"
+            android:layout_marginRight="8dp"
+            android:layout_marginTop="4dp"/>
+</merge>
diff --git a/bridge/resources/bars/v28/xhdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/xhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..987aac5
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xhdpi/ic_sysbar_back_quick_step.png b/bridge/resources/bars/v28/xhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..407ef28
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xhdpi/ic_sysbar_home.png b/bridge/resources/bars/v28/xhdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..0e2a14d
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xhdpi/ic_sysbar_home_quick_step.png b/bridge/resources/bars/v28/xhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..a7fd3a6
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xhdpi/ic_sysbar_recent.png b/bridge/resources/bars/v28/xhdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..f810704
--- /dev/null
+++ b/bridge/resources/bars/v28/xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xxhdpi/ic_sysbar_back.png b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..be03cbe
--- /dev/null
+++ b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xxhdpi/ic_sysbar_back_quick_step.png b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_back_quick_step.png
new file mode 100644
index 0000000..a1f44dc
--- /dev/null
+++ b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_back_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xxhdpi/ic_sysbar_home.png b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..f16aa48
--- /dev/null
+++ b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xxhdpi/ic_sysbar_home_quick_step.png b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_home_quick_step.png
new file mode 100644
index 0000000..0fb93ca
--- /dev/null
+++ b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_home_quick_step.png
Binary files differ
diff --git a/bridge/resources/bars/v28/xxhdpi/ic_sysbar_recent.png b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..109aeed
--- /dev/null
+++ b/bridge/resources/bars/v28/xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/bridge/src/android/animation/PropertyValuesHolder_Accessor.java b/bridge/src/android/animation/PropertyValuesHolder_Accessor.java
new file mode 100644
index 0000000..576d0ea
--- /dev/null
+++ b/bridge/src/android/animation/PropertyValuesHolder_Accessor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.animation.PropertyValuesHolder.FloatPropertyValuesHolder;
+import android.animation.PropertyValuesHolder.IntPropertyValuesHolder;
+import android.animation.PropertyValuesHolder.MultiFloatValuesHolder;
+import android.animation.PropertyValuesHolder.MultiIntValuesHolder;
+
+public class PropertyValuesHolder_Accessor {
+    /**
+     * Method used by layoutlib to ensure that {@link Class} instances are not cached by this
+     * class. Caching the {@link Class} instances will lead to leaking the {@link ClassLoader}
+     * used by that class.
+     * In layoutlib, these class loaders are instantiated dynamically to allow for new classes to
+     * be loaded when the user code changes.
+     */
+    public static void clearClassCaches() {
+        PropertyValuesHolder.sGetterPropertyMap.clear();
+        PropertyValuesHolder.sSetterPropertyMap.clear();
+        IntPropertyValuesHolder.sJNISetterPropertyMap.clear();
+        MultiIntValuesHolder.sJNISetterPropertyMap.clear();
+        FloatPropertyValuesHolder.sJNISetterPropertyMap.clear();
+        MultiFloatValuesHolder.sJNISetterPropertyMap.clear();
+    }
+}
diff --git a/bridge/src/android/content/res/AssetManager_Delegate.java b/bridge/src/android/content/res/AssetManager_Delegate.java
index 3b47ea7..6b71752 100644
--- a/bridge/src/android/content/res/AssetManager_Delegate.java
+++ b/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.util.SparseArray;
@@ -32,6 +33,28 @@
  */
 public class AssetManager_Delegate {
 
+    // ---- delegate manager ----
+
+    private static final DelegateManager<AssetManager_Delegate> sManager =
+            new DelegateManager<>(AssetManager_Delegate.class);
+
+    public static DelegateManager<AssetManager_Delegate> getDelegateManager() {
+        return sManager;
+    }
+
+    // ---- delegate methods. ----
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeCreate() {
+        AssetManager_Delegate delegate = new AssetManager_Delegate();
+        return sManager.addNewDelegate(delegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeDestroy(long ptr) {
+        sManager.removeJavaReferenceFor(ptr);
+    }
+
     @LayoutlibDelegate
     public static InputStream open(AssetManager mgr, String fileName) throws IOException {
         return mgr.open_Original(fileName);
@@ -47,13 +70,13 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long newTheme(AssetManager manager) {
+    /*package*/ static long nativeThemeCreate(long ptr) {
         return Resources_Theme_Delegate.getDelegateManager()
                 .addNewDelegate(new Resources_Theme_Delegate());
     }
 
     @LayoutlibDelegate
-    /*package*/ static void deleteTheme(AssetManager manager, long theme) {
+    /*package*/ static void nativeThemeDestroy(long theme) {
         Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme);
     }
 
@@ -61,4 +84,10 @@
     /*package*/ static SparseArray<String> getAssignedPackageIdentifiers(AssetManager manager) {
         return new SparseArray<>();
     }
+
+    @LayoutlibDelegate
+    /*package*/ static String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid() {
+        // AssetManager requires this not to be null
+        return new String[0];
+    }
 }
diff --git a/bridge/src/android/content/res/BridgeAssetManager.java b/bridge/src/android/content/res/BridgeAssetManager.java
index a1a4a19..b67d3b6 100644
--- a/bridge/src/android/content/res/BridgeAssetManager.java
+++ b/bridge/src/android/content/res/BridgeAssetManager.java
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.content.res;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.layoutlib.bridge.Bridge;
 
 public class BridgeAssetManager extends AssetManager {
-
-    private AssetRepository mAssetRepository;
+    @Nullable private AssetRepository mAssetRepository;
 
     /**
      * This initializes the static field {@link AssetManager#sSystem} which is used
@@ -48,11 +48,22 @@
         AssetManager.sSystem = null;
     }
 
-    public void setAssetRepository(AssetRepository assetRepository) {
+    public void setAssetRepository(@NonNull AssetRepository assetRepository) {
         mAssetRepository = assetRepository;
     }
 
+    /**
+     * Clears the AssetRepository reference.
+     */
+    public void releaseAssetRepository() {
+        mAssetRepository = null;
+    }
+
+    @NonNull
     public AssetRepository getAssetRepository() {
+        if (mAssetRepository == null) {
+            throw new IllegalStateException("Asset repository is not set");
+        }
         return mAssetRepository;
     }
 
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index 9505993..bbb6405 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -20,13 +20,18 @@
 import com.android.ide.common.rendering.api.AttrResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.ResourceType;
+import com.android.resources.ResourceUrl;
 
 import android.annotation.Nullable;
 import android.content.res.Resources.Theme;
@@ -68,49 +73,46 @@
 
     private final Resources mBridgeResources;
     private final BridgeContext mContext;
-    private final boolean mPlatformFile;
 
     private final int[] mResourceId;
     private final ResourceValue[] mResourceData;
     private final String[] mNames;
-    private final boolean[] mIsFramework;
+    private final ResourceNamespace[] mNamespaces;
 
     // Contains ids that are @empty. We still store null in mResourceData for that index, since we
     // want to save on the check against empty, each time a resource value is requested.
     @Nullable
     private int[] mEmptyIds;
 
-    public BridgeTypedArray(Resources resources, BridgeContext context, int len,
-            boolean platformFile) {
+    public BridgeTypedArray(Resources resources, BridgeContext context, int len) {
         super(resources);
         mBridgeResources = resources;
         mContext = context;
-        mPlatformFile = platformFile;
         mResourceId = new int[len];
         mResourceData = new ResourceValue[len];
         mNames = new String[len];
-        mIsFramework = new boolean[len];
+        mNamespaces = new ResourceNamespace[len];
     }
 
     /**
      * A bridge-specific method that sets a value in the type array
      * @param index the index of the value in the TypedArray
      * @param name the name of the attribute
-     * @param isFramework whether the attribute is in the android namespace.
+     * @param namespace namespace of the attribute
      * @param resourceId the reference id of this resource
      * @param value the value of the attribute
      */
-    public void bridgeSetValue(int index, String name, boolean isFramework, int resourceId,
+    public void bridgeSetValue(int index, String name, ResourceNamespace namespace, int resourceId,
             ResourceValue value) {
         mResourceId[index] = resourceId;
         mResourceData[index] = value;
         mNames[index] = name;
-        mIsFramework[index] = isFramework;
+        mNamespaces[index] = namespace;
     }
 
     /**
      * Seals the array after all calls to
-     * {@link #bridgeSetValue(int, String, boolean, int, ResourceValue)} have been done.
+     * {@link #bridgeSetValue(int, String, ResourceNamespace, int, ResourceValue)} have been done.
      * <p/>This allows to compute the list of non default values, permitting
      * {@link #getIndexCount()} to return the proper value.
      */
@@ -128,7 +130,7 @@
                 } else if (REFERENCE_EMPTY.equals(dataValue)) {
                     mResourceData[i] = null;
                     if (emptyIds == null) {
-                        emptyIds = new ArrayList<Integer>(4);
+                        emptyIds = new ArrayList<>(4);
                     }
                     emptyIds.add(i);
                 } else {
@@ -568,21 +570,11 @@
             return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
         }
 
-        // if the attribute was a reference to a resource, and not a declaration of an id (@+id),
+        // If the attribute was a reference to a resource, and not a declaration of an id (@+id),
         // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
-        // valid getType() and getName() returning a resource name.
-        // (and getValue() returning null!). We need to handle this!
-        if (resValue.getResourceType() != null) {
-            // if this is a framework id
-            if (mPlatformFile || resValue.isFramework()) {
-                // look for idName in the android R classes
-                return mContext.getFrameworkResourceValue(
-                        resValue.getResourceType(), resValue.getName(), defValue);
-            }
-
-            // look for idName in the project R class.
-            return mContext.getProjectResourceValue(
-                    resValue.getResourceType(), resValue.getName(), defValue);
+        // valid type, name, namespace and a potentially null value.
+        if (!(resValue instanceof UnresolvedResourceValue)) {
+            return mContext.getResourceId(resValue.asReference(), defValue);
         }
 
         // else, try to get the value, and resolve it somehow.
@@ -592,15 +584,11 @@
         }
         value = value.trim();
 
-        // if the value is just an integer, return it.
-        try {
-            int i = Integer.parseInt(value);
-            if (Integer.toString(i).equals(value)) {
-                return i;
-            }
-        } catch (NumberFormatException e) {
-            // pass
-        }
+
+        // `resValue` failed to be resolved. We extract the interesting bits and get rid of this
+        // broken object. The namespace and resolver come from where the XML attribute was defined.
+        ResourceNamespace contextNamespace = resValue.getNamespace();
+        Resolver namespaceResolver = resValue.getNamespaceResolver();
 
         if (value.startsWith("#")) {
             // this looks like a color, do not try to parse it
@@ -621,37 +609,32 @@
         // classes exclusively.
 
         // if this is a reference to an id, find it.
-        if (value.startsWith("@id/") || value.startsWith("@+") ||
-                value.startsWith("@android:id/")) {
+        ResourceUrl resourceUrl = ResourceUrl.parse(value);
+        if (resourceUrl != null) {
+            if (resourceUrl.type == ResourceType.ID) {
+                ResourceReference referencedId =
+                        resourceUrl.resolve(contextNamespace, namespaceResolver);
 
-            int pos = value.indexOf('/');
-            String idName = value.substring(pos + 1);
-            boolean create = value.startsWith("@+");
-            boolean isFrameworkId =
-                    mPlatformFile || value.startsWith("@android") || value.startsWith("@+android");
-
-            // Look for the idName in project or android R class depending on isPlatform.
-            if (create) {
-                Integer idValue;
-                if (isFrameworkId) {
-                    idValue = Bridge.getResourceId(ResourceType.ID, idName);
-                } else {
-                    idValue = mContext.getLayoutlibCallback().getResourceId(ResourceType.ID, idName);
+                // Look for the idName in project or android R class depending on isPlatform.
+                if (resourceUrl.isCreate()) {
+                    int idValue;
+                    if (referencedId.getNamespace() == ResourceNamespace.ANDROID) {
+                        idValue = Bridge.getResourceId(ResourceType.ID, resourceUrl.name);
+                    } else {
+                        idValue = mContext.getLayoutlibCallback().getOrGenerateResourceId(referencedId);
+                    }
+                    return idValue;
                 }
-                return idValue == null ? defValue : idValue;
+                // This calls the same method as in if(create), but doesn't create a dynamic id, if
+                // one is not found.
+                return mContext.getResourceId(referencedId, defValue);
             }
-            // This calls the same method as in if(create), but doesn't create a dynamic id, if
-            // one is not found.
-            if (isFrameworkId) {
-                return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
-            } else {
-                return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+            else if (resourceUrl.type == ResourceType.AAPT) {
+                ResourceReference referencedId =
+                        resourceUrl.resolve(contextNamespace, namespaceResolver);
+                return mContext.getLayoutlibCallback().getOrGenerateResourceId(referencedId);
             }
         }
-        else if (value.startsWith("@aapt:_aapt")) {
-            return mContext.getLayoutlibCallback().getResourceId(ResourceType.AAPT, value);
-        }
-
         // not a direct id valid reference. First check if it's an enum (this is a corner case
         // for attributes that have a reference|enum type), then fallback to resolve
         // as an ID without prefix.
@@ -660,33 +643,6 @@
             return enumValue;
         }
 
-        // Ok, not an enum, resolve as an ID
-        Integer idValue;
-
-        if (resValue.isFramework()) {
-            idValue = Bridge.getResourceId(resValue.getResourceType(),
-                    resValue.getName());
-        } else {
-            idValue = mContext.getLayoutlibCallback().getResourceId(
-                    resValue.getResourceType(), resValue.getName());
-        }
-
-        if (idValue != null) {
-            return idValue;
-        }
-
-        if ("text".equals(mNames[index])) {
-            // In a TextView, if the text is set from the attribute android:text, the correct
-            // behaviour is not to find a resourceId for the text, and to return the default value.
-            // So in this case, do not log a warning.
-            return defValue;
-        }
-
-        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
-                String.format(
-                    "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]),
-                    resValue);
-
         return defValue;
     }
 
@@ -707,6 +663,7 @@
      * @return Drawable for the attribute, or null if not defined.
      */
     @Override
+    @Nullable
     public Drawable getDrawable(int index) {
         if (!hasValue(index)) {
             return null;
@@ -721,6 +678,7 @@
      * @hide
      */
     @Override
+    @Nullable
     public Drawable getDrawableForDensity(int index, int density) {
         return getDrawable(index);
     }
@@ -760,7 +718,8 @@
         if (resVal instanceof ArrayResourceValue) {
             ArrayResourceValue array = (ArrayResourceValue) resVal;
             int count = array.getElementCount();
-            return count >= 0 ? Resources_Delegate.fillValues(mBridgeResources, array, new CharSequence[count]) :
+            return count >= 0 ?
+                    Resources_Delegate.resolveValues(mBridgeResources, array) :
                     null;
         }
         int id = getResourceId(index, 0);
@@ -808,6 +767,16 @@
             case TYPE_REFERENCE:
                 outValue.resourceId = mResourceId[index];
                 return true;
+            case TYPE_INT_COLOR_ARGB4:
+            case TYPE_INT_COLOR_ARGB8:
+            case TYPE_INT_COLOR_RGB4:
+            case TYPE_INT_COLOR_RGB8:
+                ColorStateList colorStateList = getColorStateList(index);
+                if (colorStateList == null) {
+                    return false;
+                }
+                outValue.data = colorStateList.getDefaultColor();
+                return true;
             default:
                 // For back-compatibility, parse as float.
                 String s = getString(index);
@@ -944,35 +913,49 @@
     private Integer resolveEnumAttribute(int index) {
         // Get the map of attribute-constant -> IntegerValue
         Map<String, Integer> map = null;
-        if (mIsFramework[index]) {
+        if (mNamespaces[index] == ResourceNamespace.ANDROID) {
             map = Bridge.getEnumValues(mNames[index]);
         } else {
             // get the styleable matching the resolved name
             RenderResources res = mContext.getRenderResources();
-            ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]);
+            ResourceValue attr = res.getResolvedResource(
+                    ResourceReference.attr(mNamespaces[index], mNames[index]));
             if (attr instanceof AttrResourceValue) {
                 map = ((AttrResourceValue) attr).getAttributeValues();
             }
         }
 
-        if (map != null) {
-            // accumulator to store the value of the 1+ constants.
+        if (map != null && !map.isEmpty()) {
+            // Accumulator to store the value of the 1+ constants.
             int result = 0;
             boolean found = false;
 
-            // split the value in case this is a mix of several flags.
-            String[] keywords = mResourceData[index].getValue().split("\\|");
-            for (String keyword : keywords) {
-                Integer i = map.get(keyword.trim());
-                if (i != null) {
-                    result |= i;
-                    found = true;
+            String value = mResourceData[index].getValue();
+            if (!value.isEmpty()) {
+                // Check if the value string is already representing an integer and return it if so.
+                // Resources coming from res.apk in an AAR may have flags and enums in integer form.
+                char c = value.charAt(0);
+                if (Character.isDigit(c) || c == '-' || c == '+') {
+                    try {
+                        return convertValueToInt(value, 0);
+                    } catch (NumberFormatException e) {
+                        // Ignore and continue.
+                    }
                 }
-                // TODO: We should act smartly and log a warning for incorrect keywords. However,
-                // this method is currently called even if the resourceValue is not an enum.
-            }
-            if (found) {
-                return result;
+                // Split the value in case it is a mix of several flags.
+                String[] keywords = value.split("\\|");
+                for (String keyword : keywords) {
+                    Integer i = map.get(keyword.trim());
+                    if (i != null) {
+                        result |= i;
+                        found = true;
+                    }
+                    // TODO: We should act smartly and log a warning for incorrect keywords. However,
+                    // this method is currently called even if the resourceValue is not an enum.
+                }
+                if (found) {
+                    return result;
+                }
             }
         }
 
@@ -1026,6 +1009,6 @@
     }
 
     static TypedArray obtain(Resources res, int len) {
-        return new BridgeTypedArray(res, null, len, true);
+        return new BridgeTypedArray(res, null, len);
     }
 }
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index 77ae90f..f127992 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -18,16 +18,22 @@
 
 import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.ArrayResourceValue;
+import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.DensityBasedResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.PluralsResourceValue;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.ResourceValueImpl;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.layoutlib.bridge.util.NinePatchInputStream;
@@ -56,25 +62,21 @@
 import android.view.DisplayAdjustments;
 import android.view.ViewGroup.LayoutParams;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
-import java.util.Iterator;
 import java.util.Objects;
 import java.util.WeakHashMap;
 
+import static android.content.res.AssetManager.ACCESS_STREAMING;
 import static com.android.SdkConstants.ANDROID_PKG;
 import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
 
 @SuppressWarnings("deprecation")
 public class Resources_Delegate {
-    private static WeakHashMap<Resources, LayoutlibCallback> sLayoutlibCallbacks = new
-            WeakHashMap<>();
-    private static WeakHashMap<Resources, BridgeContext> sContexts = new
-            WeakHashMap<>();
+    private static WeakHashMap<Resources, LayoutlibCallback> sLayoutlibCallbacks =
+            new WeakHashMap<>();
+    private static WeakHashMap<Resources, BridgeContext> sContexts = new WeakHashMap<>();
 
-    private static boolean[] mPlatformResourceFlag = new boolean[1];
     // TODO: This cache is cleared every time a render session is disposed. Look into making this
     // more long lived.
     private static LruCache<String, Drawable.ConstantState> sDrawableCache = new LruCache<>(50);
@@ -122,15 +124,13 @@
         Resources.mSystem = null;
     }
 
-    public static BridgeTypedArray newTypeArray(Resources resources, int numEntries,
-            boolean platformFile) {
-        return new BridgeTypedArray(resources, getContext(resources), numEntries, platformFile);
+    public static BridgeTypedArray newTypeArray(Resources resources, int numEntries) {
+        return new BridgeTypedArray(resources, getContext(resources), numEntries);
     }
 
-    private static Pair<ResourceType, String> getResourceInfo(Resources resources, int id,
-            boolean[] platformResFlag_out) {
+    private static ResourceReference getResourceInfo(Resources resources, int id) {
         // first get the String related to this id in the framework
-        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+        ResourceReference resourceInfo = Bridge.resolveResourceId(id);
 
         assert Resources.mSystem != null : "Resources_Delegate.initSystem wasn't called";
         // Set the layoutlib callback and context for resources
@@ -140,37 +140,25 @@
             sContexts.put(resources, getContext(Resources.mSystem));
         }
 
-        if (resourceInfo != null) {
-            platformResFlag_out[0] = true;
-            return resourceInfo;
+        if (resourceInfo == null) {
+            // Didn't find a match in the framework? Look in the project.
+            resourceInfo = getLayoutlibCallback(resources).resolveResourceId(id);
         }
 
-        // didn't find a match in the framework? look in the project.
-        resourceInfo = getLayoutlibCallback(resources).resolveResourceId(id);
-
-        if (resourceInfo != null) {
-            platformResFlag_out[0] = false;
-            return resourceInfo;
-        }
-        return null;
+        return resourceInfo;
     }
 
-    private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
-            boolean[] platformResFlag_out) {
-        Pair<ResourceType, String> resourceInfo =
-                getResourceInfo(resources, id, platformResFlag_out);
+    private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id) {
+        ResourceReference resourceInfo = getResourceInfo(resources, id);
 
         if (resourceInfo != null) {
-            String attributeName = resourceInfo.getSecond();
+            String attributeName = resourceInfo.getName();
             RenderResources renderResources = getContext(resources).getRenderResources();
-            ResourceValue value = platformResFlag_out[0] ?
-                    renderResources.getFrameworkResource(resourceInfo.getFirst(), attributeName) :
-                    renderResources.getProjectResource(resourceInfo.getFirst(), attributeName);
-
+            ResourceValue value = renderResources.getResolvedResource(resourceInfo);
             if (value == null) {
-                // Unable to resolve the attribute, just leave the unresolved value
-                value = new ResourceValue(resourceInfo.getFirst(), attributeName, attributeName,
-                        platformResFlag_out[0]);
+                // Unable to resolve the attribute, just leave the unresolved value.
+                value = new ResourceValueImpl(resourceInfo.getNamespace(),
+                        resourceInfo.getResourceType(), attributeName, attributeName);
             }
             return Pair.of(attributeName, value);
         }
@@ -185,7 +173,7 @@
 
     @LayoutlibDelegate
     static Drawable getDrawable(Resources resources, int id, Theme theme) {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
         if (value != null) {
             String key = value.getSecond().getValue();
 
@@ -219,7 +207,7 @@
 
     @LayoutlibDelegate
     static int getColor(Resources resources, int id, Theme theme) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resourceValue = value.getSecond();
@@ -228,8 +216,9 @@
             } catch (NumberFormatException e) {
                 // Check if the value passed is a file. If it is, mostly likely, user is referencing
                 // a color state list from a place where they should reference only a pure color.
+                AssetRepository repository = getAssetRepository(resources);
                 String message;
-                if (new File(resourceValue.getValue()).isFile()) {
+                if (repository.isFileResource(resourceValue.getValue())) {
                     String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/"
                             + resourceValue.getName();
                     message = "Hexadecimal color expected, found Color State List for " + resource;
@@ -255,8 +244,7 @@
     @LayoutlibDelegate
     static ColorStateList getColorStateList(Resources resources, int id, Theme theme)
             throws NotFoundException {
-        Pair<String, ResourceValue> resValue =
-                getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> resValue = getResourceValue(resources, id);
 
         if (resValue != null) {
             ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
@@ -275,7 +263,7 @@
 
     @LayoutlibDelegate
     static CharSequence getText(Resources resources, int id, CharSequence def) {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -294,7 +282,7 @@
 
     @LayoutlibDelegate
     static CharSequence getText(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -321,12 +309,13 @@
         if (resValue == null) {
             // Error already logged by getArrayResourceValue.
             return new CharSequence[0];
-        } else if (!(resValue instanceof ArrayResourceValue)) {
-            return new CharSequence[]{
-                    resolveReference(resources, resValue.getValue(), resValue.isFramework())};
         }
-        ArrayResourceValue arv = ((ArrayResourceValue) resValue);
-        return fillValues(resources, arv, new CharSequence[arv.getElementCount()]);
+        if (resValue instanceof ArrayResourceValue) {
+            ArrayResourceValue arrayValue = (ArrayResourceValue) resValue;
+            return resolveValues(resources, arrayValue);
+        }
+        RenderResources renderResources = getContext(resources).getRenderResources();
+        return new CharSequence[] { renderResources.resolveResValue(resValue).getValue() };
     }
 
     @LayoutlibDelegate
@@ -335,28 +324,28 @@
         if (resValue == null) {
             // Error already logged by getArrayResourceValue.
             return new String[0];
-        } else if (!(resValue instanceof ArrayResourceValue)) {
-            return new String[]{
-                    resolveReference(resources, resValue.getValue(), resValue.isFramework())};
         }
-        ArrayResourceValue arv = ((ArrayResourceValue) resValue);
-        return fillValues(resources, arv, new String[arv.getElementCount()]);
+        if (resValue instanceof ArrayResourceValue) {
+            ArrayResourceValue arv = (ArrayResourceValue) resValue;
+            return resolveValues(resources, arv);
+        }
+        return new String[] { resolveReference(resources, resValue) };
     }
 
     /**
-     * Resolve each element in resValue and copy them to {@code values}. The values copied are
-     * always Strings. The ideal signature for the method should be &lt;T super String&gt;, but java
-     * generics don't support it.
+     * Resolves each element in resValue and returns an array of resolved values. The returned array
+     * may contain nulls.
      */
-    static <T extends CharSequence> T[] fillValues(Resources resources, ArrayResourceValue resValue,
-            T[] values) {
-        int i = 0;
-        for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
-            @SuppressWarnings("unchecked")
-            T s = (T) resolveReference(resources, iterator.next(), resValue.isFramework());
-            values[i] = s;
+    @NonNull
+    static String[] resolveValues(@NonNull Resources resources,
+            @NonNull ArrayResourceValue resValue) {
+        String[] result = new String[resValue.getElementCount()];
+        for (int i = 0; i < resValue.getElementCount(); i++) {
+            String value = resValue.getElement(i);
+            result[i] = resolveReference(resources, value,
+                    resValue.getNamespace(), resValue.getNamespaceResolver());
         }
-        return values;
+        return result;
     }
 
     @LayoutlibDelegate
@@ -365,39 +354,57 @@
         if (rv == null) {
             // Error already logged by getArrayResourceValue.
             return new int[0];
-        } else if (!(rv instanceof ArrayResourceValue)) {
-            // This is an older IDE that can only give us the first element of the array.
-            String firstValue = resolveReference(resources, rv.getValue(), rv.isFramework());
+        }
+        if (rv instanceof ArrayResourceValue) {
+            ArrayResourceValue resValue = (ArrayResourceValue) rv;
+            int n = resValue.getElementCount();
+            int[] values = new int[n];
+            for (int i = 0; i < n; i++) {
+                String element = resolveReference(resources, resValue.getElement(i),
+                        resValue.getNamespace(), resValue.getNamespaceResolver());
+                if (element != null) {
+                    try {
+                        if (element.startsWith("#")) {
+                            // This integer represents a color (starts with #).
+                            values[i] = Color.parseColor(element);
+                        } else {
+                            values[i] = getInt(element);
+                        }
+                    } catch (NumberFormatException e) {
+                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                                "Integer resource array contains non-integer value: \"" + element +
+                                        "\"", null);
+                    } catch (IllegalArgumentException e) {
+                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                                "Integer resource array contains wrong color format: \"" + element +
+                                        "\"", null);
+                    }
+                } else {
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                            "Integer resource array contains non-integer value: \"" +
+                                    resValue.getElement(i) + "\"", null);
+                }
+            }
+            return values;
+        }
+
+        // This is an older IDE that can only give us the first element of the array.
+        String firstValue = resolveReference(resources, rv);
+        if (firstValue != null) {
             try {
                 return new int[]{getInt(firstValue)};
             } catch (NumberFormatException e) {
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
-                        "Integer resource array contains non-integer value: " +
-                                firstValue, null);
+                        "Integer resource array contains non-integer value: \"" + firstValue + "\"",
+                        null);
                 return new int[1];
             }
+        } else {
+            Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+                    "Integer resource array contains non-integer value: \"" +
+                            rv.getValue() + "\"", null);
+            return new int[1];
         }
-        ArrayResourceValue resValue = ((ArrayResourceValue) rv);
-        int[] values = new int[resValue.getElementCount()];
-        int i = 0;
-        for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
-            String element = resolveReference(resources, iterator.next(), resValue.isFramework());
-            try {
-                if (element.startsWith("#")) {
-                    // This integer represents a color (starts with #)
-                    values[i] = Color.parseColor(element);
-                } else {
-                    values[i] = getInt(element);
-                }
-            } catch (NumberFormatException e) {
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
-                        "Integer resource array contains non-integer value: " + element, null);
-            } catch (IllegalArgumentException e2) {
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
-                        "Integer resource array contains wrong color format: " + element, null);
-            }
-        }
-        return values;
     }
 
     /**
@@ -415,7 +422,7 @@
     @Nullable
     private static ResourceValue getArrayResourceValue(Resources resources, int id)
             throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
             ResourceValue resValue = v.getSecond();
@@ -447,43 +454,47 @@
         return null;
     }
 
-    @NonNull
-    private static String resolveReference(Resources resources, @NonNull String ref,
-            boolean forceFrameworkOnly) {
-        if (ref.startsWith(PREFIX_RESOURCE_REF) || ref.startsWith
-                (SdkConstants.PREFIX_THEME_REF)) {
-            ResourceValue rv =
-                    getContext(resources).getRenderResources().findResValue(ref, forceFrameworkOnly);
-            rv = getContext(resources).getRenderResources().resolveResValue(rv);
-            if (rv != null) {
-                return rv.getValue();
-            }
+    @Nullable
+    private static String resolveReference(@NonNull Resources resources, @Nullable String value,
+            @NonNull ResourceNamespace contextNamespace,
+            @NonNull ResourceNamespace.Resolver resolver) {
+        if (value != null) {
+            ResourceValue resValue = new UnresolvedResourceValue(value, contextNamespace, resolver);
+            return resolveReference(resources, resValue);
         }
-        // Not a reference.
-        return ref;
+        return null;
+    }
+
+    @Nullable
+    private static String resolveReference(@NonNull Resources resources,
+            @NonNull ResourceValue value) {
+        RenderResources renderResources = getContext(resources).getRenderResources();
+        ResourceValue resolvedValue = renderResources.resolveResValue(value);
+        return resolvedValue == null ? null : resolvedValue.getValue();
     }
 
     @LayoutlibDelegate
     static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
 
             try {
-                return ResourceHelper.getXmlBlockParser(getContext(resources), value);
+                BridgeXmlBlockParser parser =
+                        ResourceHelper.getXmlBlockParser(getContext(resources), value);
+                if (parser != null) {
+                    return parser;
+                }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+                        "Failed to parse " + value.getValue(), e, null /*data*/);
                 // we'll return null below.
-            } catch (FileNotFoundException e) {
-                // this shouldn't happen since we check above.
             }
-
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(resources, id);
+        throwException(resources, id, "layout");
 
         // this is not used since the method above always throws
         return null;
@@ -491,7 +502,7 @@
 
     @LayoutlibDelegate
     static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
@@ -500,12 +511,9 @@
                 return ResourceHelper.getXmlBlockParser(getContext(resources), value);
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+                        "Failed to parse " + value.getValue(), e, null /*data*/);
                 // we'll return null below.
-            } catch (FileNotFoundException e) {
-                // this shouldn't happen since we check above.
             }
-
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
@@ -528,12 +536,41 @@
 
     @LayoutlibDelegate
     static TypedArray obtainTypedArray(Resources resources, int id) throws NotFoundException {
-        throw new UnsupportedOperationException();
+        BridgeContext context = getContext(resources);
+        ResourceReference reference = context.resolveId(id);
+        RenderResources renderResources = context.getRenderResources();
+        ResourceValue value = renderResources.getResolvedResource(reference);
+
+        if (!(value instanceof ArrayResourceValue)) {
+            throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
+        }
+
+        ArrayResourceValue arrayValue = (ArrayResourceValue) value;
+        int length = arrayValue.getElementCount();
+        ResourceNamespace namespace = arrayValue.getNamespace();
+        BridgeTypedArray typedArray = newTypeArray(resources, length);
+
+        for (int i = 0; i < length; i++) {
+            ResourceValue elementValue;
+            ResourceUrl resourceUrl = ResourceUrl.parse(arrayValue.getElement(i));
+            if (resourceUrl != null) {
+                ResourceReference elementRef =
+                  resourceUrl.resolve(namespace, arrayValue.getNamespaceResolver());
+                elementValue = renderResources.getResolvedResource(elementRef);
+            } else {
+                elementValue = new ResourceValueImpl(namespace, ResourceType.STRING, "element" + i,
+                  arrayValue.getElement(i));
+            }
+            typedArray.bridgeSetValue(i, elementValue.getName(), namespace, i, elementValue);
+        }
+
+        typedArray.sealArray();
+        return typedArray;
     }
 
     @LayoutlibDelegate
     static float getDimension(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -567,7 +604,7 @@
 
     @LayoutlibDelegate
     static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -596,7 +633,7 @@
 
     @LayoutlibDelegate
     static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -625,7 +662,7 @@
 
     @LayoutlibDelegate
     static int getInteger(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -652,7 +689,7 @@
 
     @LayoutlibDelegate
     static float getFloat(Resources resources, int id) {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -672,7 +709,7 @@
 
     @LayoutlibDelegate
     static boolean getBoolean(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -694,29 +731,21 @@
 
     @LayoutlibDelegate
     static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
-        Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, new boolean[1]);
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
         if (resourceInfo != null) {
-            return resourceInfo.getSecond();
+            return resourceInfo.getName();
         }
         throwException(resid, null);
         return null;
-
     }
 
     @LayoutlibDelegate
     static String getResourceName(Resources resources, int resid) throws NotFoundException {
-        boolean[] platformOut = new boolean[1];
-        Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
-        String packageName;
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
         if (resourceInfo != null) {
-            if (platformOut[0]) {
-                packageName = SdkConstants.ANDROID_NS_NAME;
-            } else {
-                packageName = getContext(resources).getPackageName();
-                packageName = packageName == null ? SdkConstants.APP_PREFIX : packageName;
-            }
-            return packageName + ':' + resourceInfo.getFirst().getName() + '/' +
-                    resourceInfo.getSecond();
+            String packageName = getPackageName(resourceInfo, resources);
+            return packageName + ':' + resourceInfo.getResourceType().getName() + '/' +
+                    resourceInfo.getName();
         }
         throwException(resid, null);
         return null;
@@ -724,14 +753,9 @@
 
     @LayoutlibDelegate
     static String getResourcePackageName(Resources resources, int resid) throws NotFoundException {
-        boolean[] platformOut = new boolean[1];
-        Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
         if (resourceInfo != null) {
-            if (platformOut[0]) {
-                return SdkConstants.ANDROID_NS_NAME;
-            }
-            String packageName = getContext(resources).getPackageName();
-            return packageName == null ? SdkConstants.APP_PREFIX : packageName;
+            return getPackageName(resourceInfo, resources);
         }
         throwException(resid, null);
         return null;
@@ -739,14 +763,25 @@
 
     @LayoutlibDelegate
     static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
-        Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, new boolean[1]);
+        ResourceReference resourceInfo = getResourceInfo(resources, resid);
         if (resourceInfo != null) {
-            return resourceInfo.getFirst().getName();
+            return resourceInfo.getResourceType().getName();
         }
         throwException(resid, null);
         return null;
     }
 
+    private static String getPackageName(ResourceReference resourceInfo, Resources resources) {
+        String packageName = resourceInfo.getNamespace().getPackageName();
+        if (packageName == null) {
+            packageName = getContext(resources).getPackageName();
+            if (packageName == null) {
+                packageName = SdkConstants.APP_PREFIX;
+            }
+        }
+        return packageName;
+    }
+
     @LayoutlibDelegate
     static String getString(Resources resources, int id, Object... formatArgs)
             throws NotFoundException {
@@ -765,7 +800,7 @@
 
     @LayoutlibDelegate
     static String getString(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null && value.getSecond().getValue() != null) {
             return value.getSecond().getValue();
@@ -781,7 +816,7 @@
     @LayoutlibDelegate
     static String getQuantityString(Resources resources, int id, int quantity) throws
             NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             if (value.getSecond() instanceof PluralsResourceValue) {
@@ -823,7 +858,7 @@
     @LayoutlibDelegate
     static Typeface getFont(Resources resources, int id) throws
             NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
         if (value != null) {
             return ResourceHelper.getFont(value.getSecond(), getContext(resources), null);
         }
@@ -837,22 +872,24 @@
     @LayoutlibDelegate
     static Typeface getFont(Resources resources, TypedValue outValue, int id) throws
             NotFoundException {
-        Resources_Delegate.getValue(resources, id, outValue, true);
-        if (outValue.string != null) {
-            return ResourceHelper.getFont(outValue.string.toString(), getContext(resources), null,
-                    mPlatformResourceFlag[0]);
+        ResourceValue resVal = getResourceValue(resources, id, outValue);
+        if (resVal != null) {
+            return ResourceHelper.getFont(resVal, getContext(resources), null);
         }
 
         throwException(resources, id);
-
-        // this is not used since the method above always throws
-        return null;
+        return null; // This is not used since the method above always throws.
     }
 
     @LayoutlibDelegate
     static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        getResourceValue(resources, id, outValue);
+    }
+
+    private static ResourceValue getResourceValue(Resources resources, int id, TypedValue outValue)
+            throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             ResourceValue resVal = value.getSecond();
@@ -861,7 +898,7 @@
             if (v != null) {
                 if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
                         false /*requireUnit*/)) {
-                    return;
+                    return resVal;
                 }
                 if (resVal instanceof DensityBasedResourceValue) {
                     outValue.density =
@@ -871,12 +908,13 @@
                 // else it's a string
                 outValue.type = TypedValue.TYPE_STRING;
                 outValue.string = v;
-                return;
+                return resVal;
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
         throwException(resources, id);
+        return null; // This is not used since the method above always throws.
     }
 
     @LayoutlibDelegate
@@ -892,8 +930,14 @@
     }
 
     @LayoutlibDelegate
+    static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+        // Not supported in layoutlib
+        return Resources.ID_NULL;
+    }
+
+    @LayoutlibDelegate
     static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> v = getResourceValue(resources, id);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
@@ -902,10 +946,8 @@
                 return ResourceHelper.getXmlBlockParser(getContext(resources), value);
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+                        "Failed to parse " + value.getValue(), e, null /*data*/);
                 // we'll return null below.
-            } catch (FileNotFoundException e) {
-                // this shouldn't happen since we check above.
             }
         }
 
@@ -928,49 +970,35 @@
         // even though we know the XML file to load directly, we still need to resolve the
         // id so that we can know if it's a platform or project resource.
         // (mPlatformResouceFlag will get the result and will be used later).
-        getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> result = getResourceValue(resources, id);
 
-        File f = new File(file);
+        ResourceNamespace layoutNamespace;
+        if (result != null && result.getSecond() != null) {
+            layoutNamespace = result.getSecond().getNamespace();
+        } else {
+            // We need to pick something, even though the resource system never heard about a layout
+            // with this numeric id.
+            layoutNamespace = ResourceNamespace.RES_AUTO;
+        }
+
         try {
-            XmlPullParser parser = ParserFactory.create(f);
-
-            return new BridgeXmlBlockParser(parser, getContext(resources), mPlatformResourceFlag[0]);
+            XmlPullParser parser = ParserFactory.create(file);
+            return new BridgeXmlBlockParser(parser, getContext(resources), layoutNamespace);
         } catch (XmlPullParserException e) {
             NotFoundException newE = new NotFoundException();
             newE.initCause(e);
             throw newE;
-        } catch (FileNotFoundException e) {
-            NotFoundException newE = new NotFoundException();
-            newE.initCause(e);
-            throw newE;
         }
     }
 
     @LayoutlibDelegate
     static InputStream openRawResource(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id);
 
         if (value != null) {
             String path = value.getSecond().getValue();
-
             if (path != null) {
-                // check this is a file
-                File f = new File(path);
-                if (f.isFile()) {
-                    try {
-                        // if it's a nine-patch return a custom input stream so that
-                        // other methods (mainly bitmap factory) can detect it's a 9-patch
-                        // and actually load it as a 9-patch instead of a normal bitmap
-                        if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
-                            return new NinePatchInputStream(f);
-                        }
-                        return new FileInputStream(f);
-                    } catch (FileNotFoundException e) {
-                        NotFoundException newE = new NotFoundException();
-                        newE.initCause(e);
-                        throw newE;
-                    }
-                }
+                return openRawResource(resources, path);
             }
         }
 
@@ -982,43 +1010,46 @@
     }
 
     @LayoutlibDelegate
-    static InputStream openRawResource(Resources resources, int id, TypedValue value) throws
-            NotFoundException {
+    static InputStream openRawResource(Resources resources, int id, TypedValue value)
+            throws NotFoundException {
         getValue(resources, id, value, true);
 
         String path = value.string.toString();
+        return openRawResource(resources, path);
+    }
 
-        File f = new File(path);
-        if (f.isFile()) {
-            try {
-                // if it's a nine-patch return a custom input stream so that
-                // other methods (mainly bitmap factory) can detect it's a 9-patch
-                // and actually load it as a 9-patch instead of a normal bitmap
-                if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
-                    return new NinePatchInputStream(f);
-                }
-                return new FileInputStream(f);
-            } catch (FileNotFoundException e) {
-                NotFoundException exception = new NotFoundException();
-                exception.initCause(e);
-                throw exception;
+    private static InputStream openRawResource(Resources resources, String path)
+            throws NotFoundException {
+        AssetRepository repository = getAssetRepository(resources);
+        try {
+            InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING);
+            if (stream == null) {
+                throw new NotFoundException(path);
             }
+            // If it's a nine-patch return a custom input stream so that
+            // other methods (mainly bitmap factory) can detect it's a 9-patch
+            // and actually load it as a 9-patch instead of a normal bitmap.
+            if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
+                return new NinePatchInputStream(stream);
+            }
+            return stream;
+        } catch (IOException e) {
+            NotFoundException exception = new NotFoundException();
+            exception.initCause(e);
+            throw exception;
         }
-
-        throw new NotFoundException();
     }
 
     @LayoutlibDelegate
-    static AssetFileDescriptor openRawResourceFd(Resources resources, int id) throws
-            NotFoundException {
+    static AssetFileDescriptor openRawResourceFd(Resources resources, int id)
+            throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
     @VisibleForTesting
     @Nullable
-    static ResourceUrl resourceUrlFromName(@NonNull String name, @Nullable String defType,
-            @Nullable
-            String defPackage) {
+    static ResourceUrl resourceUrlFromName(
+            @NonNull String name, @Nullable String defType, @Nullable String defPackage) {
         int colonIdx = name.indexOf(':');
         int slashIdx = name.indexOf('/');
 
@@ -1068,13 +1099,18 @@
         }
 
         ResourceUrl url = resourceUrlFromName(name, defType, defPackage);
-        Integer id = null;
         if (url != null) {
-            id = ANDROID_PKG.equals(url.namespace) ? Bridge.getResourceId(url.type, url.name) :
-                    getLayoutlibCallback(resources).getResourceId(url.type, url.name);
+            if (ANDROID_PKG.equals(url.namespace)) {
+                return Bridge.getResourceId(url.type, url.name);
+            }
+
+            if (getContext(resources).getPackageName().equals(url.namespace)) {
+                return getLayoutlibCallback(resources).getOrGenerateResourceId(
+                        new ResourceReference(ResourceNamespace.RES_AUTO, url.type, url.name));
+            }
         }
 
-        return id != null ? id : 0;
+        return 0;
     }
 
     /**
@@ -1082,23 +1118,36 @@
      * type.
      *
      * @param id the id of the resource
+     * @param expectedType the type of resource that was expected
      *
      * @throws NotFoundException
      */
-    private static void throwException(Resources resources, int id) throws NotFoundException {
-        throwException(id, getResourceInfo(resources, id, new boolean[1]));
+    private static void throwException(Resources resources, int id, @Nullable String expectedType)
+            throws NotFoundException {
+        throwException(id, getResourceInfo(resources, id), expectedType);
     }
 
-    private static void throwException(int id, @Nullable Pair<ResourceType, String> resourceInfo) {
+    private static void throwException(Resources resources, int id) throws NotFoundException {
+        throwException(resources, id, null);
+    }
+
+    private static void throwException(int id, @Nullable ResourceReference resourceInfo) {
+        throwException(id, resourceInfo, null);
+    }
+    private static void throwException(int id, @Nullable ResourceReference resourceInfo,
+            @Nullable String expectedType) {
         String message;
         if (resourceInfo != null) {
             message = String.format(
                     "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
-                    resourceInfo.getFirst(), id, resourceInfo.getSecond());
+                    resourceInfo.getResourceType(), id, resourceInfo.getName());
         } else {
             message = String.format("Could not resolve resource value: 0x%1$X.", id);
         }
 
+        if (expectedType != null) {
+            message += " Or the resolved value was not of type " + expectedType + " as expected.";
+        }
         throw new NotFoundException(message);
     }
 
@@ -1112,4 +1161,10 @@
         }
         return Integer.parseInt(v, radix);
     }
+
+    private static AssetRepository getAssetRepository(Resources resources) {
+        BridgeContext context = getContext(resources);
+        BridgeAssetManager assetManager = context.getAssets();
+        return assetManager.getAssetRepository();
+    }
 }
diff --git a/bridge/src/android/content/res/Resources_Theme_Delegate.java b/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 8aa9216..4740fac 100644
--- a/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -142,12 +142,6 @@
         }
         BridgeContext context = RenderSessionImpl.getCurrentContext();
         ResourceReference theme = context.resolveId(nativeResid);
-        if (theme.isFramework()) {
-            return (StyleResourceValue) context.getRenderResources()
-                    .getFrameworkResource(ResourceType.STYLE, theme.getName());
-        } else {
-            return (StyleResourceValue) context.getRenderResources()
-                    .getProjectResource(ResourceType.STYLE, theme.getName());
-        }
+        return (StyleResourceValue) context.getRenderResources().getResolvedResource(theme);
     }
 }
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
index 9260099..a7e36f0 100644
--- a/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ b/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -26,10 +26,13 @@
 
 import android.annotation.Nullable;
 import android.text.TextUtils;
+import android.util.imagepool.ImagePool;
+import android.util.imagepool.ImagePoolProvider;
 
 import java.awt.*;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
@@ -75,10 +78,10 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float left, float top,
+    /*package*/ static void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, float top,
             long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) {
         // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
         if (bitmapDelegate == null) {
             return;
         }
@@ -93,11 +96,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nDrawBitmap(long nativeCanvas, Bitmap bitmap, float srcLeft, float srcTop,
-            float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
-            float dstBottom, long nativePaintOrZero, int screenDensity, int bitmapDensity) {
+    /*package*/ static void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft,
+            float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity,
+            int bitmapDensity) {
         // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
         if (bitmapDelegate == null) {
             return;
         }
@@ -112,7 +116,7 @@
             final float x, final float y, int width, int height, boolean hasAlpha,
             long nativePaintOrZero) {
         // create a temp BufferedImage containing the content.
-        final BufferedImage image = new BufferedImage(width, height,
+        final ImagePool.Image image = ImagePoolProvider.get().acquire(width, height,
                 hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
         image.setRGB(0, 0, width, height, colors, offset, stride);
 
@@ -123,7 +127,7 @@
                                 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                     }
 
-                    graphics.drawImage(image, (int) x, (int) y, null);
+                    image.drawImage(graphics, (int) x, (int) y, null);
                 });
     }
 
@@ -155,6 +159,12 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nDrawColor(long nativeCanvas, long nativeColorSpace, long color,
+            int mode) {
+        nDrawColor(nativeCanvas, Color.toArgb(color), mode);
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -315,6 +325,49 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+            float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
+            float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
+            float innerRy, long nativePaint) {
+        nDrawDoubleRoundRect(nativeCanvas, outerLeft, outerTop, outerRight, outerBottom,
+                new float[]{outerRx, outerRy, outerRx, outerRy, outerRx, outerRy, outerRx, outerRy},
+                innerLeft, innerTop, innerRight, innerBottom,
+                new float[]{innerRx, innerRy, innerRx, innerRy, innerRx, innerRy, innerRx, innerRy},
+                nativePaint);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+            float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+            float innerLeft, float innerTop, float innerRight, float innerBottom,
+            float[] innerRadii, long nativePaint) {
+        draw(nativeCanvas, nativePaint, false /*compositeOnly*/, false /*forceSrcMode*/,
+                (graphics, paintDelegate) -> {
+                    RoundRectangle innerRect = new RoundRectangle(innerLeft, innerTop,
+                            innerRight - innerLeft, innerBottom - innerTop, innerRadii);
+                    RoundRectangle outerRect = new RoundRectangle(outerLeft, outerTop,
+                            outerRight - outerLeft, outerBottom - outerTop, outerRadii);
+
+                    int style = paintDelegate.getStyle();
+
+                    // draw
+                    if (style == Paint.Style.STROKE.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        graphics.draw(innerRect);
+                        graphics.draw(outerRect);
+                    }
+
+                    if (style == Paint.Style.FILL.nativeInt ||
+                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                        Area outerArea = new Area(outerRect);
+                        Area innerArea = new Area(innerRect);
+                        outerArea.subtract(innerArea);
+                        graphics.fill(outerArea);
+                    }
+                });
+    }
+
+    @LayoutlibDelegate
     public static void nDrawPath(long nativeCanvas, long path, long paint) {
         final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
         if (pathDelegate == null) {
@@ -417,7 +470,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nDrawBitmapMatrix(long nCanvas, Bitmap bitmap,
+    /*package*/ static void nDrawBitmapMatrix(long nCanvas, long bitmapHandle,
             long nMatrix, long nPaint) {
         // get the delegate from the native int.
         BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
@@ -429,7 +482,7 @@
         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
 
         // get the delegate from the native int.
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
         if (bitmapDelegate == null) {
             return;
         }
@@ -455,7 +508,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nDrawBitmapMesh(long nCanvas, Bitmap bitmap,
+    /*package*/ static void nDrawBitmapMesh(long nCanvas, long bitmapHandle,
             int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
             int colorOffset, long nPaint) {
         // FIXME
@@ -508,7 +561,7 @@
     /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
             int start, int count, int contextStart, int contextCount,
             float x, float y, boolean isRtl, long paint,
-            long nativeMeasuredText, int measuredTextOffset) {
+            long nativeMeasuredText) {
         drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint);
     }
 
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
index 9a102c1..204022d 100644
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ b/bridge/src/android/graphics/BidiRenderer.java
@@ -154,7 +154,6 @@
         }
 
         while (start < limit) {
-            boolean foundFont = false;
             int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
             if (canDisplayUpTo == -1) {
                 // We can draw all characters in the text.
@@ -166,38 +165,59 @@
                 render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
                 advancesIndex += canDisplayUpTo - start;
                 start = canDisplayUpTo;
-            }
+            } else {
+                // We can display everything with the preferred font. Search for the font that
+                // allows us to display the maximum number of chars
+                List<FontInfo> fontInfos = mPaint.getFonts();
+                Font bestFont = null;
+                int highestUpTo = canDisplayUpTo;
+                //noinspection ForLoopReplaceableByForEach
+                for (int i = 0; i < fontInfos.size(); i++) {
+                    Font font = fontInfos.get(i).mFont;
 
-            // The current character cannot be drawn with the preferred font. Cycle through all the
-            // fonts to check which one can draw it.
-            int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-            List<FontInfo> fontInfos = mPaint.getFonts();
-            //noinspection ForLoopReplaceableByForEach (avoid iterator allocation)
-            for (int i = 0; i < fontInfos.size(); i++) {
-                Font font = fontInfos.get(i).mFont;
-                if (font == null) {
-                    logFontWarning();
-                    continue;
+                    if (preferredFont == font) {
+                        // We know this font won't work since we've already tested it at the
+                        // beginning of the loop
+                        continue;
+                    }
+
+                    if (font == null) {
+                        logFontWarning();
+                        continue;
+                    }
+
+                    canDisplayUpTo = font.canDisplayUpTo(mText, start, limit);
+                    if (canDisplayUpTo == -1) {
+                        // This font can dis
+                        highestUpTo = limit;
+                        bestFont = font;
+                        break;
+                    } else if (canDisplayUpTo > highestUpTo) {
+                        highestUpTo = canDisplayUpTo;
+                        bestFont = font;
+                        // Keep searching in case there is a font that allows to display even
+                        // more text
+                    }
                 }
-                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
-                if (canDisplayUpTo == -1) {
-                    render(start, start+charCount, font, flag, advances, advancesIndex, draw);
+
+                if (bestFont != null) {
+                    render(start, highestUpTo, bestFont, flag, advances, advancesIndex, draw);
+                    advancesIndex += highestUpTo - start;
+                    start = highestUpTo;
+                } else {
+                    int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
+
+                    // No font can display this char. Use the preferred font and skip this char.
+                    // The char will most probably appear as a box or a blank space. We could,
+                    // probably, use some heuristics and break the character into the base
+                    // character and diacritics and then draw it, but it's probably not worth the
+                    // effort.
+                    render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
+                            draw);
                     start += charCount;
                     advancesIndex += charCount;
-                    foundFont = true;
-                    break;
                 }
             }
-            if (!foundFont) {
-                // No font can display this char. Use the preferred font. The char will most
-                // probably appear as a box or a blank space. We could, probably, use some
-                // heuristics and break the character into the base character and diacritics and
-                // then draw it, but it's probably not worth the effort.
-                render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
-                        draw);
-                start += charCount;
-                advancesIndex += charCount;
-            }
         }
     }
 
@@ -313,19 +333,29 @@
     // TODO: Replace this method with one which returns the font based on the scriptCode.
     @NonNull
     private static Font getScriptFont(char[] text, int start, int limit, List<FontInfo> fonts) {
-        for (FontInfo fontInfo : fonts) {
-            if (fontInfo.mFont.canDisplayUpTo(text, start, limit) == -1) {
-                return fontInfo.mFont;
-            }
-        }
-
         if (fonts.isEmpty()) {
             logFontWarning();
             // Fallback font in case no font can be loaded
             return Font.getFont(Font.SERIF);
         }
 
-        return fonts.get(0).mFont;
+        // From all the fonts, select the one that can display the highest number of characters
+        Font bestFont = fonts.get(0).mFont;
+        int bestFontCount = 0;
+        for (FontInfo fontInfo : fonts) {
+            int count = fontInfo.mFont.canDisplayUpTo(text, start, limit);
+            if (count == -1) {
+                // This font can display everything, return this one
+                return fontInfo.mFont;
+            }
+
+            if (count > bestFontCount) {
+                bestFontCount = count;
+                bestFont = fontInfo.mFont;
+            }
+        }
+
+        return bestFont;
     }
 
     private static int getIcuFlags(int bidiFlag) {
diff --git a/bridge/src/android/graphics/BitmapFactory_Delegate.java b/bridge/src/android/graphics/BitmapFactory_Delegate.java
index ee099e1..80c6761 100644
--- a/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -49,7 +49,8 @@
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
-            @Nullable Rect padding, @Nullable Options opts) {
+            @Nullable Rect padding, @Nullable Options opts, long inBitmapHandle,
+            long colorSpaceHandle) {
         Bitmap bm = null;
 
         Density density = Density.MEDIUM;
@@ -100,7 +101,7 @@
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
-            Rect padding, Options opts) {
+            Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
         if (opts != null) {
             opts.inBitmap = null;
         }
@@ -108,7 +109,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static Bitmap nativeDecodeAsset(long asset, Rect padding, Options opts) {
+    /*package*/ static Bitmap nativeDecodeAsset(long asset, Rect padding, Options opts,
+            long inBitmapHandle, long colorSpaceHandle) {
         if (opts != null) {
             opts.inBitmap = null;
         }
@@ -117,7 +119,7 @@
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
-            int length, Options opts) {
+            int length, Options opts, long inBitmapHandle, long colorSpaceHandle) {
         if (opts != null) {
             opts.inBitmap = null;
         }
diff --git a/bridge/src/android/graphics/BitmapShader_Delegate.java b/bridge/src/android/graphics/BitmapShader_Delegate.java
index 891b07f..0e3e9a8 100644
--- a/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -77,9 +77,9 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreate(long nativeMatrix, Bitmap androidBitmap,
+    /*package*/ static long nativeCreate(long nativeMatrix, long bitmapHandle,
             int shaderTileModeX, int shaderTileModeY) {
-        Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(androidBitmap);
+        Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(bitmapHandle);
         if (bitmap == null) {
             return 0;
         }
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
index 6c72cb2..c3d9665 100644
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceValue;
@@ -29,11 +30,11 @@
 
 import android.annotation.Nullable;
 import android.graphics.Bitmap.Config;
+import android.hardware.HardwareBuffer;
 import android.os.Parcel;
 
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -45,6 +46,8 @@
 import javax.imageio.ImageIO;
 import libcore.util.NativeAllocationRegistry_Delegate;
 
+import static android.content.res.AssetManager.ACCESS_STREAMING;
+
 /**
  * Delegate implementing the native methods of android.graphics.Bitmap
  *
@@ -60,7 +63,6 @@
  */
 public final class Bitmap_Delegate {
 
-
     public enum BitmapCreateFlags {
         NONE, PREMULTIPLIED, MUTABLE
     }
@@ -79,6 +81,7 @@
     private boolean mHasMipMap = false;      // TODO: check the default.
     private boolean mIsPremultiplied = true;
     private int mGenerationId = 0;
+    private boolean mIsMutable;
 
 
     // ---- Public Helper methods ----
@@ -90,23 +93,18 @@
         return sManager.getDelegate(native_bitmap);
     }
 
-    @Nullable
-    public static Bitmap_Delegate getDelegate(@Nullable Bitmap bitmap) {
-        return bitmap == null ? null : getDelegate(bitmap.getNativeInstance());
-    }
-
     /**
-     * Creates and returns a {@link Bitmap} initialized with the given file content.
+     * Creates and returns a {@link Bitmap} initialized with the given stream content.
      *
-     * @param input the file from which to read the bitmap content
+     * @param input the stream from which to read the bitmap content
      * @param isMutable whether the bitmap is mutable
      * @param density the density associated with the bitmap
      *
      * @see Bitmap#isMutable()
      * @see Bitmap#getDensity()
      */
-    public static Bitmap createBitmap(File input, boolean isMutable, Density density)
-            throws IOException {
+    public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable,
+            Density density) throws IOException {
         return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
     }
 
@@ -120,58 +118,30 @@
      * @see Bitmap#isMutable()
      * @see Bitmap#getDensity()
      */
-    private static Bitmap createBitmap(File input, Set<BitmapCreateFlags> createFlags,
+    static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags,
             Density density) throws IOException {
         // create a delegate with the content of the file.
-        BufferedImage image = ImageIO.read(input);
-        if (image == null && input.exists()) {
+        BufferedImage image = input == null ? null : ImageIO.read(input);
+        if (image == null) {
             // There was a problem decoding the image, or the decoder isn't registered. Webp maybe.
             // Replace with a broken image icon.
             BridgeContext currentContext = RenderAction.getCurrentContext();
             if (currentContext != null) {
                 RenderResources resources = currentContext.getRenderResources();
-                ResourceValue broken = resources.getFrameworkResource(ResourceType.DRAWABLE,
-                        "ic_menu_report_image");
-                File brokenFile = new File(broken.getValue());
-                if (brokenFile.exists()) {
-                    image = ImageIO.read(brokenFile);
+                ResourceValue broken = resources.getResolvedResource(
+                        BridgeContext.createFrameworkResourceReference(
+                                ResourceType.DRAWABLE, "ic_menu_report_image"));
+                AssetRepository assetRepository = currentContext.getAssets().getAssetRepository();
+                try (InputStream stream =
+                        assetRepository.openNonAsset(0, broken.getValue(), ACCESS_STREAMING)) {
+                    if (stream != null) {
+                        image = ImageIO.read(stream);
+                    }
                 }
             }
         }
         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
-
-        return createBitmap(delegate, createFlags, density.getDpiValue());
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given stream content.
-     *
-     * @param input the stream from which to read the bitmap content
-     * @param isMutable whether the bitmap is mutable
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density)
-            throws IOException {
-        return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
-    }
-
-    /**
-     * Creates and returns a {@link Bitmap} initialized with the given stream content.
-     *
-     * @param input the stream from which to read the bitmap content
-     * @param density the density associated with the bitmap
-     *
-     * @see Bitmap#isPremultiplied()
-     * @see Bitmap#isMutable()
-     * @see Bitmap#getDensity()
-     */
-    public static Bitmap createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags,
-            Density density) throws IOException {
-        // create a delegate with the content of the stream.
-        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
+        delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
 
         return createBitmap(delegate, createFlags, density.getDpiValue());
     }
@@ -204,6 +174,7 @@
             Density density) {
         // create a delegate with the given image.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
+        delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
 
         return createBitmap(delegate, createFlags, density.getDpiValue());
     }
@@ -248,8 +219,7 @@
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
-            int height, int nativeConfig, boolean isMutable, @Nullable float[] xyzD50,
-            @Nullable ColorSpace.Rgb.TransferParameters p) {
+            int height, int nativeConfig, boolean isMutable, long nativeColorSpace) {
         int imageType = getBufferedImageType();
 
         // create the image
@@ -261,6 +231,7 @@
 
         // create a delegate with the content of the stream.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
+        delegate.mIsMutable = isMutable;
 
         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
                             Bitmap.getDefaultDensity());
@@ -290,6 +261,7 @@
 
         // create a delegate with the content of the stream.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
+        delegate.mIsMutable = isMutable;
 
         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
                 Bitmap.getDefaultDensity());
@@ -320,9 +292,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nativeRecycle(long nativeBitmap) {
-        // In our case reycle() is a no-op. We will let the finalizer to dispose the bitmap.
-        return true;
+    /*package*/ static void nativeRecycle(long nativeBitmap) {
+        // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap.
     }
 
     @LayoutlibDelegate
@@ -361,6 +332,11 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
+        nativeErase(nativeBitmap, Color.toArgb(color));
+    }
+
+    @LayoutlibDelegate
     /*package*/ static int nativeRowBytes(long nativeBitmap) {
         // get the delegate from the native int.
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -410,6 +386,11 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) {
+        return nativeGetPixel(nativeBitmap, x, y);
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
             int stride, int x, int y, int width, int height) {
         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
@@ -509,6 +490,7 @@
 
         // create the delegate. The actual Bitmap config is only an alpha channel
         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
+        delegate.mIsMutable = true;
 
         // the density doesn't matter, it's set by the Java method.
         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
@@ -633,15 +615,17 @@
 
         // create a delegate with the content of the stream.
         Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
+        delegate.mIsMutable = srcBmpDelegate.mIsMutable;
 
         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
                 Bitmap.getDefaultDensity());
     }
 
     @LayoutlibDelegate
-    /*package*/ static Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer) {
+    /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
+            long nativeColorSpace) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Bitmap.nativeCreateHardwareBitmap() is not supported", null /*data*/);
+                "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null);
         return null;
     }
 
@@ -660,16 +644,41 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params) {
+    /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "Color spaces are not supported", null /*data*/);
+        return null;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
+        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+                "Color spaces are not supported", null /*data*/);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
                 "Color spaces are not supported", null /*data*/);
         return false;
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nativeCopyColorSpace(long srcBitmap, long dstBitmap) {
-        Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
-                "Color spaces are not supported", null /*data*/);
+    /*package*/ static void nativeSetImmutable(long nativePtr) {
+        Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
+        if (bmpDelegate == null) {
+            return;
+        }
+        bmpDelegate.mIsMutable = false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nativeIsImmutable(long nativePtr) {
+        Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
+        if (bmpDelegate == null) {
+            return false;
+        }
+        return !bmpDelegate.mIsMutable;
     }
 
     // ---- Private delegate/helper methods ----
@@ -686,12 +695,11 @@
 
         int width = delegate.mImage.getWidth();
         int height = delegate.mImage.getHeight();
-        boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
 
         // and create/return a new Bitmap with it
-        return new Bitmap(nativeInt, width, height, density, isMutable,
-                          isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
+        return new Bitmap(nativeInt, width, height, density, isPremultiplied,
+                null /*ninePatchChunk*/, null /* layoutBounds */, true /* fromMalloc */);
     }
 
     private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
diff --git a/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java b/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java
new file mode 100644
index 0000000..fc25903
--- /dev/null
+++ b/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.BlendModeColorFilter
+ *
+ * Through the layoutlib_create tool, the original native methods of BlendModeColorFilter have
+ * been replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PorterDuffColorFilter class.
+ *
+ * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the Shader classes will be added to the manager
+ * owned by {@link ColorFilter_Delegate}.
+ *
+ * @see ColorFilter_Delegate
+ *
+ */
+public class BlendModeColorFilter_Delegate extends ColorFilter_Delegate {
+
+    @Override
+    public String getSupportMessage() {
+        return "BlendMode Color Filters are not supported.";
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static long native_CreateBlendModeFilter(int srcColor, int blendmode) {
+        return PorterDuffColorFilter_Delegate.native_CreateBlendModeFilter(srcColor, blendmode);
+    }
+}
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index a23244b..2d18d7f 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -97,14 +97,10 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nInitRaster(@Nullable Bitmap bitmap) {
-        long nativeBitmapOrZero = 0;
-        if (bitmap != null) {
-            nativeBitmapOrZero = bitmap.getNativeInstance();
-        }
-        if (nativeBitmapOrZero > 0) {
+    /*package*/ static long nInitRaster(long bitmapHandle) {
+        if (bitmapHandle > 0) {
             // get the Bitmap from the int
-            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
+            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
 
             // create a new Canvas_Delegate with the given bitmap and return its new native int.
             Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
@@ -119,10 +115,10 @@
     }
 
     @LayoutlibDelegate
-    public static void nSetBitmap(long canvas, Bitmap bitmap) {
+    public static void nSetBitmap(long canvas, long bitmapHandle) {
         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
-        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
-        if (canvasDelegate == null || bitmapDelegate==null) {
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
+        if (canvasDelegate == null || bitmapDelegate == null) {
             return;
         }
         canvasDelegate.mBitmap = bitmapDelegate;
@@ -184,15 +180,17 @@
         }
 
         Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
-        if (paintDelegate == null) {
-            return 0;
-        }
 
         return canvasDelegate.saveLayer(new RectF(l, t, r, b),
                 paintDelegate, layerFlags);
     }
 
     @LayoutlibDelegate
+    public static int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b) {
+        return nSaveLayer(nativeCanvas, l, t, r, b, 0, 0);
+    }
+
+    @LayoutlibDelegate
     public static int nSaveLayerAlpha(long nativeCanvas, float l,
                                                     float t, float r, float b,
                                                     int alpha, int layerFlags) {
diff --git a/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java b/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java
new file mode 100644
index 0000000..3637055
--- /dev/null
+++ b/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.ColorSpace
+ *
+ * Through the layoutlib_create tool, the original native methods of ColorSpace have been replaced
+ * by calls to methods of the same name in this delegate class.
+ */
+public class ColorSpace_Rgb_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<ColorSpace_Rgb_Delegate> sManager =
+            new DelegateManager<>(ColorSpace_Rgb_Delegate.class);
+    private static long sFinalizer = -1;
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeGetNativeFinalizer() {
+        synchronized (ColorSpace_Rgb_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeCreate(float a, float b, float c, float d,
+            float e, float f, float g, float[] xyz) {
+        // Layoutlib does not support color spaces, but a native object is required
+        // for ColorSpace$Rgb. This creates an empty delegate for it.
+        return sManager.addNewDelegate(new ColorSpace_Rgb_Delegate());
+    }
+}
diff --git a/bridge/src/android/graphics/Color_Delegate.java b/bridge/src/android/graphics/Color_Delegate.java
new file mode 100644
index 0000000..afd24f0
--- /dev/null
+++ b/bridge/src/android/graphics/Color_Delegate.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Color
+ *
+ * Through the layoutlib_create tool, the original native methods of Color have been replaced
+ * by calls to methods of the same name in this delegate class.
+ */
+public class Color_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static void nativeRGBToHSV(int red, int greed, int blue, float hsv[]) {
+        java.awt.Color.RGBtoHSB(red, greed, blue, hsv);
+        hsv[0] = hsv[0] * 360;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nativeHSVToColor(int alpha, float hsv[]) {
+        java.awt.Color rgb = new java.awt.Color(java.awt.Color.HSBtoRGB(hsv[0] / 360, pin(hsv[1]), pin(hsv[2])));
+        return Color.argb(alpha, rgb.getRed(), rgb.getGreen(), rgb.getBlue());
+    }
+
+    private static float pin(float value) {
+        return Math.max(0, Math.min(1, value));
+    }
+}
diff --git a/bridge/src/android/graphics/FontFamily_Delegate.java b/bridge/src/android/graphics/FontFamily_Delegate.java
index 1ad1f8f..db2cd1b 100644
--- a/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -44,6 +44,10 @@
 import java.util.Objects;
 import java.util.Scanner;
 import java.util.Set;
+import java.util.logging.Logger;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+import sun.font.FontUtilities;
 
 import static android.graphics.Typeface.RESOLVE_BY_FONT_TABLE;
 import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
@@ -92,11 +96,11 @@
     /**
      * A class associating {@link Font} with its metadata.
      */
-    private static final class FontInfo {
+    public static final class FontInfo {
         @Nullable
-        Font mFont;
-        int mWeight;
-        boolean mIsItalic;
+        public Font mFont;
+        public int mWeight;
+        public boolean mIsItalic;
 
         @Override
         public boolean equals(Object o) {
@@ -124,6 +128,7 @@
     // ---- delegate manager ----
     private static final DelegateManager<FontFamily_Delegate> sManager =
             new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
+    private static long sFamilyFinalizer = -1;
 
     // ---- delegate helper data ----
     private static String sFontLocation;
@@ -218,17 +223,16 @@
         } else {
             int bestMatch = Integer.MAX_VALUE;
 
-            //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
             for (FontInfo font : mFonts.keySet()) {
                 int match = computeMatch(font, desiredStyle);
                 if (match < bestMatch) {
                     bestMatch = match;
                     bestFont = font;
+                    if (bestMatch == 0) {
+                        break;
+                    }
                 }
             }
-
-            // This would mean that we already had the font so it should be in the set
-            assert bestMatch != 0;
         }
 
         if (bestFont == null) {
@@ -284,7 +288,7 @@
     }
 
     @Nullable
-    /*package*/ static String getFontLocation() {
+    public static String getFontLocation() {
         return sFontLocation;
     }
 
@@ -323,10 +327,14 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nUnrefFamily(long nativePtr) {
-        // Removing the java reference for the object doesn't mean that it's freed for garbage
-        // collection. Typeface_Delegate may still hold a reference for it.
-        sManager.removeJavaReferenceFor(nativePtr);
+    /*package*/ static long nGetFamilyReleaseFunc() {
+        synchronized (FontFamily_Delegate.class) {
+            if (sFamilyFinalizer == -1) {
+                sFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFamilyFinalizer;
     }
 
     @LayoutlibDelegate
@@ -406,12 +414,16 @@
                 fontInfo = new FontInfo();
                 fontInfo.mFont = font;
                 if (weight == RESOLVE_BY_FONT_TABLE) {
-                    fontInfo.mWeight = font.isBold() ? BOLD_FONT_WEIGHT : DEFAULT_FONT_WEIGHT;
+                    fontInfo.mWeight = FontUtilities.getFont2D(font).getWeight();
                 } else {
                     fontInfo.mWeight = weight;
                 }
-                fontInfo.mIsItalic = isItalic == RESOLVE_BY_FONT_TABLE ? font.isItalic() :
-                        isItalic == 1;
+                if (isItalic == RESOLVE_BY_FONT_TABLE) {
+                    fontInfo.mIsItalic =
+                            (FontUtilities.getFont2D(font).getStyle() & Font.ITALIC) != 0;
+                } else {
+                    fontInfo.mIsItalic = isItalic == 1;
+                }
                 ffd.addFont(fontInfo);
                 return true;
             } catch (IOException e) {
@@ -447,8 +459,10 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nAbort(long builderPtr) {
-        sManager.removeJavaReferenceFor(builderPtr);
+    /*package*/ static long nGetBuilderReleaseFunc() {
+        // Layoutlib uses the same reference for the builder and the font family,
+        // so it should not release that reference at the builder stage.
+        return -1;
     }
 
     // ---- private helper methods ----
@@ -476,6 +490,7 @@
     private boolean addFont(@NonNull String path, int weight, int italic) {
         if (path.startsWith(SYSTEM_FONTS) &&
                 !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) {
+            Logger.getLogger(FontFamily_Delegate.class.getSimpleName()).warning("Unable to load font " + path);
             return mValid = false;
         }
         // Set valid to true, even if the font fails to load.
@@ -499,10 +514,10 @@
     /**
      * Compute matching metric between two styles - 0 is an exact match.
      */
-    private static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
-        int score = Math.abs(font1.mWeight - font2.mWeight);
+    public static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
+        int score = Math.abs(font1.mWeight / 100 - font2.mWeight / 100);
         if (font1.mIsItalic != font2.mIsItalic) {
-            score += 200;
+            score += 2;
         }
         return score;
     }
@@ -514,9 +529,8 @@
      * @param srcFont the source font
      * @param outFont contains the desired font style. Updated to contain the derived font and
      *                its style
-     * @return outFont
      */
-    private void deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
+    public static void deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
         int desiredWeight = outFont.mWeight;
         int srcWeight = srcFont.mWeight;
         assert srcFont.mFont != null;
diff --git a/bridge/src/android/graphics/Gradient_Delegate.java b/bridge/src/android/graphics/Gradient_Delegate.java
index 64410e4..8aed4b4 100644
--- a/bridge/src/android/graphics/Gradient_Delegate.java
+++ b/bridge/src/android/graphics/Gradient_Delegate.java
@@ -18,6 +18,8 @@
 
 import android.graphics.Shader.TileMode;
 
+import java.util.Arrays;
+
 /**
  * Base class for true Gradient shader delegate.
  */
@@ -47,7 +49,7 @@
      *            corresponding color in the colors array. If this is null, the
      *            the colors are distributed evenly along the gradient line.
      */
-    protected Gradient_Delegate(long nativeMatrix, int colors[], float positions[]) {
+    protected Gradient_Delegate(long nativeMatrix, long[] colors, float[] positions) {
         super(nativeMatrix);
         assert colors.length >= 2 : "needs >= 2 number of colors";
 
@@ -62,9 +64,13 @@
         } else {
             assert colors.length == positions.length :
                     "color and position " + "arrays must be of equal length";
+            positions[0] = Math.min(Math.max(0, positions[0]), 1);
+            for (int i = 1; i < positions.length; i++) {
+                positions[i] = Math.min(Math.max(positions[i-1], positions[i]), 1);
+            }
         }
 
-        mColors = colors;
+        mColors = Arrays.stream(colors).mapToInt(Color::toArgb).toArray();
         mPositions = positions;
     }
 
@@ -107,14 +113,24 @@
                 for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
                     // compute current position
                     float currentPos = (float)i/GRADIENT_SIZE;
-                    while (currentPos > mPositions[nextPos]) {
+
+                    if (currentPos < mPositions[0]) {
+                        mGradient[i] = mColors[0];
+                        continue;
+                    }
+
+                    while (nextPos < mPositions.length && currentPos >= mPositions[nextPos]) {
                         prevPos = nextPos++;
                     }
 
-                    float percent = (currentPos - mPositions[prevPos]) /
-                            (mPositions[nextPos] - mPositions[prevPos]);
+                    if (nextPos == mPositions.length || currentPos == prevPos) {
+                        mGradient[i] = mColors[prevPos];
+                    } else {
+                        float percent = (currentPos - mPositions[prevPos]) /
+                                (mPositions[nextPos] - mPositions[prevPos]);
 
-                    mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
+                        mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
+                    }
                 }
             }
         }
diff --git a/bridge/src/android/graphics/ImageDecoder.java b/bridge/src/android/graphics/ImageDecoder.java
index 506eab5..eefdb2e 100644
--- a/bridge/src/android/graphics/ImageDecoder.java
+++ b/bridge/src/android/graphics/ImageDecoder.java
@@ -513,9 +513,7 @@
      *
      *  @param allocator Type of allocator to use.
      */
-    public ImageDecoder setAllocator(@Allocator int allocator) {
-        return this;
-    }
+    public void setAllocator(@Allocator int allocator) { }
 
     /**
      *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
@@ -546,9 +544,7 @@
      *  {@link Canvas} will be recorded immediately and then applied to each
      *  frame.</p>
      */
-    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
-        return this;
-    }
+    public void setPostProcessor(@Nullable PostProcessor p) { }
 
     /**
      *  Set (replace) the {@link OnPartialImageListener} on this object.
@@ -556,9 +552,7 @@
      *  Will be called if there is an error in the input. Without one, a
      *  partial {@link Bitmap} will be created.
      */
-    public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
-        return this;
-    }
+    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { }
 
     /**
      *  Crop the output to {@code subset} of the (possibly) scaled image.
@@ -572,9 +566,7 @@
      *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
      *  but merely crops the output.</p>
      */
-    public ImageDecoder setCrop(@Nullable Rect subset) {
-        return this;
-    }
+    public void setCrop(@Nullable Rect subset) { }
 
     /**
      *  Set a Rect for retrieving nine patch padding.
@@ -584,9 +576,8 @@
      *
      *  @hide
      */
-    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+    public void setOutPaddingRect(@NonNull Rect outPadding) {
         mOutPaddingRect = outPadding;
-        return this;
     }
 
     /**
@@ -701,4 +692,77 @@
     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
         return decodeBitmap(src, null);
     }
+
+    public static final class DecodeException extends IOException {
+        /**
+         *  An Exception was thrown reading the {@link Source}.
+         */
+        public static final int SOURCE_EXCEPTION  = 1;
+
+        /**
+         *  The encoded data was incomplete.
+         */
+        public static final int SOURCE_INCOMPLETE = 2;
+
+        /**
+         *  The encoded data contained an error.
+         */
+        public static final int SOURCE_MALFORMED_DATA      = 3;
+
+        @Error final int mError;
+        @NonNull final Source mSource;
+
+        DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
+            super(errorMessage(error, cause), cause);
+            mError = error;
+            mSource = source;
+        }
+
+        /**
+         * Private method called by JNI.
+         */
+        @SuppressWarnings("unused")
+        DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
+                @NonNull Source source) {
+            super(msg + errorMessage(error, cause), cause);
+            mError = error;
+            mSource = source;
+        }
+
+        /**
+         *  Retrieve the reason that decoding was interrupted.
+         *
+         *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
+         *  {@link java.lang.Throwable} can be retrieved with
+         *  {@link java.lang.Throwable#getCause}.</p>
+         */
+        @Error
+        public int getError() {
+            return mError;
+        }
+
+        /**
+         *  Retrieve the {@link Source Source} that was interrupted.
+         *
+         *  <p>This can be used for equality checking to find the Source which
+         *  failed to completely decode.</p>
+         */
+        @NonNull
+        public Source getSource() {
+            return mSource;
+        }
+
+        private static String errorMessage(@Error int error, @Nullable Throwable cause) {
+            switch (error) {
+                case SOURCE_EXCEPTION:
+                    return "Exception in input: " + cause;
+                case SOURCE_INCOMPLETE:
+                    return "Input was incomplete.";
+                case SOURCE_MALFORMED_DATA:
+                    return "Input contained an error.";
+                default:
+                    return "";
+            }
+        }
+    }
 }
diff --git a/bridge/src/android/graphics/LinearGradient_Delegate.java b/bridge/src/android/graphics/LinearGradient_Delegate.java
index 477705c..8dcf374 100644
--- a/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -59,22 +59,14 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreate1(LinearGradient thisGradient, long matrix,
-            float x0, float y0, float x1, float y1,
-            int colors[], float positions[], int tileMode) {
+    /*package*/ static long nativeCreate(LinearGradient thisGradient, long matrix,
+            float x0, float y0, float x1, float y1, long[] colors, float[] positions,
+            int tileMode, long colorSpaceHandle) {
         LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(matrix, x0, y0,
                 x1, y1, colors, positions, Shader_Delegate.getTileMode(tileMode));
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate2(LinearGradient thisGradient, long matrix,
-            float x0, float y0, float x1, float y1,
-            int color0, int color1, int tileMode) {
-        return nativeCreate1(thisGradient, matrix, x0, y0, x1, y1, new int[] { color0, color1},
-                null /*positions*/, tileMode);
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
@@ -92,7 +84,7 @@
      * @param tile The Shader tiling mode
      */
     private LinearGradient_Delegate(long nativeMatrix, float x0, float y0, float x1,
-            float y1, int colors[], float positions[], TileMode tile) {
+            float y1, long[] colors, float[] positions, TileMode tile) {
         super(nativeMatrix, colors, positions);
         mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
     }
@@ -111,8 +103,8 @@
         private final float mDy;
         private final float mDSize2;
 
-        public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
-                float positions[], TileMode tile) {
+        public LinearGradientPaint(float x0, float y0, float x1, float y1, int[] colors,
+                float[] positions, TileMode tile) {
             super(colors, positions, tile);
             mX0 = x0;
             mY0 = y0;
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
index 5ae181d..64d8864 100644
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/bridge/src/android/graphics/Matrix_Delegate.java
@@ -616,15 +616,25 @@
             return false;
         }
 
-        try {
-            AffineTransform affineTransform = d.getAffineTransform();
-            AffineTransform inverseTransform = affineTransform.createInverse();
-            setValues(inverseTransform, inv_mtx.mValues);
+        float det = d.mValues[0] * (d.mValues[4] * d.mValues[8] - d.mValues[5] * d.mValues[7])
+                  + d.mValues[1] * (d.mValues[5] * d.mValues[6] - d.mValues[3] * d.mValues[8])
+                  + d.mValues[2] * (d.mValues[3] * d.mValues[7] - d.mValues[4] * d.mValues[6]);
 
-            return true;
-        } catch (NoninvertibleTransformException e) {
+        if (det == 0.0) {
             return false;
         }
+
+        inv_mtx.mValues[0] = (d.mValues[4] * d.mValues[8] - d.mValues[5] * d.mValues[7]) / det;
+        inv_mtx.mValues[1] = (d.mValues[2] * d.mValues[7] - d.mValues[1] * d.mValues[8]) / det;
+        inv_mtx.mValues[2] = (d.mValues[1] * d.mValues[5] - d.mValues[2] * d.mValues[4]) / det;
+        inv_mtx.mValues[3] = (d.mValues[5] * d.mValues[6] - d.mValues[3] * d.mValues[8]) / det;
+        inv_mtx.mValues[4] = (d.mValues[0] * d.mValues[8] - d.mValues[2] * d.mValues[6]) / det;
+        inv_mtx.mValues[5] = (d.mValues[2] * d.mValues[3] - d.mValues[0] * d.mValues[5]) / det;
+        inv_mtx.mValues[6] = (d.mValues[3] * d.mValues[7] - d.mValues[4] * d.mValues[6]) / det;
+        inv_mtx.mValues[7] = (d.mValues[1] * d.mValues[6] - d.mValues[0] * d.mValues[7]) / det;
+        inv_mtx.mValues[8] = (d.mValues[0] * d.mValues[4] - d.mValues[1] * d.mValues[3]) / det;
+
+        return true;
     }
 
     @LayoutlibDelegate
diff --git a/bridge/src/android/graphics/NinePatch_Delegate.java b/bridge/src/android/graphics/NinePatch_Delegate.java
index ce2c18b..a6cd51e 100644
--- a/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -108,12 +108,10 @@
      */
     public static NinePatchChunk getChunk(byte[] array) {
         SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
-        NinePatchChunk chunk = chunkRef.get();
+        NinePatchChunk chunk = chunkRef == null ? null : chunkRef.get();
         if (chunk == null) {
             ByteArrayInputStream bais = new ByteArrayInputStream(array);
-            ObjectInputStream ois = null;
-            try {
-                ois = new ObjectInputStream(bais);
+            try (ObjectInputStream ois = new ObjectInputStream(bais)) {
                 chunk = (NinePatchChunk) ois.readObject();
 
                 // put back the chunk in the cache
@@ -128,13 +126,6 @@
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                         "Failed to deserialize NinePatchChunk class.", e, null /*data*/);
                 return null;
-            } finally {
-                if (ois != null) {
-                    try {
-                        ois.close();
-                    } catch (IOException ignored) {
-                    }
-                }
             }
         }
 
@@ -170,7 +161,8 @@
 
 
     @LayoutlibDelegate
-    /*package*/ static long nativeGetTransparentRegion(Bitmap bitmap, long chunk, Rect location) {
+    /*package*/ static long nativeGetTransparentRegion(long bitmapHandle, long chunk,
+            Rect location) {
         return 0;
     }
 
@@ -182,4 +174,7 @@
         return null;
     }
 
+    public static void clearCache() {
+        sChunkCache.clear();
+    }
 }
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index dae966d..df22d63 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -37,6 +37,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 
@@ -98,7 +99,8 @@
     private float mTextScaleX;
     private float mTextSkewX;
     private int mHintingMode = Paint.HINTING_ON;
-    private int mHyphenEdit;
+    private int mStartHyphenEdit;
+    private int mEndHyphenEdit;
     private float mLetterSpacing;  // not used in actual text rendering.
     private float mWordSpacing;  // not used in actual text rendering.
     // Variant of the font. A paint's variant can only be compact or elegant.
@@ -145,6 +147,7 @@
 
         List<FontInfo> infoList = StreamSupport.stream(typeface.getFonts(mFontVariant).spliterator
                 (), false)
+                .filter(Objects::nonNull)
                 .map(font -> getFontInfo(font, mTextSize, affineTransform))
                 .collect(Collectors.toList());
         mFonts = Collections.unmodifiableList(infoList);
@@ -374,20 +377,18 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetColor(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+    /*package*/ static void nSetColor(long paintPtr, long colorSpaceHandle, long color) {
+        Paint_Delegate delegate = sManager.getDelegate(paintPtr);
         if (delegate == null) {
-            return 0;
+            return;
         }
 
-        return delegate.mColor;
+        delegate.mColor = Color.toArgb(color);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nSetColor(long nativePaint, int color) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+    /*package*/ static void nSetColor(long paintPtr, int color) {
+        Paint_Delegate delegate = sManager.getDelegate(paintPtr);
         if (delegate == null) {
             return;
         }
@@ -396,17 +397,6 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetAlpha(long nativePaint) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
-            return 0;
-        }
-
-        return delegate.getAlpha();
-    }
-
-    @LayoutlibDelegate
     /*package*/ static void nSetAlpha(long nativePaint, int a) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
@@ -462,9 +452,9 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nSetShadowLayer(long paint, float radius, float dx, float dy,
-            int color) {
-        // FIXME
+    /*package*/ static void nSetShadowLayer(long paintPtr,
+            float radius, float dx, float dy, long colorSpaceHandle,
+            long shadowColor) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.setShadowLayer is not supported.", null, null /*data*/);
     }
@@ -1032,7 +1022,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nGetCharArrayBounds(long nativePaint, char[] text, int index,
+    public static void nGetCharArrayBounds(long nativePaint, char[] text, int index,
             int count, int bidiFlags, Rect bounds) {
 
         // get the delegate from the native int.
@@ -1100,21 +1090,39 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetHyphenEdit(long nativePaint) {
+    /*package*/ static int nGetStartHyphenEdit(long nativePaint) {
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return 0;
         }
-        return delegate.mHyphenEdit;
+        return delegate.mStartHyphenEdit;
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nSetHyphenEdit(long nativePaint, int hyphen) {
+    /*package*/ static void nSetStartHyphenEdit(long nativePaint, int hyphen) {
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return;
         }
-        delegate.mHyphenEdit = hyphen;
+        delegate.mStartHyphenEdit = hyphen;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetEndHyphenEdit(long nativePaint) {
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return 0;
+        }
+        return delegate.mEndHyphenEdit;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nSetEndHyphenEdit(long nativePaint, int hyphen) {
+        Paint_Delegate delegate = sManager.getDelegate(nativePaint);
+        if (delegate == null) {
+            return;
+        }
+        delegate.mEndHyphenEdit = hyphen;
     }
 
     @LayoutlibDelegate
diff --git a/bridge/src/android/graphics/Path_Delegate.java b/bridge/src/android/graphics/Path_Delegate.java
index 0979017..2f86026 100644
--- a/bridge/src/android/graphics/Path_Delegate.java
+++ b/bridge/src/android/graphics/Path_Delegate.java
@@ -38,6 +38,8 @@
 import java.awt.geom.RoundRectangle2D;
 import java.util.ArrayList;
 
+import libcore.util.NativeAllocationRegistry_Delegate;
+
 /**
  * Delegate implementing the native methods of android.graphics.Path
  *
@@ -59,6 +61,8 @@
 
     private static final float EPSILON = 1e-4f;
 
+    private static long sFinalizer = -1;
+
     // ---- delegate data ----
     private FillType mFillType = FillType.WINDING;
     private Path2D mPath = new Path2D.Double();
@@ -476,8 +480,14 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nFinalize(long nPath) {
-        sManager.removeJavaReferenceFor(nPath);
+    /*package*/ static long nGetFinalizer() {
+        synchronized (Path_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     @LayoutlibDelegate
diff --git a/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index ff3f19f..0dc6f61 100644
--- a/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -74,7 +74,7 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
+    /*package*/ static long native_CreateBlendModeFilter(int srcColor, int porterDuffMode) {
         PorterDuffColorFilter_Delegate newDelegate =
                 new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
         return sManager.addNewDelegate(newDelegate);
diff --git a/bridge/src/android/graphics/RadialGradient_Delegate.java b/bridge/src/android/graphics/RadialGradient_Delegate.java
index 25521d2..a42d654 100644
--- a/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -59,20 +59,13 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreate1(long matrix, float x, float y, float radius,
-            int colors[], float positions[], int tileMode) {
+    /*package*/ static long nativeCreate(long matrix, float x, float y, float radius,
+            long[] colors, float[] positions, int tileMode, long colorSpaceHandle) {
         RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(matrix, x, y, radius,
                 colors, positions, Shader_Delegate.getTileMode(tileMode));
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate2(long matrix, float x, float y, float radius,
-            int color0, int color1, int tileMode) {
-        return nativeCreate1(matrix, x, y, radius, new int[] { color0, color1 },
-                null /*positions*/, tileMode);
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
@@ -91,7 +84,7 @@
      * @param tile The Shader tiling mode
      */
     private RadialGradient_Delegate(long nativeMatrix, float x, float y, float radius,
-            int colors[], float positions[], TileMode tile) {
+            long[] colors, float[] positions, TileMode tile) {
         super(nativeMatrix, colors, positions);
         mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
     }
diff --git a/bridge/src/android/view/RenderNode_Delegate.java b/bridge/src/android/graphics/RenderNode_Delegate.java
similarity index 98%
rename from bridge/src/android/view/RenderNode_Delegate.java
rename to bridge/src/android/graphics/RenderNode_Delegate.java
index 152878b..ae08b00 100644
--- a/bridge/src/android/view/RenderNode_Delegate.java
+++ b/bridge/src/android/graphics/RenderNode_Delegate.java
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package android.view;
+package android.graphics;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.graphics.Matrix;
-
 import libcore.util.NativeAllocationRegistry_Delegate;
 
 /**
@@ -172,7 +170,7 @@
     /*package*/ static void getMatrix(RenderNode renderNode, Matrix outMatrix) {
         outMatrix.reset();
         if (renderNode != null) {
-            float rotation = renderNode.getRotation();
+            float rotation = renderNode.getRotationZ();
             float translationX = renderNode.getTranslationX();
             float translationY = renderNode.getTranslationY();
             float pivotX = renderNode.getPivotX();
diff --git a/bridge/src/android/graphics/Shader_Delegate.java b/bridge/src/android/graphics/Shader_Delegate.java
index eefa929..d606e2d 100644
--- a/bridge/src/android/graphics/Shader_Delegate.java
+++ b/bridge/src/android/graphics/Shader_Delegate.java
@@ -49,6 +49,7 @@
 
     // ---- delegate data ----
     private Matrix_Delegate mLocalMatrix = null;
+    private float mAlpha = 1.0f;
 
     // ---- Public Helper methods ----
 
@@ -107,4 +108,11 @@
         return new java.awt.geom.AffineTransform();
     }
 
+    public void setAlpha(float alpha) {
+        mAlpha = alpha;
+    }
+
+    public float getAlpha() {
+        return mAlpha;
+    }
 }
diff --git a/bridge/src/android/graphics/SweepGradient_Delegate.java b/bridge/src/android/graphics/SweepGradient_Delegate.java
index 6e8aca3..d29307a 100644
--- a/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -56,19 +56,13 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreate1(long matrix, float x, float y, int colors[], float
-            positions[]) {
+    /*package*/ static long nativeCreate(long matrix, float x, float y, long[] colors,
+            float[] positions, long colorSpaceHandle) {
         SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(matrix, x, y, colors,
                 positions);
         return sManager.addNewDelegate(newDelegate);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static long nativeCreate2(long matrix, float x, float y, int color0, int color1) {
-        return nativeCreate1(matrix, x, y, new int[] { color0, color1 },
-                null /*positions*/);
-    }
-
     // ---- Private delegate/helper methods ----
 
     /**
@@ -87,7 +81,7 @@
      *                 spaced evenly.
      */
     private SweepGradient_Delegate(long nativeMatrix, float cx, float cy,
-            int colors[], float positions[]) {
+            long[] colors, float[] positions) {
         super(nativeMatrix, colors, positions);
         mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
     }
diff --git a/bridge/src/android/graphics/Typeface_Builder_Delegate.java b/bridge/src/android/graphics/Typeface_Builder_Delegate.java
new file mode 100644
index 0000000..9d9d844
--- /dev/null
+++ b/bridge/src/android/graphics/Typeface_Builder_Delegate.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.graphics.Typeface.Builder;
+import android.graphics.fonts.FontVariationAxis;
+
+public class Typeface_Builder_Delegate {
+
+    /**
+     * Creates a unique id for a given AssetManager and asset path.
+     *
+     * @param mgr  AssetManager instance
+     * @param path The path for the asset.
+     * @param ttcIndex The TTC index for the font.
+     * @param axes The font variation settings.
+     * @return Unique id for a given AssetManager and asset path.
+     */
+    @LayoutlibDelegate
+    public static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
+            @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) {
+        return Builder.createAssetUid_Original(mgr, path, ttcIndex, axes, weight, italic, fallback);
+    }
+}
diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java
index d793ade..c728513 100644
--- a/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/bridge/src/android/graphics/Typeface_Delegate.java
@@ -18,12 +18,12 @@
 
 import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.RenderAction;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
@@ -34,27 +34,22 @@
 import android.annotation.Nullable;
 import android.content.res.FontResourcesParser;
 import android.graphics.FontFamily_Delegate.FontVariant;
+import android.graphics.fonts.FontFamily_Builder_Delegate;
 import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
-import android.util.ArrayMap;
 
 import java.awt.Font;
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.lang.ref.SoftReference;
-import java.nio.ByteBuffer;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.ArrayList;
-import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Spliterator;
 import java.util.Spliterators;
 
-import static android.graphics.FontFamily_Delegate.getFontLocation;
+import libcore.util.NativeAllocationRegistry_Delegate;
 
 /**
  * Delegate implementing the native methods of android.graphics.Typeface
@@ -72,25 +67,32 @@
 
     public static final String SYSTEM_FONTS = "/system/fonts/";
 
+    public static final Map<String, FontFamily_Delegate[]> sGenericNativeFamilies = new HashMap<>();
+
     // ---- delegate manager ----
     private static final DelegateManager<Typeface_Delegate> sManager =
             new DelegateManager<>(Typeface_Delegate.class);
-
+    private static long sFinalizer = -1;
 
     // ---- delegate data ----
     private static long sDefaultTypeface;
     @NonNull
     private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
+    @NonNull
+    private final FontFamily_Builder_Delegate[] mFontFamilyBuilders;  // the reference to
+    // FontFamily_Builder_Delegate.
     /** @see Font#getStyle() */
     private final int mStyle;
     private final int mWeight;
-    private SoftReference<EnumMap<FontVariant, List<Font>>> mFontsCache = new SoftReference<>(null);
 
 
     // ---- Public Helper methods ----
 
-    public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) {
+    private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies,
+            @NonNull FontFamily_Builder_Delegate[] fontFamilyBuilders, int style,
+            int weight) {
         mFontFamilies = fontFamilies;
+        mFontFamilyBuilders = fontFamilyBuilders;
         mStyle = style;
         mWeight = weight;
     }
@@ -99,18 +101,6 @@
         return sManager.getDelegate(nativeTypeface);
     }
 
-    /**
-     * Clear the default typefaces when disposing bridge.
-     */
-    public static void resetDefaults() {
-        // Sometimes this is called before the Bridge is initialized. In that case, we don't want to
-        // initialize Typeface because the SDK fonts location hasn't been set.
-        if (FontFamily_Delegate.getFontLocation() != null) {
-            Typeface.sDefaults = null;
-        }
-    }
-
-
     // ---- native methods ----
 
     @LayoutlibDelegate
@@ -124,7 +114,8 @@
         }
 
         return sManager.addNewDelegate(
-                new Typeface_Delegate(delegate.mFontFamilies, style, delegate.mWeight));
+                new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders, style,
+                        delegate.mWeight));
     }
 
     @LayoutlibDelegate
@@ -141,7 +132,8 @@
         int style = weight >= 600 ? (italic ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
                 (italic ? Typeface.ITALIC : Typeface.NORMAL);
         return sManager.addNewDelegate(
-                new Typeface_Delegate(delegate.mFontFamilies, style, weight));
+                new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders, style,
+                        weight));
     }
 
     @LayoutlibDelegate
@@ -172,16 +164,23 @@
             return 0;
         }
         Typeface_Delegate weightAlias =
-                new Typeface_Delegate(delegate.mFontFamilies, delegate.mStyle, weight);
+                new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders,
+                        delegate.mStyle,
+                        weight);
         return sManager.addNewDelegate(weightAlias);
     }
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray, int weight,
             int italic) {
-        FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
-        for (int i = 0; i < familyArray.length; i++) {
-            fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
+        List<FontFamily_Delegate> fontFamilies = new ArrayList<>();
+        List<FontFamily_Builder_Delegate> fontFamilyBuilders = new ArrayList<>();
+        for (long aFamilyArray : familyArray) {
+            try {
+                fontFamilies.add(FontFamily_Delegate.getDelegate(aFamilyArray));
+            } catch (ClassCastException e) {
+                fontFamilyBuilders.add(FontFamily_Builder_Delegate.getDelegate(aFamilyArray));
+            }
         }
         if (weight == Typeface.RESOLVE_BY_FONT_TABLE) {
             weight = 400;
@@ -191,13 +190,22 @@
         }
         int style = weight >= 600 ? (italic == 1 ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
                 (italic == 1 ? Typeface.ITALIC : Typeface.NORMAL);
-        Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, style, weight);
+        Typeface_Delegate delegate =
+                new Typeface_Delegate(fontFamilies.toArray(new FontFamily_Delegate[0]),
+                fontFamilyBuilders.toArray(new FontFamily_Builder_Delegate[0]),
+                style, weight);
         return sManager.addNewDelegate(delegate);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nativeUnref(long native_instance) {
-        sManager.removeJavaReferenceFor(native_instance);
+    /*package*/ static long nativeGetReleaseFunc() {
+        synchronized (Typeface_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     @LayoutlibDelegate
@@ -224,26 +232,6 @@
         return delegate.mWeight;
     }
 
-    @LayoutlibDelegate
-    /*package*/ static void buildSystemFallback(String xmlPath, String fontDir,
-            ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
-        Typeface.buildSystemFallback_Original(getFontLocation() + "/fonts.xml", fontDir, fontMap,
-                fallbackMap);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static FontFamily createFontFamily(String familyName, List<FontConfig.Font> fonts,
-            String[] languageTags, int variant, Map<String, ByteBuffer> cache, String fontDir) {
-        FontFamily fontFamily = new FontFamily(languageTags, variant);
-        for (FontConfig.Font font : fonts) {
-            String fullPathName = fontDir + font.getFontName();
-            FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, fullPathName, font.getWeight(),
-                    font.isItalic());
-        }
-        fontFamily.freeze();
-        return fontFamily;
-    }
-
     /**
      * Loads a single font or font family from disk
      */
@@ -261,27 +249,18 @@
             // create a block parser for the file
             Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
                     RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
-            XmlPullParser parser = null;
+            XmlPullParser parser;
             if (psiParserSupport != null && psiParserSupport) {
-                parser = context.getLayoutlibCallback().getXmlFileParser(path);
+                parser = context.getLayoutlibCallback().createXmlParserForPsiFile(path);
             } else {
-                File f = new File(path);
-                if (f.isFile()) {
-                    try {
-                        parser = ParserFactory.create(f);
-                    } catch (XmlPullParserException | FileNotFoundException e) {
-                        // this is an error and not warning since the file existence is checked
-                        // before
-                        // attempting to parse it.
-                        Bridge.getLog().error(null, "Failed to parse file " + path, e,
-                                null /*data*/);
-                    }
-                }
+                parser = context.getLayoutlibCallback().createXmlParserForFile(path);
             }
 
             if (parser != null) {
+                // TODO(namespaces): The aapt namespace should not matter for parsing font files?
                 BridgeXmlBlockParser blockParser =
-                        new BridgeXmlBlockParser(parser, context, isFramework);
+                        new BridgeXmlBlockParser(
+                                parser, context, ResourceNamespace.fromBoolean(isFramework));
                 try {
                     FontResourcesParser.FamilyResourceEntry entry =
                             FontResourcesParser.parse(blockParser, context.getResources());
@@ -297,7 +276,7 @@
                         null /*data*/);
             }
         } else {
-            typeface = Typeface.createFromResources(context.getAssets(), path, 0);
+            typeface = new Typeface.Builder(context.getAssets(), path).build();
         }
 
         return typeface;
@@ -324,59 +303,17 @@
         return Typeface.create_Original(family, style, isItalic);
     }
 
-    // ---- Private delegate/helper methods ----
-
-    private static List<Font> computeFonts(FontVariant variant, FontFamily_Delegate[] fontFamilies,
-            int inputWeight, int inputStyle) {
-        // Calculate the required weight based on style and weight of this typeface.
-        int weight = inputWeight + 50 +
-                ((inputStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
-        if (weight > 1000) {
-            weight = 1000;
-        } else if (weight < 100) {
-            weight = 100;
+    @LayoutlibDelegate
+    /*package*/ static void nativeRegisterGenericFamily(String str, long nativePtr) {
+        Typeface_Delegate delegate = sManager.getDelegate(nativePtr);
+        if (delegate == null) {
+            return;
         }
-        final boolean isItalic = (inputStyle & Font.ITALIC) != 0;
-        List<Font> fonts = new ArrayList<Font>(fontFamilies.length);
-        for (int i = 0; i < fontFamilies.length; i++) {
-            FontFamily_Delegate ffd = fontFamilies[i];
-            if (ffd != null && ffd.isValid()) {
-                Font font = ffd.getFont(weight, isItalic);
-                if (font != null) {
-                    FontVariant ffdVariant = ffd.getVariant();
-                    if (ffdVariant == FontVariant.NONE) {
-                        fonts.add(font);
-                        continue;
-                    }
-                    // We cannot open each font and get locales supported, etc to match the fonts.
-                    // As a workaround, we hardcode certain assumptions like Elegant and Compact
-                    // always appear in pairs.
-                    assert i < fontFamilies.length - 1;
-                    FontFamily_Delegate ffd2 = fontFamilies[++i];
-                    assert ffd2 != null;
-                    FontVariant ffd2Variant = ffd2.getVariant();
-                    Font font2 = ffd2.getFont(weight, isItalic);
-                    assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant &&
-                            font2 != null;
-                    // Add the font with the matching variant to the list.
-                    if (variant == ffd.getVariant()) {
-                        fonts.add(font);
-                    } else {
-                        fonts.add(font2);
-                    }
-                } else {
-                    // The FontFamily is valid but doesn't contain any matching font. This means
-                    // that the font failed to load. We add null to the list of fonts. Don't throw
-                    // the warning just yet. If this is a non-english font, we don't want to warn
-                    // users who are trying to render only english text.
-                    fonts.add(null);
-                }
-            }
-        }
-
-        return fonts;
+        sGenericNativeFamilies.put(str, delegate.mFontFamilies);
     }
 
+    // ---- Private delegate/helper methods ----
+
     /**
      * Return an Iterable of fonts that match the style and variant. The list is ordered
      * according to preference of fonts.
@@ -392,11 +329,12 @@
     public Iterable<Font> getFonts(final FontVariant variant) {
         assert variant != FontVariant.NONE;
 
-        return new FontsIterator(mFontFamilies, variant, mWeight, mStyle);
+        return new FontsIterator(mFontFamilies, mFontFamilyBuilders, variant, mWeight, mStyle);
     }
 
     private static class FontsIterator implements Iterator<Font>, Iterable<Font> {
         private final FontFamily_Delegate[] fontFamilies;
+        private final FontFamily_Builder_Delegate[] fontFamilyBuilders;
         private final int weight;
         private final boolean isItalic;
         private final FontVariant variant;
@@ -404,6 +342,7 @@
         private int index = 0;
 
         private FontsIterator(@NonNull FontFamily_Delegate[] fontFamilies,
+                @NonNull FontFamily_Builder_Delegate[] fontFamilyBuilders,
                 @NonNull FontVariant variant, int weight, int style) {
             // Calculate the required weight based on style and weight of this typeface.
             int boldExtraWeight =
@@ -411,23 +350,36 @@
             this.weight = Math.min(Math.max(100, weight + 50 + boldExtraWeight), 1000);
             this.isItalic = (style & Font.ITALIC) != 0;
             this.fontFamilies = fontFamilies;
+            this.fontFamilyBuilders = fontFamilyBuilders;
             this.variant = variant;
         }
 
         @Override
         public boolean hasNext() {
-            return index < fontFamilies.length;
+            return index < (fontFamilies.length + fontFamilyBuilders.length);
         }
 
         @Override
         @Nullable
         public Font next() {
-            FontFamily_Delegate ffd = fontFamilies[index++];
-            if (ffd == null || !ffd.isValid()) {
-                return null;
+            Font font;
+            FontVariant ffdVariant;
+            if (index < fontFamilies.length) {
+                FontFamily_Delegate ffd = fontFamilies[index++];
+                if (ffd == null || !ffd.isValid()) {
+                    return null;
+                }
+                font = ffd.getFont(weight, isItalic);
+                ffdVariant = ffd.getVariant();
+            } else {
+                FontFamily_Builder_Delegate ffd = fontFamilyBuilders[index++ - fontFamilies.length];
+                if (ffd == null) {
+                    return null;
+                }
+                font = ffd.getFont(weight, isItalic);
+                ffdVariant = ffd.getVariant();
             }
 
-            Font font = ffd.getFont(weight, isItalic);
             if (font == null) {
                 // The FontFamily is valid but doesn't contain any matching font. This means
                 // that the font failed to load. We add null to the list of fonts. Don't throw
@@ -436,27 +388,26 @@
                 return null;
             }
 
-            FontVariant ffdVariant = ffd.getVariant();
-            if (ffdVariant == FontVariant.NONE) {
+            if (ffdVariant == FontVariant.NONE || ffdVariant == variant) {
                 return font;
             }
 
             // We cannot open each font and get locales supported, etc to match the fonts.
             // As a workaround, we hardcode certain assumptions like Elegant and Compact
             // always appear in pairs.
-            assert index < fontFamilies.length - 1;
-            FontFamily_Delegate ffd2 = fontFamilies[index++];
-            assert ffd2 != null;
+            if (index < fontFamilies.length) {
+                assert index < fontFamilies.length - 1;
+                FontFamily_Delegate ffd2 = fontFamilies[index++];
+                assert ffd2 != null;
 
-            if (ffdVariant == variant) {
-                return font;
+                return ffd2.getFont(weight, isItalic);
+            } else {
+                assert index < fontFamilies.length + fontFamilyBuilders.length - 1;
+                FontFamily_Builder_Delegate ffd2 = fontFamilyBuilders[index++ - fontFamilies.length];
+                assert ffd2 != null;
+
+                return ffd2.getFont(weight, isItalic);
             }
-
-            FontVariant ffd2Variant = ffd2.getVariant();
-            Font font2 = ffd2.getFont(weight, isItalic);
-            assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant && font2 != null;
-            // Add the font with the matching variant to the list.
-            return variant == ffd.getVariant() ? font : font2;
         }
 
         @NonNull
@@ -467,7 +418,8 @@
 
         @Override
         public Spliterator<Font> spliterator() {
-            return Spliterators.spliterator(iterator(), fontFamilies.length,
+            return Spliterators.spliterator(iterator(),
+                    fontFamilies.length + fontFamilyBuilders.length,
                     Spliterator.IMMUTABLE | Spliterator.SIZED);
         }
     }
diff --git a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index d9f8692..8102894 100644
--- a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -32,6 +32,7 @@
 import android.graphics.Paint.Join;
 import android.graphics.Paint_Delegate;
 import android.graphics.Path;
+import android.graphics.Path.FillType;
 import android.graphics.PathMeasure;
 import android.graphics.Path_Delegate;
 import android.graphics.Rect;
@@ -1144,6 +1145,7 @@
             mRenderPath.reset();
 
             if (VPath.isClipPath()) {
+                mRenderPath.setFillType(FillType.WINDING);
                 mRenderPath.addPath(path, mFinalPathMatrix);
                 Canvas_Delegate.nClipPath(canvasPtr, mRenderPath.mNativePath, Op
                         .INTERSECT.nativeInt);
@@ -1194,6 +1196,7 @@
                         // If there is a shader, apply the local transformation to make sure
                         // the gradient is transformed to match the viewport
                         shaderDelegate.setLocalMatrix(mFinalPathMatrix.native_instance);
+                        shaderDelegate.setAlpha(fullPath.mFillAlpha);
                     }
 
                     fillPaintDelegate.setShader(fullPath.mFillGradient);
@@ -1232,6 +1235,11 @@
                     strokePaintDelegate.setColorFilter(filterPtr);
                     final float finalStrokeScale = minScale * matrixScale;
                     strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
+                    Shader_Delegate strokeShaderDelegate =
+                            Shader_Delegate.getDelegate(fullPath.mStrokeGradient);
+                    if (strokeShaderDelegate != null) {
+                        strokeShaderDelegate.setAlpha(fullPath.mStrokeAlpha);
+                    }
                     strokePaintDelegate.setShader(fullPath.mStrokeGradient);
                     BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
                             .getNativeInstance());
diff --git a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java b/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
new file mode 100644
index 0000000..06096ff
--- /dev/null
+++ b/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.FontFamily_Delegate.FontInfo;
+import android.graphics.FontFamily_Delegate.FontVariant;
+import android.graphics.Paint;
+
+import java.awt.Font;
+import java.io.ByteArrayInputStream;
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+import static android.graphics.FontFamily_Delegate.computeMatch;
+import static android.graphics.FontFamily_Delegate.deriveFont;
+
+/**
+ * Delegate implementing the native methods of android.graphics.fonts.FontFamily$Builder
+ * <p>
+ * Through the layoutlib_create tool, the original native methods of FontFamily$Builder have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original FontFamily$Builder class.
+ *
+ * @see DelegateManager
+ */
+public class FontFamily_Builder_Delegate {
+    private static final DelegateManager<FontFamily_Builder_Delegate> sBuilderManager =
+            new DelegateManager<>(FontFamily_Builder_Delegate.class);
+
+    private static long sFontFamilyFinalizer = -1;
+
+    // Order does not really matter but we use a LinkedHashMap to get reproducible results across
+    // render calls
+    private Map<FontInfo, Font> mFonts = new LinkedHashMap<>();
+    /**
+     * The variant of the Font Family - compact or elegant.
+     * <p/>
+     * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
+     * android.graphics.FontFamily
+     *
+     * @see Paint#setElegantTextHeight(boolean)
+     */
+    private FontVariant mVariant;
+    private boolean mIsCustomFallback;
+
+    @LayoutlibDelegate
+    /*package*/ static long nInitBuilder() {
+        return sBuilderManager.addNewDelegate(new FontFamily_Builder_Delegate());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nAddFont(long builderPtr, long fontPtr) {
+        FontFamily_Builder_Delegate builder = sBuilderManager.getDelegate(builderPtr);
+        Font_Builder_Delegate font = Font_Builder_Delegate.sBuilderManager.getDelegate(fontPtr);
+        if (builder != null && font != null) {
+            builder.addFont(font.mBuffer, font.mTtcIndex, font.mWeight, font.mItalic);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nBuild(long builderPtr, String langTags, int variant,
+            boolean isCustomFallback) {
+        FontFamily_Builder_Delegate builder = sBuilderManager.getDelegate(builderPtr);
+        if (builder != null) {
+            assert variant < 3;
+            builder.mVariant = FontVariant.values()[variant];
+            builder.mIsCustomFallback = isCustomFallback;
+        }
+        return builderPtr;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nGetReleaseNativeFamily() {
+        synchronized (Font_Builder_Delegate.class) {
+            if (sFontFamilyFinalizer == -1) {
+                sFontFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sBuilderManager::removeJavaReferenceFor);
+            }
+        }
+        return sFontFamilyFinalizer;
+    }
+
+    public static FontFamily_Builder_Delegate getDelegate(long nativeFontFamily) {
+        return sBuilderManager.getDelegate(nativeFontFamily);
+    }
+
+    @Nullable
+    public Font getFont(int desiredWeight, boolean isItalic) {
+        FontInfo desiredStyle = new FontInfo();
+        desiredStyle.mWeight = desiredWeight;
+        desiredStyle.mIsItalic = isItalic;
+
+        Font cachedFont = mFonts.get(desiredStyle);
+        if (cachedFont != null) {
+            return cachedFont;
+        }
+
+        FontInfo bestFont = null;
+
+        if (mFonts.size() == 1) {
+            // No need to compute the match since we only have one candidate
+            bestFont = mFonts.keySet().iterator().next();
+        } else {
+            int bestMatch = Integer.MAX_VALUE;
+
+            for (FontInfo font : mFonts.keySet()) {
+                int match = computeMatch(font, desiredStyle);
+                if (match < bestMatch) {
+                    bestMatch = match;
+                    bestFont = font;
+                    if (bestMatch == 0) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (bestFont == null) {
+            return null;
+        }
+
+
+        // Derive the font as required and add it to the list of Fonts.
+        deriveFont(bestFont, desiredStyle);
+        addFont(desiredStyle);
+        return desiredStyle.mFont;
+    }
+
+    public FontVariant getVariant() {
+        return mVariant;
+    }
+
+    // ---- private helper methods ----
+
+    private void addFont(final ByteBuffer buffer, int ttcIndex, int weight, boolean italic) {
+        addFont(buffer, weight, italic);
+    }
+
+    private void addFont(@NonNull ByteBuffer buffer, int weight, boolean italic) {
+        // Set valid to true, even if the font fails to load.
+        Font font = loadFont(buffer);
+        if (font == null) {
+            return;
+        }
+        FontInfo fontInfo = new FontInfo();
+        fontInfo.mFont = font;
+        fontInfo.mWeight = weight;
+        fontInfo.mIsItalic = italic;
+        addFont(fontInfo);
+    }
+
+    private void addFont(@NonNull FontInfo fontInfo) {
+        mFonts.putIfAbsent(fontInfo, fontInfo.mFont);
+    }
+
+    private static Font loadFont(@NonNull ByteBuffer buffer) {
+        try {
+            byte[] byteArray = new byte[buffer.limit()];
+            buffer.get(byteArray);
+            buffer.rewind();
+            return Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(byteArray));
+        } catch (Exception e) {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN, "Unable to load font",
+                    e, null);
+        }
+
+        return null;
+    }
+}
diff --git a/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
new file mode 100644
index 0000000..afa7dca
--- /dev/null
+++ b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.content.res.AssetManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+/**
+ * Delegate implementing the native methods of android.graphics.fonts.Font$Builder
+ * <p>
+ * Through the layoutlib_create tool, the original native methods of Font$Builder have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original Font$Builder class.
+ *
+ * @see DelegateManager
+ */
+public class Font_Builder_Delegate {
+    protected static final DelegateManager<Font_Builder_Delegate> sBuilderManager =
+            new DelegateManager<>(Font_Builder_Delegate.class);
+    private static final DelegateManager<String> sAssetManager =
+            new DelegateManager<>(String.class);
+    private static long sFontFinalizer = -1;
+    private static long sAssetFinalizer = -1;
+
+    protected ByteBuffer mBuffer;
+    protected int mWeight;
+    protected boolean mItalic;
+    protected int mTtcIndex;
+    protected String filePath;
+
+    @LayoutlibDelegate
+    /*package*/ static long nInitBuilder() {
+        return sBuilderManager.addNewDelegate(new Font_Builder_Delegate());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nGetNativeAsset(
+            @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie) {
+        return sAssetManager.addNewDelegate(path);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static ByteBuffer nGetAssetBuffer(long nativeAsset) {
+        String fullPath = sAssetManager.getDelegate(nativeAsset);
+        if (fullPath == null) {
+            return null;
+        }
+        try {
+            byte[] byteArray = Files.readAllBytes(new File(fullPath).toPath());
+            return ByteBuffer.wrap(byteArray);
+        } catch (IOException e) {
+            Bridge.getLog().error(LayoutLog.TAG_MISSING_ASSET,
+                    "Error mapping font file " + fullPath, null, null, null);
+            return null;
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nGetReleaseNativeAssetFunc() {
+        synchronized (Font_Builder_Delegate.class) {
+            if (sAssetFinalizer == -1) {
+                sAssetFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sAssetManager::removeJavaReferenceFor);
+            }
+        }
+        return sAssetFinalizer;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nAddAxis(long builderPtr, int tag, float value) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Font$Builder.nAddAxis is not supported.", null, null, null);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nBuild(long builderPtr, ByteBuffer buffer, String filePath, int weight,
+            boolean italic, int ttcIndex) {
+        Font_Builder_Delegate font = sBuilderManager.getDelegate(builderPtr);
+        if (font != null) {
+            font.mBuffer = buffer;
+            font.mWeight = weight;
+            font.mItalic = italic;
+            font.mTtcIndex = ttcIndex;
+            font.filePath = filePath;
+        }
+        return builderPtr;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nGetReleaseNativeFont() {
+        synchronized (Font_Builder_Delegate.class) {
+            if (sFontFinalizer == -1) {
+                sFontFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sBuilderManager::removeJavaReferenceFor);
+            }
+        }
+        return sFontFinalizer;
+    }
+}
diff --git a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java b/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
new file mode 100644
index 0000000..6937093
--- /dev/null
+++ b/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.text.FontConfig;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+
+import static android.graphics.FontFamily_Delegate.getFontLocation;
+
+/**
+ * Delegate implementing the native methods of android.graphics.fonts.SystemFonts
+ * <p>
+ * Through the layoutlib_create tool, the original native methods of SystemFonts have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original SystemFonts class.
+ *
+ * @see DelegateManager
+ */
+public class SystemFonts_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
+            @NonNull String fontDir,
+            @NonNull FontCustomizationParser.Result oemCustomization,
+            @NonNull ArrayMap<String, FontFamily[]> fallbackMap,
+            @NonNull ArrayList<Font> availableFonts) {
+        Bridge.sIsTypefaceInitialized = true;
+        return SystemFonts.buildSystemFallback_Original(getFontLocation() + "/fonts.xml",
+                getFontLocation() + "/", oemCustomization, fallbackMap, availableFonts);
+    }
+}
diff --git a/bridge/src/android/text/LineBreaker.java b/bridge/src/android/graphics/text/BaseLineBreaker.java
similarity index 60%
rename from bridge/src/android/text/LineBreaker.java
rename to bridge/src/android/graphics/text/BaseLineBreaker.java
index 06e9c84..61fa216 100644
--- a/bridge/src/android/text/LineBreaker.java
+++ b/bridge/src/android/graphics/text/BaseLineBreaker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,30 +14,40 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import android.annotation.NonNull;
-import android.text.StaticLayout.LineBreaks;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 // Based on the native implementation of LineBreaker in
 // frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public abstract class LineBreaker {
+public abstract class BaseLineBreaker {
 
     protected static final int TAB_MASK   = 0x20000000;  // keep in sync with StaticLayout
 
     protected final @NonNull List<Primitive> mPrimitives;
-    protected final @NonNull LineWidth mLineWidth;
-    protected final @NonNull TabStops mTabStops;
+    protected final @NonNull
+    LineWidth mLineWidth;
+    protected final @NonNull
+    TabStops mTabStops;
 
-    public LineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
+    public BaseLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
             @NonNull TabStops tabStops) {
         mPrimitives = Collections.unmodifiableList(primitives);
         mLineWidth = lineWidth;
         mTabStops = tabStops;
     }
 
-    public abstract void computeBreaks(@NonNull LineBreaks breakInfo);
+    public abstract Result computeBreaks();
+
+    public static class Result {
+        List<Integer> mLineBreakOffset = new ArrayList<>();
+        List<Float> mLineWidths = new ArrayList<>();
+        List<Float> mLineAscents = new ArrayList<>();
+        List<Float> mLineDescents = new ArrayList<>();
+        List<Integer> mLineFlags = new ArrayList<>();
+    }
 }
diff --git a/bridge/src/android/text/GreedyLineBreaker.java b/bridge/src/android/graphics/text/GreedyLineBreaker.java
similarity index 67%
rename from bridge/src/android/text/GreedyLineBreaker.java
rename to bridge/src/android/graphics/text/GreedyLineBreaker.java
index 50289e9..940e235 100644
--- a/bridge/src/android/text/GreedyLineBreaker.java
+++ b/bridge/src/android/graphics/text/GreedyLineBreaker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,20 +14,18 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import android.annotation.NonNull;
-import android.text.Primitive.PrimitiveType;
-import android.text.StaticLayout.LineBreaks;
+import android.graphics.text.Primitive.PrimitiveType;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY;
+import static android.graphics.text.Primitive.PrimitiveType.PENALTY_INFINITY;
 
 // Based on the native implementation of GreedyLineBreaker in
 // frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class GreedyLineBreaker extends LineBreaker {
+public class GreedyLineBreaker extends BaseLineBreaker {
 
     public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
             @NonNull TabStops tabStops) {
@@ -35,8 +33,7 @@
     }
 
     @Override
-    public void computeBreaks(@NonNull LineBreaks lineBreaks) {
-        BreakInfo breakInfo = new BreakInfo();
+    public Result computeBreaks() {
         int lineNum = 0;
         float width = 0, printedWidth = 0;
         boolean breakFound = false, goodBreakFound = false;
@@ -47,6 +44,7 @@
         float maxWidth = mLineWidth.getLineWidth(lineNum);
 
         int numPrimitives = mPrimitives.size();
+        Result result = new Result();
         // greedily fit as many characters as possible on each line
         // loop over all primitives, and choose the best break point
         // (if possible, a break point without splitting a word)
@@ -77,18 +75,22 @@
                         i = goodBreakIndex; // no +1 because of i++
                         lineNum++;
                         maxWidth = mLineWidth.getLineWidth(lineNum);
-                        breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location);
-                        breakInfo.mWidthsList.add(goodBreakWidth);
-                        breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex);
+                        result.mLineBreakOffset.add(mPrimitives.get(goodBreakIndex).location);
+                        result.mLineWidths.add(goodBreakWidth);
+                        result.mLineAscents.add(0f);
+                        result.mLineDescents.add(0f);
+                        result.mLineFlags.add(firstTabIndex < goodBreakIndex ? TAB_MASK : 0);
                         firstTabIndex = Integer.MAX_VALUE;
                     } else {
                         // must split a word because there is no other option
                         i = breakIndex; // no +1 because of i++
                         lineNum++;
                         maxWidth = mLineWidth.getLineWidth(lineNum);
-                        breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location);
-                        breakInfo.mWidthsList.add(breakWidth);
-                        breakInfo.mFlagsList.add(firstTabIndex < breakIndex);
+                        result.mLineBreakOffset.add(mPrimitives.get(breakIndex).location);
+                        result.mLineWidths.add(breakWidth);
+                        result.mLineAscents.add(0f);
+                        result.mLineDescents.add(0f);
+                        result.mLineFlags.add(firstTabIndex < breakIndex ? TAB_MASK : 0);
                         firstTabIndex = Integer.MAX_VALUE;
                     }
                     printedWidth = width = 0;
@@ -111,9 +113,11 @@
                 if (p.penalty == -PENALTY_INFINITY) {
                     lineNum++;
                     maxWidth = mLineWidth.getLineWidth(lineNum);
-                    breakInfo.mBreaksList.add(p.location);
-                    breakInfo.mWidthsList.add(printedWidth);
-                    breakInfo.mFlagsList.add(firstTabIndex < i);
+                    result.mLineBreakOffset.add(p.location);
+                    result.mLineWidths.add(printedWidth);
+                    result.mLineAscents.add(0f);
+                    result.mLineDescents.add(0f);
+                    result.mLineFlags.add(firstTabIndex < i ? TAB_MASK : 0);
                     firstTabIndex = Integer.MAX_VALUE;
                     printedWidth = width = 0;
                     goodBreakFound = breakFound = false;
@@ -144,49 +148,19 @@
         if (breakFound || goodBreakFound) {
             // output last break if there are more characters to output
             if (goodBreakFound) {
-                breakInfo.mBreaksList.add(mPrimitives.get(goodBreakIndex).location);
-                breakInfo.mWidthsList.add(goodBreakWidth);
-                breakInfo.mFlagsList.add(firstTabIndex < goodBreakIndex);
+                result.mLineBreakOffset.add(mPrimitives.get(goodBreakIndex).location);
+                result.mLineWidths.add(goodBreakWidth);
+                result.mLineAscents.add(0f);
+                result.mLineDescents.add(0f);
+                result.mLineFlags.add(firstTabIndex < goodBreakIndex ? TAB_MASK : 0);
             } else {
-                breakInfo.mBreaksList.add(mPrimitives.get(breakIndex).location);
-                breakInfo.mWidthsList.add(breakWidth);
-                breakInfo.mFlagsList.add(firstTabIndex < breakIndex);
+                result.mLineBreakOffset.add(mPrimitives.get(breakIndex).location);
+                result.mLineWidths.add(breakWidth);
+                result.mLineAscents.add(0f);
+                result.mLineDescents.add(0f);
+                result.mLineFlags.add(firstTabIndex < breakIndex ? TAB_MASK : 0);
             }
         }
-        breakInfo.copyTo(lineBreaks);
-    }
-
-    private static class BreakInfo {
-        List<Integer> mBreaksList = new ArrayList<Integer>();
-        List<Float> mWidthsList = new ArrayList<Float>();
-        List<Boolean> mFlagsList = new ArrayList<Boolean>();
-
-        public void copyTo(LineBreaks lineBreaks) {
-            if (lineBreaks.breaks.length != mBreaksList.size()) {
-                lineBreaks.breaks = new int[mBreaksList.size()];
-                lineBreaks.widths = new float[mWidthsList.size()];
-                lineBreaks.flags = new int[mFlagsList.size()];
-            }
-
-            int i = 0;
-            for (int b : mBreaksList) {
-                lineBreaks.breaks[i] = b;
-                i++;
-            }
-            i = 0;
-            for (float b : mWidthsList) {
-                lineBreaks.widths[i] = b;
-                i++;
-            }
-            i = 0;
-            for (boolean b : mFlagsList) {
-                lineBreaks.flags[i] = b ? TAB_MASK : 0;
-                i++;
-            }
-
-            mBreaksList = null;
-            mWidthsList = null;
-            mFlagsList = null;
-        }
+        return result;
     }
 }
diff --git a/bridge/src/android/text/StaticLayout_Delegate.java b/bridge/src/android/graphics/text/LineBreaker_Delegate.java
similarity index 61%
rename from bridge/src/android/text/StaticLayout_Delegate.java
rename to bridge/src/android/graphics/text/LineBreaker_Delegate.java
index d7cb596..92c99cc 100644
--- a/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/bridge/src/android/graphics/text/LineBreaker_Delegate.java
@@ -1,4 +1,20 @@
-package android.text;
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -6,16 +22,17 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.icu.text.BreakIterator;
+import android.text.Layout;
 import android.text.Layout.BreakStrategy;
 import android.text.Layout.HyphenationFrequency;
-import android.text.Primitive.PrimitiveType;
-import android.text.StaticLayout.LineBreaks;
+import android.graphics.text.Primitive.PrimitiveType;
 
 import java.text.CharacterIterator;
 import java.util.ArrayList;
 import java.util.List;
 
 import javax.swing.text.Segment;
+import libcore.util.NativeAllocationRegistry_Delegate;
 
 /**
  * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
@@ -24,7 +41,7 @@
  * by calls to methods of the same name in this delegate class.
  *
  */
-public class StaticLayout_Delegate {
+public class LineBreaker_Delegate {
 
     private static final char CHAR_SPACE     = 0x20;
     private static final char CHAR_TAB       = 0x09;
@@ -33,28 +50,38 @@
 
     // ---- Builder delegate manager ----
     private static final DelegateManager<Builder> sBuilderManager =
-        new DelegateManager<Builder>(Builder.class);
+        new DelegateManager<>(Builder.class);
+    private static long sFinalizer = -1;
+
+    // ---- Result delegate manager ----
+    private static final DelegateManager<Result> sResultManager =
+        new DelegateManager<>(Result.class);
+    private static long sResultFinalizer = -1;
 
     @LayoutlibDelegate
     /*package*/ static long nInit(
             @BreakStrategy int breakStrategy,
             @HyphenationFrequency int hyphenationFrequency,
             boolean isJustified,
-            @Nullable int[] indents,
-            @Nullable int[] leftPaddings,
-            @Nullable int[] rightPaddings) {
+            @Nullable int[] indents) {
         Builder builder = new Builder();
         builder.mBreakStrategy = breakStrategy;
         return sBuilderManager.addNewDelegate(builder);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nFinish(long nativePtr) {
-        sBuilderManager.removeJavaReferenceFor(nativePtr);
+    /*package*/ static long nGetReleaseFunc() {
+        synchronized (MeasuredText_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sBuilderManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nComputeLineBreaks(
+    /*package*/ static long nComputeLineBreaks(
             /* non zero */ long nativePtr,
 
             // Inputs
@@ -64,19 +91,9 @@
             float firstWidth,
             int firstWidthLineCount,
             float restWidth,
-            @Nullable int[] variableTabStops,
-            int defaultTabStop,
-            int indentsOffset,
-
-            // Outputs
-            @NonNull LineBreaks recycle,
-            int recycleLength,
-            @NonNull int[] recycleBreaks,
-            @NonNull float[] recycleWidths,
-            @NonNull float[] recycleAscents,
-            @NonNull float[] recycleDescents,
-            @NonNull int[] recycleFlags,
-            @NonNull float[] charWidths) {
+            @Nullable float[] variableTabStops,
+            float defaultTabStop,
+            int indentsOffset) {
         Builder builder = sBuilderManager.getDelegate(nativePtr);
         if (builder == null) {
             return 0;
@@ -87,7 +104,7 @@
         builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
         builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
 
-        MeasuredParagraph_Delegate.computeRuns(measuredTextPtr, builder);
+        MeasuredText_Delegate.computeRuns(measuredTextPtr, builder);
 
         // compute all possible breakpoints.
         BreakIterator it = BreakIterator.getLineInstance();
@@ -120,9 +137,55 @@
                 builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
                         builder.mTabStopCalculator);
         }
-        builder.mLineBreaker.computeBreaks(recycle);
-        System.arraycopy(builder.mWidths, 0, charWidths, 0, builder.mWidths.length);
-        return recycle.breaks.length;
+        Result result = new Result(builder.mLineBreaker.computeBreaks());
+        return sResultManager.addNewDelegate(result);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetLineCount(long ptr) {
+        Result result = sResultManager.getDelegate(ptr);
+        return result.mResult.mLineBreakOffset.size();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetLineBreakOffset(long ptr, int idx) {
+        Result result = sResultManager.getDelegate(ptr);
+        return result.mResult.mLineBreakOffset.get(idx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetLineWidth(long ptr, int idx) {
+        Result result = sResultManager.getDelegate(ptr);
+        return result.mResult.mLineWidths.get(idx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetLineAscent(long ptr, int idx) {
+        Result result = sResultManager.getDelegate(ptr);
+        return result.mResult.mLineAscents.get(idx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetLineDescent(long ptr, int idx) {
+        Result result = sResultManager.getDelegate(ptr);
+        return result.mResult.mLineDescents.get(idx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetLineFlag(long ptr, int idx) {
+        Result result = sResultManager.getDelegate(ptr);
+        return result.mResult.mLineFlags.get(idx);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nGetReleaseResultFunc() {
+        synchronized (MeasuredText_Delegate.class) {
+            if (sResultFinalizer == -1) {
+                sResultFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sBuilderManager::removeJavaReferenceFor);
+            }
+        }
+        return sResultFinalizer;
     }
 
     /**
@@ -173,7 +236,7 @@
     public static class Builder {
         char[] mText;
         float[] mWidths;
-        private LineBreaker mLineBreaker;
+        private BaseLineBreaker mLineBreaker;
         private int mBreakStrategy;
         private LineWidth mLineWidth;
         private TabStops mTabStopCalculator;
@@ -190,4 +253,11 @@
 
         abstract void addTo(Builder builder);
     }
+
+    public static class Result {
+        final BaseLineBreaker.Result mResult;
+        public Result(BaseLineBreaker.Result result) {
+            mResult = result;
+        }
+    }
 }
diff --git a/bridge/src/android/text/LineWidth.java b/bridge/src/android/graphics/text/LineWidth.java
similarity index 92%
rename from bridge/src/android/text/LineWidth.java
rename to bridge/src/android/graphics/text/LineWidth.java
index 2ea886d..809b967 100644
--- a/bridge/src/android/text/LineWidth.java
+++ b/bridge/src/android/graphics/text/LineWidth.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 // Based on the native implementation of LineWidth in
 // frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
diff --git a/bridge/src/android/text/MeasuredParagraph_Delegate.java b/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
similarity index 60%
rename from bridge/src/android/text/MeasuredParagraph_Delegate.java
rename to bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
index 4694890..f9abbe1 100644
--- a/bridge/src/android/text/MeasuredParagraph_Delegate.java
+++ b/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -24,41 +24,37 @@
 import android.graphics.Paint;
 import android.graphics.Paint_Delegate;
 import android.graphics.RectF;
-import android.text.StaticLayout_Delegate.Builder;
-import android.text.StaticLayout_Delegate.Run;
+import android.graphics.text.LineBreaker_Delegate.Builder;
+import android.graphics.text.LineBreaker_Delegate.Run;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 
-import libcore.util.NativeAllocationRegistry_Delegate;
-
 /**
- * Delegate that provides implementation for native methods in {@link android.text.MeasuredParagraph}
+ * Delegate that provides implementation for native methods in
+ * {@link android.graphics.text.MeasuredText}
  * <p/>
  * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
  * by calls to methods of the same name in this delegate class.
  *
  */
-public class MeasuredParagraph_Delegate {
-
+public class MeasuredText_Builder_Delegate {
     // ---- Builder delegate manager ----
-    private static final DelegateManager<MeasuredParagraphBuilder> sBuilderManager =
-            new DelegateManager<>(MeasuredParagraphBuilder.class);
-    private static final DelegateManager<MeasuredParagraph_Delegate> sManager =
-            new DelegateManager<>(MeasuredParagraph_Delegate.class);
-    private static long sFinalizer = -1;
+    protected static final DelegateManager<MeasuredText_Builder_Delegate>
+            sBuilderManager =
+            new DelegateManager<>(MeasuredText_Builder_Delegate.class);
 
-    private long mNativeBuilderPtr;
+    protected final ArrayList<Run> mRuns = new ArrayList<>();
 
     @LayoutlibDelegate
     /*package*/ static long nInitBuilder() {
-        return sBuilderManager.addNewDelegate(new MeasuredParagraphBuilder());
+        return sBuilderManager.addNewDelegate(new MeasuredText_Builder_Delegate());
     }
 
     /**
      * Apply style to make native measured text.
      *
-     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+     * @param nativeBuilderPtr The native NativeMeasuredParagraph builder pointer.
      * @param paintPtr The native paint pointer to be applied.
      * @param start The start offset in the copied buffer.
      * @param end The end offset in the copied buffer.
@@ -67,7 +63,7 @@
     @LayoutlibDelegate
     /*package*/ static void nAddStyleRun(long nativeBuilderPtr, long paintPtr, int start,
             int end, boolean isRtl) {
-        MeasuredParagraphBuilder builder = sBuilderManager.getDelegate(nativeBuilderPtr);
+        MeasuredText_Builder_Delegate builder = sBuilderManager.getDelegate(nativeBuilderPtr);
         if (builder == null) {
             return;
         }
@@ -77,7 +73,7 @@
     /**
      * Apply ReplacementRun to make native measured text.
      *
-     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+     * @param nativeBuilderPtr The native NativeMeasuredParagraph builder pointer.
      * @param paintPtr The native paint pointer to be applied.
      * @param start The start offset in the copied buffer.
      * @param end The end offset in the copied buffer.
@@ -86,7 +82,7 @@
     @LayoutlibDelegate
     /*package*/ static void nAddReplacementRun(long nativeBuilderPtr, long paintPtr, int start,
             int end, float width) {
-        MeasuredParagraphBuilder builder = sBuilderManager.getDelegate(nativeBuilderPtr);
+        MeasuredText_Builder_Delegate builder = sBuilderManager.getDelegate(nativeBuilderPtr);
         if (builder == null) {
             return;
         }
@@ -94,11 +90,11 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nBuildNativeMeasuredParagraph(long nativeBuilderPtr,
+    /*package*/ static long nBuildMeasuredText(long nativeBuilderPtr, long hintMtPtr,
             @NonNull char[] text, boolean computeHyphenation, boolean computeLayout) {
-        MeasuredParagraph_Delegate delegate = new MeasuredParagraph_Delegate();
+        MeasuredText_Delegate delegate = new MeasuredText_Delegate();
         delegate.mNativeBuilderPtr = nativeBuilderPtr;
-        return sManager.addNewDelegate(delegate);
+        return MeasuredText_Delegate.sManager.addNewDelegate(delegate);
     }
 
     @LayoutlibDelegate
@@ -106,29 +102,6 @@
         sBuilderManager.removeJavaReferenceFor(nativeBuilderPtr);
     }
 
-    @LayoutlibDelegate
-    /*package*/ static float nGetWidth(long nativePtr, int start, int end) {
-        // Ignore as it is not used for the layoutlib implementation
-        return 0.0f;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static long nGetReleaseFunc() {
-        synchronized (MeasuredParagraph_Delegate.class) {
-            if (sFinalizer == -1) {
-                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
-                        sManager::removeJavaReferenceFor);
-            }
-        }
-        return sFinalizer;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nGetMemoryUsage(long nativePtr) {
-        // Ignore as it is not used for the layoutlib implementation
-        return 0;
-    }
-
     private static float measureText(long nativePaint, char[] text, int index, int count,
             float[] widths, int bidiFlags) {
         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
@@ -138,20 +111,6 @@
         return bounds.right - bounds.left;
     }
 
-    public static void computeRuns(long measuredTextPtr, Builder staticLayoutBuilder) {
-        MeasuredParagraph_Delegate delegate = sManager.getDelegate(measuredTextPtr);
-        if (delegate == null) {
-            return;
-        }
-        MeasuredParagraphBuilder builder = sBuilderManager.getDelegate(delegate.mNativeBuilderPtr);
-        if (builder == null) {
-            return;
-        }
-        for (Run run: builder.mRuns) {
-            run.addTo(staticLayoutBuilder);
-        }
-    }
-
     private static class StyleRun extends Run {
         private final long mNativePaint;
         private final boolean mIsRtl;
@@ -184,8 +143,4 @@
             Arrays.fill(builder.mWidths, mStart + 1, mEnd, 0.0f);
         }
     }
-
-    private static class MeasuredParagraphBuilder {
-        private final ArrayList<Run> mRuns = new ArrayList<>();
-    }
 }
diff --git a/bridge/src/android/graphics/text/MeasuredText_Delegate.java b/bridge/src/android/graphics/text/MeasuredText_Delegate.java
new file mode 100644
index 0000000..02e2845
--- /dev/null
+++ b/bridge/src/android/graphics/text/MeasuredText_Delegate.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Rect;
+import android.graphics.text.LineBreaker_Delegate.Builder;
+import android.graphics.text.LineBreaker_Delegate.Run;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+/**
+ * Delegate that provides implementation for native methods in
+ * {@link android.graphics.text.MeasuredText}
+ * <p/>
+ * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class MeasuredText_Delegate {
+
+    // ---- Builder delegate manager ----
+    protected static final DelegateManager<MeasuredText_Delegate> sManager =
+            new DelegateManager<>(MeasuredText_Delegate.class);
+    private static long sFinalizer = -1;
+
+    protected long mNativeBuilderPtr;
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetWidth(long nativePtr, int start, int end) {
+        // Ignore as it is not used for the layoutlib implementation
+        return 0.0f;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nGetReleaseFunc() {
+        synchronized (MeasuredText_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static int nGetMemoryUsage(long nativePtr) {
+        // Ignore as it is not used for the layoutlib implementation
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nGetBounds(long nativePtr, char[] buf, int start, int end, Rect rect) {
+        // Ignore as it is not used for the layoutlib implementation
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static float nGetCharWidthAt(long nativePtr, int offset) {
+        // Ignore as it is not used for the layoutlib implementation
+        return 0.0f;
+    }
+
+    public static void computeRuns(long measuredTextPtr, Builder staticLayoutBuilder) {
+        MeasuredText_Delegate delegate = sManager.getDelegate(measuredTextPtr);
+        if (delegate == null) {
+            return;
+        }
+        MeasuredText_Builder_Delegate builder =
+                MeasuredText_Builder_Delegate.sBuilderManager.getDelegate(delegate.mNativeBuilderPtr);
+        if (builder == null) {
+            return;
+        }
+        for (Run run: builder.mRuns) {
+            run.addTo(staticLayoutBuilder);
+        }
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/text/OptimizingLineBreaker.java b/bridge/src/android/graphics/text/OptimizingLineBreaker.java
similarity index 84%
rename from bridge/src/android/text/OptimizingLineBreaker.java
rename to bridge/src/android/graphics/text/OptimizingLineBreaker.java
index ed8e33a..95e0920 100644
--- a/bridge/src/android/text/OptimizingLineBreaker.java
+++ b/bridge/src/android/graphics/text/OptimizingLineBreaker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import android.annotation.NonNull;
-import android.text.Primitive.PrimitiveType;
-import android.text.StaticLayout.LineBreaks;
+import android.graphics.text.Primitive.PrimitiveType;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
 
-import static android.text.Primitive.PrimitiveType.PENALTY_INFINITY;
+import static android.graphics.text.Primitive.PrimitiveType.PENALTY_INFINITY;
 
 
 // Based on the native implementation of OptimizingLineBreaker in
@@ -33,7 +33,7 @@
  * A more complex version of line breaking where we try to prevent the right edge from being too
  * jagged.
  */
-public class OptimizingLineBreaker extends LineBreaker {
+public class OptimizingLineBreaker extends BaseLineBreaker {
 
     public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
             @NonNull TabStops tabStops) {
@@ -41,17 +41,20 @@
     }
 
     @Override
-    public void computeBreaks(@NonNull LineBreaks breakInfo) {
+    public Result computeBreaks() {
+        Result result = new Result();
         int numBreaks = mPrimitives.size();
         assert numBreaks > 0;
         if (numBreaks == 1) {
             // This can be true only if it's an empty paragraph.
             Primitive p = mPrimitives.get(0);
             assert p.type == PrimitiveType.PENALTY;
-            breakInfo.breaks = new int[]{0};
-            breakInfo.widths = new float[]{p.width};
-            breakInfo.flags = new int[]{0};
-            return;
+            result.mLineBreakOffset.add(0);
+            result.mLineWidths.add(p.width);
+            result.mLineAscents.add(0f);
+            result.mLineDescents.add(0f);
+            result.mLineFlags.add(0);
+            return result;
         }
         Node[] opt = new Node[numBreaks];
         opt[0] = new Node(-1, 0, 0, 0, false);
@@ -120,35 +123,21 @@
         }
 
         int idx = numBreaks - 1;
-        int count = opt[idx].mPrevCount;
-        resize(breakInfo, count);
         while (opt[idx].mPrev != -1) {
-            count--;
-            assert count >=0;
-
-            breakInfo.breaks[count] = mPrimitives.get(idx).location;
-            breakInfo.widths[count] = opt[idx].mWidth;
-            breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0;
+            result.mLineBreakOffset.add(mPrimitives.get(idx).location);
+            result.mLineWidths.add(opt[idx].mWidth);
+            result.mLineAscents.add(0f);
+            result.mLineDescents.add(0f);
+            result.mLineFlags.add(opt[idx].mHasTabs ? TAB_MASK : 0);
             idx = opt[idx].mPrev;
         }
-    }
 
-    private static void resize(LineBreaks lineBreaks, int size) {
-        if (lineBreaks.breaks.length == size) {
-            return;
-        }
-        int[] breaks = new int[size];
-        float[] widths = new float[size];
-        int[] flags = new int[size];
-
-        int toCopy = Math.min(size, lineBreaks.breaks.length);
-        System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy);
-        System.arraycopy(lineBreaks.widths, 0, widths, 0, toCopy);
-        System.arraycopy(lineBreaks.flags, 0, flags, 0, toCopy);
-
-        lineBreaks.breaks = breaks;
-        lineBreaks.widths = widths;
-        lineBreaks.flags = flags;
+        Collections.reverse(result.mLineBreakOffset);
+        Collections.reverse(result.mLineWidths);
+        Collections.reverse(result.mLineAscents);
+        Collections.reverse(result.mLineDescents);
+        Collections.reverse(result.mLineFlags);
+        return result;
     }
 
     @NonNull
diff --git a/bridge/src/android/text/Primitive.java b/bridge/src/android/graphics/text/Primitive.java
similarity index 96%
rename from bridge/src/android/text/Primitive.java
rename to bridge/src/android/graphics/text/Primitive.java
index 37ed072..b8157ec 100644
--- a/bridge/src/android/text/Primitive.java
+++ b/bridge/src/android/graphics/text/Primitive.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import android.annotation.NonNull;
 
diff --git a/bridge/src/android/text/TabStops.java b/bridge/src/android/graphics/text/TabStops.java
similarity index 72%
rename from bridge/src/android/text/TabStops.java
rename to bridge/src/android/graphics/text/TabStops.java
index 6c2f1e1..6ede24c 100644
--- a/bridge/src/android/text/TabStops.java
+++ b/bridge/src/android/graphics/text/TabStops.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import android.annotation.Nullable;
 
@@ -22,23 +22,23 @@
 // frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
 public class TabStops {
     @Nullable
-    private int[] mStops;
-    private final int mTabWidth;
+    private float[] mStops;
+    private final float mTabWidth;
 
-    public TabStops(@Nullable int[] stops, int defaultTabWidth) {
+    public TabStops(@Nullable float[] stops, float defaultTabWidth) {
         mTabWidth = defaultTabWidth;
         mStops = stops;
     }
 
     public float width(float widthSoFar) {
         if (mStops != null) {
-            for (int i : mStops) {
-                if (i > widthSoFar) {
-                    return i;
+            for (float f : mStops) {
+                if (f > widthSoFar) {
+                    return f;
                 }
             }
         }
         // find the next tabStop after widthSoFar.
-        return (int) ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
+        return ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
     }
 }
diff --git a/bridge/src/android/provider/DeviceConfig_Delegate.java b/bridge/src/android/provider/DeviceConfig_Delegate.java
new file mode 100644
index 0000000..daf86dc
--- /dev/null
+++ b/bridge/src/android/provider/DeviceConfig_Delegate.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate that provides alternative implementation for methods in {@link DeviceConfig}
+ * <p/>
+ * Through the layoutlib_create tool, selected methods of DeviceConfig have been replaced by
+ * calls to methods of the same name in this delegate class.
+ */
+public class DeviceConfig_Delegate {
+
+    @LayoutlibDelegate
+    public static String getString(String namespace, String name, String defaultValue) {
+        return defaultValue;
+    }
+
+    @LayoutlibDelegate
+    public static boolean getBoolean(String namespace, String name, boolean defaultValue) {
+        return defaultValue;
+    }
+
+    @LayoutlibDelegate
+    public static int getInt(String namespace, String name, int defaultValue) {
+        return defaultValue;
+    }
+
+    @LayoutlibDelegate
+    public static long getLong(String namespace, String name, long defaultValue) {
+        return defaultValue;
+    }
+
+    @LayoutlibDelegate
+    public static float getFloat(String namespace, String name, float defaultValue) {
+        return defaultValue;
+    }
+}
diff --git a/bridge/src/android/util/BridgeXmlPullAttributes.java b/bridge/src/android/util/BridgeXmlPullAttributes.java
index f1af1d1..2f4b0ca 100644
--- a/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -13,22 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.util;
 
 import com.android.ide.common.rendering.api.AttrResourceValue;
-import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
+import com.android.layoutlib.bridge.android.XmlPullParserResolver;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParser;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import java.util.Map;
 import java.util.function.Function;
@@ -36,33 +39,48 @@
 /**
  * A correct implementation of the {@link AttributeSet} interface on top of a XmlPullParser
  */
-public class BridgeXmlPullAttributes extends XmlPullAttributes {
+public class BridgeXmlPullAttributes extends XmlPullAttributes implements ResolvingAttributeSet {
 
-    private final BridgeContext mContext;
-    private final boolean mPlatformFile;
-    private final Function<String, Map<String, Integer>> mFrameworkEnumValueSupplier;
-    private final Function<String, Map<String, Integer>> mProjectEnumValueSupplier;
-
-    // VisibleForTesting
-    BridgeXmlPullAttributes(@NonNull XmlPullParser parser, @NonNull BridgeContext context,
-            boolean platformFile,
-            @NonNull Function<String, Map<String, Integer>> frameworkEnumValueSupplier,
-            @NonNull Function<String, Map<String, Integer>> projectEnumValueSupplier) {
-        super(parser);
-        mContext = context;
-        mPlatformFile = platformFile;
-        mFrameworkEnumValueSupplier = frameworkEnumValueSupplier;
-        mProjectEnumValueSupplier = projectEnumValueSupplier;
+    interface EnumValueSupplier {
+        @Nullable
+        Map<String, Integer> getEnumValues(
+                @NonNull ResourceNamespace namespace, @NonNull String attrName);
     }
 
-    public BridgeXmlPullAttributes(@NonNull XmlPullParser parser, @NonNull BridgeContext context,
-            boolean platformFile) {
-        this(parser, context, platformFile, Bridge::getEnumValues, attrName -> {
-            // get the styleable matching the resolved name
-            RenderResources res = context.getRenderResources();
-            ResourceValue attr = res.getProjectResource(ResourceType.ATTR, attrName);
-            return attr instanceof AttrResourceValue ?
-                    ((AttrResourceValue) attr).getAttributeValues() : null;
+    private final BridgeContext mContext;
+    private final ResourceNamespace mXmlFileResourceNamespace;
+    private final ResourceNamespace.Resolver mResourceNamespaceResolver;
+    private final Function<String, Map<String, Integer>> mFrameworkEnumValueSupplier;
+    private final EnumValueSupplier mProjectEnumValueSupplier;
+
+    // VisibleForTesting
+    BridgeXmlPullAttributes(
+            @NonNull XmlPullParser parser,
+            @NonNull BridgeContext context,
+            @NonNull ResourceNamespace xmlFileResourceNamespace,
+            @NonNull Function<String, Map<String, Integer>> frameworkEnumValueSupplier,
+            @NonNull EnumValueSupplier projectEnumValueSupplier) {
+        super(parser);
+        mContext = context;
+        mFrameworkEnumValueSupplier = frameworkEnumValueSupplier;
+        mProjectEnumValueSupplier = projectEnumValueSupplier;
+        mXmlFileResourceNamespace = xmlFileResourceNamespace;
+        mResourceNamespaceResolver =
+                new XmlPullParserResolver(
+                        mParser, context.getLayoutlibCallback().getImplicitNamespaces());
+    }
+
+    public BridgeXmlPullAttributes(
+            @NonNull XmlPullParser parser,
+            @NonNull BridgeContext context,
+            @NonNull ResourceNamespace xmlFileResourceNamespace) {
+        this(parser, context, xmlFileResourceNamespace, Bridge::getEnumValues, (ns, attrName) -> {
+            ResourceValue attr =
+                    context.getRenderResources().getUnresolvedResource(
+                            ResourceReference.attr(ns, attrName));
+            return attr instanceof AttrResourceValue
+                    ? ((AttrResourceValue) attr).getAttributeValues()
+                    : null;
         });
     }
 
@@ -84,15 +102,16 @@
 
         if (BridgeConstants.NS_RESOURCES.equals(ns)) {
             return Bridge.getResourceId(ResourceType.ATTR, name);
-
         }
 
         // this is not an attribute in the android namespace, we query the customviewloader, if
         // the namespaces match.
         if (mContext.getLayoutlibCallback().getNamespace().equals(ns)) {
-            Integer v = mContext.getLayoutlibCallback().getResourceId(ResourceType.ATTR, name);
-            if (v != null) {
-                return v;
+            // TODO(namespaces): cache the namespace objects.
+            ResourceNamespace namespace = ResourceNamespace.fromNamespaceUri(ns);
+            if (namespace != null) {
+                return mContext.getLayoutlibCallback().getOrGenerateResourceId(
+                        ResourceReference.attr(namespace, name));
             }
         }
 
@@ -161,9 +180,15 @@
             return XmlUtils.convertValueToInt(value, defaultValue);
         } catch (NumberFormatException e) {
             // This is probably an enum
-            Map<String, Integer> enumValues = BridgeConstants.NS_RESOURCES.equals(namespace) ?
-                    mFrameworkEnumValueSupplier.apply(attribute) :
-                    mProjectEnumValueSupplier.apply(attribute);
+            Map<String, Integer> enumValues = null;
+            if (BridgeConstants.NS_RESOURCES.equals(namespace)) {
+                enumValues = mFrameworkEnumValueSupplier.apply(attribute);
+            } else {
+                ResourceNamespace attrNamespace = ResourceNamespace.fromNamespaceUri(namespace);
+                if (attrNamespace != null) {
+                    enumValues = mProjectEnumValueSupplier.getEnumValues(attrNamespace, attribute);
+                }
+            }
 
             Integer enumValue = enumValues != null ? enumValues.get(value) : null;
             if (enumValue != null) {
@@ -241,9 +266,8 @@
 
     @Override
     public int getAttributeIntValue(int index, int defaultValue) {
-        return getAttributeIntValue(mParser.getAttributeNamespace(index),
-                getAttributeName(index)
-                , defaultValue);
+        return getAttributeIntValue(
+                mParser.getAttributeNamespace(index), getAttributeName(index), defaultValue);
     }
 
     @Override
@@ -278,6 +302,14 @@
         return defaultValue;
     }
 
+    @Override
+    @Nullable
+    public ResourceValue getResolvedAttributeValue(@Nullable String namespace,
+            @NonNull String name) {
+        String s = getAttributeValue(namespace, name);
+        return s == null ? null : getResourceValue(s);
+    }
+
     // -- private helper methods
 
     /**
@@ -285,8 +317,9 @@
      */
     private ResourceValue getResourceValue(String value) {
         // now look for this particular value
-        RenderResources resources = mContext.getRenderResources();
-        return resources.resolveResValue(resources.findResValue(value, mPlatformFile));
+        return mContext.getRenderResources().resolveResValue(
+                new UnresolvedResourceValue(
+                        value, mXmlFileResourceNamespace, mResourceNamespaceResolver));
     }
 
     /**
@@ -295,17 +328,9 @@
     private int resolveResourceValue(String value, int defaultValue) {
         ResourceValue resource = getResourceValue(value);
         if (resource != null) {
-            Integer id;
-            if (mPlatformFile || resource.isFramework()) {
-                id = Bridge.getResourceId(resource.getResourceType(), resource.getName());
-            } else {
-                id = mContext.getLayoutlibCallback().getResourceId(
-                        resource.getResourceType(), resource.getName());
-            }
-
-            if (id != null) {
-                return id;
-            }
+            return resource.isFramework() ?
+                    Bridge.getResourceId(resource.getResourceType(), resource.getName()) :
+                    mContext.getLayoutlibCallback().getOrGenerateResourceId(resource.asReference());
         }
 
         return defaultValue;
diff --git a/bridge/src/android/util/ResolvingAttributeSet.java b/bridge/src/android/util/ResolvingAttributeSet.java
new file mode 100644
index 0000000..6efd812
--- /dev/null
+++ b/bridge/src/android/util/ResolvingAttributeSet.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import com.android.ide.common.rendering.api.ResourceValue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+public interface ResolvingAttributeSet extends AttributeSet {
+    /**
+     * Returns the resolved value of the attribute with the given name and namespace
+     *
+     * @param namespace the namespace of the attribute
+     * @param name the name of the attribute
+     * @return the resolved value of the attribute, or null if the attribute does not exist
+     */
+    @Nullable
+    ResourceValue getResolvedAttributeValue(@Nullable String namespace, @NonNull String name);
+}
diff --git a/bridge/src/android/util/Xml_Delegate.java b/bridge/src/android/util/Xml_Delegate.java
index 213e848..e309dc6 100644
--- a/bridge/src/android/util/Xml_Delegate.java
+++ b/bridge/src/android/util/Xml_Delegate.java
@@ -33,11 +33,10 @@
  * around to map int to instance of the delegate.
  */
 public class Xml_Delegate {
-
     @LayoutlibDelegate
     /*package*/ static XmlPullParser newPullParser() {
         try {
-            return ParserFactory.instantiateParser(null);
+            return ParserFactory.create();
         } catch (XmlPullParserException e) {
             throw new AssertionError();
         }
diff --git a/bridge/src/android/util/imagepool/Bucket.java b/bridge/src/android/util/imagepool/Bucket.java
new file mode 100644
index 0000000..c562243
--- /dev/null
+++ b/bridge/src/android/util/imagepool/Bucket.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+
+import android.util.imagepool.ImagePool.Image.Orientation;
+
+import java.awt.image.BufferedImage;
+import java.lang.ref.SoftReference;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * Data model for image pool. Bucket contains the list of same sized buffered image in soft ref.
+ */
+/* private package */ class Bucket {
+
+    @VisibleForTesting final Queue<SoftReference<BufferedImage>> mBufferedImageRef = new LinkedList<>();
+
+    public boolean isEmpty() {
+        return mBufferedImageRef.isEmpty();
+    }
+
+    @Nullable
+    public BufferedImage remove() {
+        if (mBufferedImageRef.isEmpty()) {
+            return null;
+        }
+
+        SoftReference<BufferedImage> reference = mBufferedImageRef.remove();
+        return reference == null ? null : reference.get();
+    }
+
+    public void offer(BufferedImage img) {
+        mBufferedImageRef.offer(new SoftReference<>(img));
+    }
+
+    public void clear() {
+        mBufferedImageRef.clear();
+    }
+
+    static class BucketCreationMetaData {
+        public final int mWidth;
+        public final int mHeight;
+        public final int mType;
+        public final int mNumberOfCopies;
+        public final Orientation mOrientation;
+        public final long mMaxCacheSize;
+
+        BucketCreationMetaData(int width, int height, int type, int numberOfCopies,
+                Orientation orientation, long maxCacheSize) {
+            mWidth = width;
+            mHeight = height;
+            mType = type;
+            mNumberOfCopies = numberOfCopies;
+            mOrientation = orientation;
+            mMaxCacheSize = maxCacheSize;
+        }
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImageImpl.java b/bridge/src/android/util/imagepool/ImageImpl.java
new file mode 100644
index 0000000..42a6e73
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImageImpl.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Representation of buffered image. When used with ImagePool, it provides 2 features
+ * (last one not yet impl'd):
+ *
+ * <ul>
+ *   <li>Automatic recycle of BufferedImage through use of reference counter</li>
+ *   <li>Able to re-use the image for similar size of buffered image</li>
+ *   <li>TODO: Able to re-use the iamge for different orientation (not yet impl'd)</li>
+ * </ul>
+ */
+/* private package */ class ImageImpl implements ImagePool.Image {
+
+    private final ReadWriteLock mLock = new ReentrantReadWriteLock();
+
+    private final int mWidth;
+    private final int mHeight;
+    private final Orientation mOrientation;
+
+    @VisibleForTesting final BufferedImage mImg;
+
+    ImageImpl(
+            int width,
+            int height,
+            @NotNull BufferedImage img,
+            Orientation orientation) {
+        mImg = img;
+        mWidth = width;
+        mHeight = height;
+        mOrientation = orientation;
+    }
+
+    @Override
+    public int getWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getHeight() {
+        return mHeight;
+    }
+
+    @Override
+    public void setRGB(int x, int y, int width, int height, int[] colors, int offset, int stride) {
+        mLock.readLock().lock();
+        try {
+            // TODO: Apply orientation.
+            mImg.setRGB(x, y, width, height, colors, offset, stride);
+        } finally {
+            mLock.readLock().unlock();
+        }
+    }
+
+    @Override
+    public void drawImage(Graphics2D graphics, int x, int y, @Nullable ImageObserver o) {
+        mLock.readLock().lock();
+        try {
+            // TODO: Apply orientation.
+            graphics.drawImage(mImg, x, y, mWidth, mHeight, o);
+        } finally {
+            mLock.readLock().unlock();
+        }
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePool.java b/bridge/src/android/util/imagepool/ImagePool.java
new file mode 100644
index 0000000..baec65d
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePool.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.ImageObserver;
+import java.util.function.Consumer;
+
+/**
+ * Simplified version of image pool that exists in Studio.
+ *
+ * Lacks:
+ * - PhantomReference and FinalizableReference to recognize the death of references automatically.
+ *   (Meaning devs need to be more deligent in dispose.)
+ * Has:
+ * + Debugger that allows us to better trace where image is being leaked in stack.
+ */
+public interface ImagePool {
+
+    /**
+     * Returns a new image of width w and height h.
+     */
+    @NotNull
+    Image acquire(final int w, final int h, final int type);
+
+    /**
+     * Disposes the image pool, releasing all the references to the buffered images.
+     */
+    void dispose();
+
+    /**
+     * Interface that represents a buffered image. Using this wrapper allows us ot better track
+     * memory usages around BufferedImage. When all of it's references are removed, it will
+     * automatically be pooled back into the image pool for re-use.
+     */
+    interface Image {
+
+        /**
+         * Same as {@link BufferedImage#setRGB(int, int, int, int, int[], int, int)}
+         */
+        void setRGB(int x, int y, int width, int height, int[] colors, int offset, int stride);
+
+        /**
+         * Same as {@link Graphics2D#drawImage(java.awt.Image, int, int, ImageObserver)}
+         */
+        void drawImage(Graphics2D graphics, int x, int y, ImageObserver o);
+
+        /**
+         * Image orientation. It's not used at the moment. To be used later.
+         */
+        enum Orientation {
+            NONE,
+            CW_90
+        }
+
+        int getWidth();
+        int getHeight();
+    }
+
+    /**
+     * Policy for how to set up the memory pool.
+     */
+    class ImagePoolPolicy {
+
+        public final int[] mBucketSizes;
+        public final int[] mNumberOfCopies;
+        public final long mBucketMaxCacheSize;
+
+        /**
+         * @param bucketPixelSizes - list of pixel sizes to bucket (categorize) images. The list
+         * must be sorted (low to high).
+         * @param numberOfCopies - Allows users to create multiple copies of the bucket. It is
+         * recommended to create more copies for smaller images to avoid fragmentation in memory.
+         * It must match the size of bucketPixelSizes.
+         * @param bucketMaxCacheByteSize - Maximum cache byte sizes image pool is allowed to hold onto
+         * in memory.
+         */
+        public ImagePoolPolicy(
+                int[] bucketPixelSizes, int[] numberOfCopies, long bucketMaxCacheByteSize) {
+            assert bucketPixelSizes.length == numberOfCopies.length;
+            mBucketSizes = bucketPixelSizes;
+            mNumberOfCopies = numberOfCopies;
+            mBucketMaxCacheSize = bucketMaxCacheByteSize;
+        }
+    }
+}
diff --git a/bridge/src/android/util/imagepool/ImagePoolHelper.java b/bridge/src/android/util/imagepool/ImagePoolHelper.java
new file mode 100644
index 0000000..292fc59
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePoolHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import android.util.imagepool.Bucket.BucketCreationMetaData;
+import android.util.imagepool.ImagePool.Image.Orientation;
+import android.util.imagepool.ImagePool.ImagePoolPolicy;
+
+import java.awt.image.BufferedImage;
+import java.util.Map;
+
+/* private package */ class ImagePoolHelper {
+
+    @Nullable
+    public static BucketCreationMetaData getBucketCreationMetaData(int w, int h, int type, ImagePoolPolicy poolPolicy, ImagePoolStats stats) {
+        // Find the bucket sizes for both dimensions
+        int widthBucket = -1;
+        int heightBucket = -1;
+        int index = 0;
+
+        for (int bucketMinSize : poolPolicy.mBucketSizes) {
+            if (widthBucket == -1 && w <= bucketMinSize) {
+                widthBucket = bucketMinSize;
+
+                if (heightBucket != -1) {
+                    break;
+                }
+            }
+            if (heightBucket == -1 && h <= bucketMinSize) {
+                heightBucket = bucketMinSize;
+
+                if (widthBucket != -1) {
+                    break;
+                }
+            }
+            ++index;
+        }
+
+        stats.recordBucketRequest(w, h);
+
+        if (index >= poolPolicy.mNumberOfCopies.length) {
+            return null;
+        }
+
+        // TODO: Apply orientation
+//        if (widthBucket < heightBucket) {
+//            return new BucketCreationMetaData(heightBucket, widthBucket, type, poolPolicy.mNumberOfCopies[index],
+//                    Orientation.CW_90, poolPolicy.mBucketMaxCacheSize);
+//        }
+        return new BucketCreationMetaData(widthBucket, heightBucket, type, poolPolicy.mNumberOfCopies[index],
+                Orientation.NONE, poolPolicy.mBucketMaxCacheSize);
+    }
+
+    @Nullable
+    public static BufferedImage getBufferedImage(
+            Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
+
+        // strongref is just for gc.
+        BufferedImage strongRef = populateBucket(bucket, metaData, stats);
+
+        // pool is too small to create the requested buffer.
+        if (bucket.isEmpty()) {
+            assert strongRef == null;
+            return null;
+        }
+
+        // Go through the bucket of soft references to find the first buffer that's not null.
+        // Even if gc is triggered here, strongref should survive.
+        BufferedImage img = bucket.remove();
+        while (img == null && !bucket.isEmpty()) {
+            img = bucket.remove();
+        }
+
+        if (img == null && bucket.isEmpty()) {
+            // Whole buffer was null. Recurse.
+            return getBufferedImage(bucket, metaData, stats);
+        }
+        return img;
+    }
+
+    /**
+     * Populate the bucket in greedy manner to avoid fragmentation.
+     * Behaviour is controlled by {@link ImagePoolPolicy}.
+     * Returns one strong referenced buffer to avoid getting results gc'd. Null if pool is not large
+     * enough.
+     */
+    @Nullable
+    private static BufferedImage populateBucket(
+            Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
+        if (!bucket.isEmpty()) {
+            // If not empty no need to populate.
+            return null;
+        }
+
+        BufferedImage strongRef = null;
+        for (int i = 0; i < metaData.mNumberOfCopies; i++) {
+            if (!stats.fitsMaxCacheSize(
+                    metaData.mWidth, metaData.mHeight, metaData.mMaxCacheSize)) {
+                break;
+            }
+            strongRef =new BufferedImage(metaData.mWidth, metaData.mHeight,
+                    metaData.mType);
+            bucket.offer(strongRef);
+            stats.recordBucketCreation(metaData.mWidth, metaData.mHeight);
+        }
+        return strongRef;
+    }
+
+    private static String toKey(int w, int h, int type) {
+        return new StringBuilder()
+                .append(w)
+                .append('x')
+                .append(h)
+                .append(':')
+                .append(type)
+                .toString();
+    }
+
+    public static Bucket getBucket(Map<String, Bucket> map, BucketCreationMetaData metaData, ImagePoolPolicy mPolicy) {
+        String key = toKey(metaData.mWidth, metaData.mHeight, metaData.mType);
+        Bucket bucket = map.get(key);
+        if (bucket == null) {
+            bucket = new Bucket();
+            map.put(key, bucket);
+        }
+        return bucket;
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolImpl.java b/bridge/src/android/util/imagepool/ImagePoolImpl.java
new file mode 100644
index 0000000..3da706e
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePoolImpl.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+
+import android.util.imagepool.Bucket.BucketCreationMetaData;
+import android.util.imagepool.ImagePool.Image.Orientation;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.lang.ref.Reference;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Consumer;
+
+import com.google.common.base.FinalizablePhantomReference;
+import com.google.common.base.FinalizableReferenceQueue;
+
+class ImagePoolImpl implements ImagePool {
+
+    private final ReentrantReadWriteLock mReentrantLock = new ReentrantReadWriteLock();
+    private final ImagePoolPolicy mPolicy;
+    @VisibleForTesting final Map<String, Bucket> mPool = new HashMap<>();
+    @VisibleForTesting final ImagePoolStats mImagePoolStats = new ImagePoolStatsProdImpl();
+    private final FinalizableReferenceQueue mFinalizableReferenceQueue = new FinalizableReferenceQueue();
+    private final Set<Reference<?>> mReferences = new HashSet<>();
+
+    public ImagePoolImpl(ImagePoolPolicy policy) {
+        mPolicy = policy;
+        mImagePoolStats.start();
+    }
+
+    @Override
+    public Image acquire(int w, int h, int type) {
+        return acquire(w, h, type, null);
+    }
+
+    /* package private */ Image acquire(int w, int h, int type,
+            @Nullable Consumer<BufferedImage> freedCallback) {
+        mReentrantLock.writeLock().lock();
+        try {
+            BucketCreationMetaData metaData =
+                    ImagePoolHelper.getBucketCreationMetaData(w, h, type, mPolicy, mImagePoolStats);
+            if (metaData == null) {
+                return defaultImageImpl(w, h, type, freedCallback);
+            }
+
+            final Bucket existingBucket = ImagePoolHelper.getBucket(mPool, metaData, mPolicy);
+            final BufferedImage img =
+                    ImagePoolHelper.getBufferedImage(existingBucket, metaData, mImagePoolStats);
+            if (img == null) {
+                return defaultImageImpl(w, h, type, freedCallback);
+            }
+
+            // Clear the image. - is this necessary?
+            if (img.getRaster().getDataBuffer().getDataType() == java.awt.image.DataBuffer.TYPE_INT) {
+                Arrays.fill(((DataBufferInt)img.getRaster().getDataBuffer()).getData(), 0);
+            }
+
+            return prepareImage(
+                    new ImageImpl(w, h, img, metaData.mOrientation),
+                    true,
+                    img,
+                    existingBucket,
+                    freedCallback);
+        } finally {
+            mReentrantLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Add statistics as well as dispose behaviour before returning image.
+     */
+    private Image prepareImage(
+            Image image,
+            boolean offerBackToBucket,
+            @Nullable BufferedImage img,
+            @Nullable Bucket existingBucket,
+            @Nullable Consumer<BufferedImage> freedCallback) {
+        final Integer imageHash = image.hashCode();
+        mImagePoolStats.acquiredImage(imageHash);
+        FinalizablePhantomReference<Image> reference =
+                new FinalizablePhantomReference<ImagePool.Image>(image, mFinalizableReferenceQueue) {
+                    @Override
+                    public void finalizeReferent() {
+                        // This method might be called twice if the user has manually called the free() method. The second call will have no effect.
+                        if (mReferences.remove(this)) {
+                            mImagePoolStats.disposeImage(imageHash);
+                            if (offerBackToBucket) {
+                                if (!mImagePoolStats.fitsMaxCacheSize(img.getWidth(), img.getHeight(),
+                                        mPolicy.mBucketMaxCacheSize)) {
+                                    mImagePoolStats.tooBigForCache();
+                                    // Adding this back would go over the max cache size we set for ourselves. Release it.
+                                    return;
+                                }
+
+                                // else stat does not change.
+                                existingBucket.offer(img);
+                            }
+                            if (freedCallback != null) {
+                                freedCallback.accept(img);
+                            }
+                        }
+                    }
+                };
+        mReferences.add(reference);
+        return image;
+    }
+
+    /**
+     * Default Image Impl to be used when the pool is not big enough.
+     */
+    private Image defaultImageImpl(int w, int h, int type,
+            @Nullable Consumer<BufferedImage> freedCallback) {
+        BufferedImage bufferedImage = new BufferedImage(w, h, type);
+        mImagePoolStats.tooBigForCache();
+        mImagePoolStats.recordAllocOutsidePool(w, h);
+        return prepareImage(new ImageImpl(w, h, bufferedImage, Orientation.NONE),
+                false,  null, null, freedCallback);
+    }
+
+    @Override
+    public void dispose() {
+        mReentrantLock.writeLock().lock();
+        try {
+            for (Bucket bucket : mPool.values()) {
+                bucket.clear();
+            }
+            mImagePoolStats.clear();
+        } finally {
+            mReentrantLock.writeLock().unlock();
+        }
+    }
+
+    /* package private */ void printStat() {
+        System.out.println(mImagePoolStats.getStatistic());
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolProvider.java b/bridge/src/android/util/imagepool/ImagePoolProvider.java
new file mode 100644
index 0000000..dbdd849
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePoolProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.util.imagepool.ImagePool.ImagePoolPolicy;
+
+public class ImagePoolProvider {
+
+    private static ImagePool sInstance;
+
+    @NotNull
+    public static synchronized ImagePool get() {
+        if (sInstance == null) {
+            // Idea is to create more
+            ImagePoolPolicy policy = new ImagePoolPolicy(
+                    new int[]{100, 200, 400, 600, 800, 1000, 1600, 3200},
+                    new int[]{  3,   3,   2,   2,   2,    1,    1,    1},
+                    10_000_000L); // 10 MB
+
+            sInstance = new ImagePoolImpl(policy);
+        }
+        return sInstance;
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolStats.java b/bridge/src/android/util/imagepool/ImagePoolStats.java
new file mode 100644
index 0000000..17dab14
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePoolStats.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+/**
+ * Keeps track of statistics. Some are purely for debugging purposes in which case it's wrapped
+ * around DEBUG check.
+ */
+interface ImagePoolStats {
+
+    void recordBucketCreation(int widthBucket, int heightBucket);
+
+    boolean fitsMaxCacheSize(int width, int height, long maxCacheSize);
+
+    void clear();
+
+    void recordBucketRequest(int w, int h);
+
+    void recordAllocOutsidePool(int width, int height);
+
+    void tooBigForCache();
+
+    void acquiredImage(Integer imageHash);
+
+    void disposeImage(Integer imageHash);
+
+    void start();
+
+    String getStatistic();
+}
diff --git a/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java b/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java
new file mode 100644
index 0000000..de0f757
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+
+/**
+ * Useful impl for debugging reproable error.
+ */
+public class ImagePoolStatsDebugImpl extends ImagePoolStatsProdImpl {
+
+    private static String PACKAGE_NAME = ImagePoolStats.class.getPackage().getName();
+
+    // Used for deugging purposes only.
+    private final Map<Integer, String> mCallStack = new HashMap<>();
+    private long mRequestedTotalBytes = 0;
+    private long mAllocatedOutsidePoolBytes = 0;
+
+    // Used for gc-related stats.
+    private long mPreviousGcCollection = 0;
+    private long mPreviousGcTime = 0;
+
+    /** Used for policy */
+    @Override
+    public void recordBucketCreation(int widthBucket, int heightBucket) {
+        super.recordBucketCreation(widthBucket, heightBucket);
+    }
+
+    @Override
+    public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
+        return super.fitsMaxCacheSize(width, height, maxCacheSize);
+    }
+
+    @Override
+    public void clear() {
+        super.clear();
+
+        mRequestedTotalBytes = 0;
+        mAllocatedOutsidePoolBytes = 0;
+        mTooBigForPoolCount = 0;
+        mCallStack.clear();
+    }
+
+    @Override
+    public void tooBigForCache() {
+        super.tooBigForCache();
+    }
+
+    /** Used for Debugging only */
+    @Override
+    public void recordBucketRequest(int w, int h) {
+        mRequestedTotalBytes += (w * h * ESTIMATED_PIXEL_BYTES);
+    }
+
+    @Override
+    public void recordAllocOutsidePool(int width, int height) {
+        mAllocatedOutsidePoolBytes += (width * height * ESTIMATED_PIXEL_BYTES);
+    }
+
+    @Override
+    public void acquiredImage(Integer imageHash) {
+        for (int i = 1; i < Thread.currentThread().getStackTrace().length; i++) {
+            StackTraceElement element = Thread.currentThread().getStackTrace()[i];
+            String str = element.toString();
+
+            if (!str.contains(PACKAGE_NAME)) {
+                mCallStack.put(imageHash, str);
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void disposeImage(Integer imageHash) {
+        mCallStack.remove(imageHash);
+    }
+
+    @Override
+    public void start() {
+        long totalGarbageCollections = 0;
+        long garbageCollectionTime = 0;
+        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+            long count = gc.getCollectionCount();
+            if (count >= 0) {
+                totalGarbageCollections += count;
+            }
+            long time = gc.getCollectionTime();
+            if (time >= 0) {
+                garbageCollectionTime += time;
+            }
+        }
+        mPreviousGcCollection = totalGarbageCollections;
+        mPreviousGcTime = garbageCollectionTime;
+    }
+
+    private String calculateGcStatAndReturn() {
+        long totalGarbageCollections = 0;
+        long garbageCollectionTime = 0;
+        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+            long count = gc.getCollectionCount();
+            if (count > 0) {
+                totalGarbageCollections += count;
+            }
+            long time = gc.getCollectionTime();
+            if(time > 0) {
+                garbageCollectionTime += time;
+            }
+        }
+        totalGarbageCollections -= mPreviousGcCollection;
+        garbageCollectionTime -= mPreviousGcTime;
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("Total Garbage Collections: ");
+        builder.append(totalGarbageCollections);
+        builder.append("\n");
+
+        builder.append("Total Garbage Collection Time (ms): ");
+        builder.append(garbageCollectionTime);
+        builder.append("\n");
+
+        return builder.toString();
+    }
+
+    @Override
+    public String getStatistic() {
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(calculateGcStatAndReturn());
+        builder.append("Memory\n");
+        builder.append(" requested total         : ");
+        builder.append(mRequestedTotalBytes / 1_000_000);
+        builder.append(" MB\n");
+        builder.append(" allocated (in pool)     : ");
+        builder.append(mAllocateTotalBytes / 1_000_000);
+        builder.append(" MB\n");
+        builder.append(" allocated (out of pool) : ");
+        builder.append(mAllocatedOutsidePoolBytes / 1_000_000);
+        builder.append(" MB\n");
+
+        double percent = (1.0 - (double) mRequestedTotalBytes / (mAllocateTotalBytes +
+                mAllocatedOutsidePoolBytes));
+        if (percent < 0.0) {
+            builder.append(" saved : ");
+            builder.append(-1.0 * percent);
+            builder.append("%\n");
+        } else {
+            builder.append(" wasting : ");
+            builder.append(percent);
+            builder.append("%\n");
+        }
+
+        builder.append("Undispose images\n");
+        Multiset<String> countSet = HashMultiset.create();
+        for (String callsite : mCallStack.values()) {
+            countSet.add(callsite);
+        }
+
+        for (Multiset.Entry<String> entry : countSet.entrySet()) {
+            builder.append(" - ");
+            builder.append(entry.getElement());
+            builder.append(" - missed dispose : ");
+            builder.append(entry.getCount());
+            builder.append(" times\n");
+        }
+
+        builder.append("Number of times requested image didn't fit the pool : ");
+        builder.append(mTooBigForPoolCount);
+        builder.append("\n");
+
+        return builder.toString();
+    }
+}
diff --git a/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java b/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java
new file mode 100644
index 0000000..e6c6abe
--- /dev/null
+++ b/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+
+class ImagePoolStatsProdImpl implements ImagePoolStats {
+
+    static int ESTIMATED_PIXEL_BYTES = 4;
+
+    // Used for determining how many buckets can be created.
+    @VisibleForTesting long mAllocateTotalBytes = 0;
+    @VisibleForTesting int mTooBigForPoolCount = 0;
+
+    /** Used for policy */
+    @Override
+    public void recordBucketCreation(int widthBucket, int heightBucket) {
+        mAllocateTotalBytes += (widthBucket * heightBucket * ESTIMATED_PIXEL_BYTES);
+    }
+
+    @Override
+    public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
+        long newTotal = mAllocateTotalBytes + (width * height * ESTIMATED_PIXEL_BYTES);
+        return newTotal <= maxCacheSize;
+    }
+
+    @Override
+    public void tooBigForCache() {
+        mTooBigForPoolCount++;
+    }
+
+    @Override
+    public void clear() {
+        mAllocateTotalBytes = 0;
+    }
+
+    @Override
+    public void recordBucketRequest(int w, int h) { }
+
+    @Override
+    public void recordAllocOutsidePool(int width, int height) { }
+
+    @Override
+    public void acquiredImage(@NotNull Integer imageHash) { }
+
+    @Override
+    public void disposeImage(@NotNull Integer imageHash) { }
+
+    @Override
+    public void start() { }
+
+    @Override
+    public String getStatistic() { return ""; }
+}
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index 84fd0ed..0421f58 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -16,9 +16,11 @@
 
 package android.view;
 
+import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.MergeCookie;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.layoutlib.bridge.Bridge;
@@ -26,6 +28,7 @@
 import com.android.layoutlib.bridge.MockView;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
 import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil;
 import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
 import com.android.layoutlib.bridge.impl.ParserFactory;
@@ -33,7 +36,6 @@
 import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.util.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -43,10 +45,11 @@
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.ResolvingAttributeSet;
+import android.view.View.OnAttachStateChangeListener;
 import android.widget.ImageView;
 import android.widget.NumberPicker;
 
-import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -60,7 +63,17 @@
  * Custom implementation of {@link LayoutInflater} to handle custom views.
  */
 public final class BridgeInflater extends LayoutInflater {
-
+    private static final String INFLATER_CLASS_ATTR_NAME = "viewInflaterClass";
+    private static final ResourceReference RES_AUTO_INFLATER_CLASS_ATTR =
+            ResourceReference.attr(ResourceNamespace.RES_AUTO, INFLATER_CLASS_ATTR_NAME);
+    private static final ResourceReference LEGACY_APPCOMPAT_INFLATER_CLASS_ATTR =
+            ResourceReference.attr(ResourceNamespace.APPCOMPAT_LEGACY, INFLATER_CLASS_ATTR_NAME);
+    private static final ResourceReference ANDROIDX_APPCOMPAT_INFLATER_CLASS_ATTR =
+            ResourceReference.attr(ResourceNamespace.APPCOMPAT, INFLATER_CLASS_ATTR_NAME);
+    private static final String LEGACY_DEFAULT_APPCOMPAT_INFLATER_NAME =
+            "android.support.v7.app.AppCompatViewInflater";
+    private static final String ANDROIDX_DEFAULT_APPCOMPAT_INFLATER_NAME =
+            "androidx.appcompat.app.AppCompatViewInflater";
     private final LayoutlibCallback mLayoutlibCallback;
 
     private boolean mIsInMerge = false;
@@ -186,7 +199,19 @@
     @Nullable
     private static Class<?> findCustomInflater(@NotNull BridgeContext bc,
             @NotNull LayoutlibCallback layoutlibCallback) {
-        ResourceValue value = bc.getRenderResources().findItemInTheme("viewInflaterClass", false);
+        ResourceReference attrRef;
+        if (layoutlibCallback.isResourceNamespacingRequired()) {
+            if (layoutlibCallback.hasLegacyAppCompat()) {
+                attrRef = LEGACY_APPCOMPAT_INFLATER_CLASS_ATTR;
+            } else if (layoutlibCallback.hasAndroidXAppCompat()) {
+                attrRef = ANDROIDX_APPCOMPAT_INFLATER_CLASS_ATTR;
+            } else {
+                return null;
+            }
+        } else {
+            attrRef = RES_AUTO_INFLATER_CLASS_ATTR;
+        }
+        ResourceValue value = bc.getRenderResources().findItemInTheme(attrRef);
         String inflaterName = value != null ? value.getValue() : null;
 
         if (inflaterName != null) {
@@ -195,12 +220,16 @@
             } catch (ClassNotFoundException ignore) {
             }
 
-            // viewInflaterClass was defined but we couldn't find the class
+            // viewInflaterClass was defined but we couldn't find the class.
         } else if (bc.isAppCompatTheme()) {
             // Older versions of AppCompat do not define the viewInflaterClass so try to get it
-            // manually
+            // manually.
             try {
-                return layoutlibCallback.findClass("android.support.v7.app.AppCompatViewInflater");
+                if (layoutlibCallback.hasLegacyAppCompat()) {
+                    return layoutlibCallback.findClass(LEGACY_DEFAULT_APPCOMPAT_INFLATER_NAME);
+                } else if (layoutlibCallback.hasAndroidXAppCompat()) {
+                    return layoutlibCallback.findClass(ANDROIDX_DEFAULT_APPCOMPAT_INFLATER_NAME);
+                }
             } catch (ClassNotFoundException ignore) {
             }
         }
@@ -332,36 +361,32 @@
 
             ResourceValue value = null;
 
-            @SuppressWarnings("deprecation")
-            Pair<ResourceType, String> layoutInfo = Bridge.resolveResourceId(resource);
-            if (layoutInfo != null) {
-                value = bridgeContext.getRenderResources().getFrameworkResource(
-                        ResourceType.LAYOUT, layoutInfo.getSecond());
-            } else {
+            ResourceReference layoutInfo = Bridge.resolveResourceId(resource);
+            if (layoutInfo == null) {
                 layoutInfo = mLayoutlibCallback.resolveResourceId(resource);
 
-                if (layoutInfo != null) {
-                    value = bridgeContext.getRenderResources().getProjectResource(
-                            ResourceType.LAYOUT, layoutInfo.getSecond());
-                }
+            }
+            if (layoutInfo != null) {
+                value = bridgeContext.getRenderResources().getResolvedResource(layoutInfo);
             }
 
             if (value != null) {
-                File f = new File(value.getValue());
-                if (f.isFile()) {
-                    try {
-                        XmlPullParser parser = ParserFactory.create(f, true);
-
-                        BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
-                                parser, bridgeContext, value.isFramework());
-
-                        return inflate(bridgeParser, root);
-                    } catch (Exception e) {
-                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                                "Failed to parse file " + f.getAbsolutePath(), e, null);
-
+                String path = value.getValue();
+                try {
+                    XmlPullParser parser = ParserFactory.create(path, true);
+                    if (parser == null) {
                         return null;
                     }
+
+                    BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
+                            parser, bridgeContext, value.getNamespace());
+
+                    return inflate(bridgeParser, root);
+                } catch (Exception e) {
+                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                            "Failed to parse file " + path, e, null);
+
+                    return null;
                 }
             }
         }
@@ -407,72 +432,90 @@
     private void setupViewInContext(View view, AttributeSet attrs) {
         Context context = getContext();
         context = getBaseContext(context);
-        if (context instanceof BridgeContext) {
-            BridgeContext bc = (BridgeContext) context;
-            // get the view key
-            Object viewKey = getViewKeyFromParser(attrs, bc, mResourceReference, mIsInMerge);
-            if (viewKey != null) {
-                bc.addViewKey(view, viewKey);
-            }
-            String scrollPosX = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollX");
-            if (scrollPosX != null && scrollPosX.endsWith("px")) {
-                int value = Integer.parseInt(scrollPosX.substring(0, scrollPosX.length() - 2));
-                bc.setScrollXPos(view, value);
-            }
-            String scrollPosY = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollY");
-            if (scrollPosY != null && scrollPosY.endsWith("px")) {
-                int value = Integer.parseInt(scrollPosY.substring(0, scrollPosY.length() - 2));
-                bc.setScrollYPos(view, value);
-            }
-            if (ReflectionUtils.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
-                Integer resourceId = null;
-                String attrListItemValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
-                        BridgeConstants.ATTR_LIST_ITEM);
-                int attrItemCountValue = attrs.getAttributeIntValue(BridgeConstants.NS_TOOLS_URI,
-                        BridgeConstants.ATTR_ITEM_COUNT, -1);
-                if (attrListItemValue != null && !attrListItemValue.isEmpty()) {
-                    ResourceValue resValue = bc.getRenderResources().findResValue(attrListItemValue, false);
-                    if (resValue.isFramework()) {
-                        resourceId = Bridge.getResourceId(resValue.getResourceType(),
-                                resValue.getName());
-                    } else {
-                        resourceId = mLayoutlibCallback.getResourceId(resValue.getResourceType(),
-                                resValue.getName());
-                    }
-                }
-                if (resourceId == null) {
-                    resourceId = 0;
-                }
-                RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId, attrItemCountValue);
-            } else if (ReflectionUtils.isInstanceOf(view, DrawerLayoutUtil.CN_DRAWER_LAYOUT)) {
-                String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
-                        BridgeConstants.ATTR_OPEN_DRAWER);
-                if (attrVal != null) {
-                    getDrawerLayoutMap().put(view, attrVal);
-                }
-            }
-            else if (view instanceof NumberPicker) {
-                NumberPicker numberPicker = (NumberPicker) view;
-                String minValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "minValue");
-                if (minValue != null) {
-                    numberPicker.setMinValue(Integer.parseInt(minValue));
-                }
-                String maxValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "maxValue");
-                if (maxValue != null) {
-                    numberPicker.setMaxValue(Integer.parseInt(maxValue));
-                }
-            }
-            else if (view instanceof ImageView) {
-                ImageView img = (ImageView) view;
-                Drawable drawable = img.getDrawable();
-                if (drawable instanceof Animatable) {
-                    if (!((Animatable) drawable).isRunning()) {
-                        ((Animatable) drawable).start();
-                    }
-                }
-            }
-
+        if (!(context instanceof BridgeContext)) {
+            return;
         }
+
+        BridgeContext bc = (BridgeContext) context;
+        // get the view key
+        Object viewKey = getViewKeyFromParser(attrs, bc, mResourceReference, mIsInMerge);
+        if (viewKey != null) {
+            bc.addViewKey(view, viewKey);
+        }
+        String scrollPosX = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollX");
+        if (scrollPosX != null && scrollPosX.endsWith("px")) {
+            int value = Integer.parseInt(scrollPosX.substring(0, scrollPosX.length() - 2));
+            bc.setScrollXPos(view, value);
+        }
+        String scrollPosY = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollY");
+        if (scrollPosY != null && scrollPosY.endsWith("px")) {
+            int value = Integer.parseInt(scrollPosY.substring(0, scrollPosY.length() - 2));
+            bc.setScrollYPos(view, value);
+        }
+        if (ReflectionUtils.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
+            int resourceId = 0;
+            int attrItemCountValue = attrs.getAttributeIntValue(BridgeConstants.NS_TOOLS_URI,
+                    BridgeConstants.ATTR_ITEM_COUNT, -1);
+            if (attrs instanceof ResolvingAttributeSet) {
+                ResourceValue attrListItemValue =
+                        ((ResolvingAttributeSet) attrs).getResolvedAttributeValue(
+                                BridgeConstants.NS_TOOLS_URI, BridgeConstants.ATTR_LIST_ITEM);
+                if (attrListItemValue != null) {
+                    resourceId = bc.getResourceId(attrListItemValue.asReference(), 0);
+                }
+            }
+            RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId, attrItemCountValue);
+        } else if (ReflectionUtils.isInstanceOf(view, DrawerLayoutUtil.CN_DRAWER_LAYOUT)) {
+            String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
+                    BridgeConstants.ATTR_OPEN_DRAWER);
+            if (attrVal != null) {
+                getDrawerLayoutMap().put(view, attrVal);
+            }
+        }
+        else if (view instanceof NumberPicker) {
+            NumberPicker numberPicker = (NumberPicker) view;
+            String minValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "minValue");
+            if (minValue != null) {
+                numberPicker.setMinValue(Integer.parseInt(minValue));
+            }
+            String maxValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "maxValue");
+            if (maxValue != null) {
+                numberPicker.setMaxValue(Integer.parseInt(maxValue));
+            }
+        }
+        else if (view instanceof ImageView) {
+            ImageView img = (ImageView) view;
+            Drawable drawable = img.getDrawable();
+            if (drawable instanceof Animatable) {
+                if (!((Animatable) drawable).isRunning()) {
+                    ((Animatable) drawable).start();
+                }
+            }
+        }
+        else if (view instanceof ViewStub) {
+            // By default, ViewStub will be set to GONE and won't be inflate. If the XML has the
+            // tools:visibility attribute we'll workaround that behavior.
+            String visibility = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
+                    SdkConstants.ATTR_VISIBILITY);
+
+            boolean isVisible = "visible".equals(visibility);
+            if (isVisible || "invisible".equals(visibility)) {
+                // We can not inflate the view until is attached to its parent so we need to delay
+                // the setVisible call until after that happens.
+                final int visibilityValue = isVisible ? View.VISIBLE : View.INVISIBLE;
+                view.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View v) {
+                        v.removeOnAttachStateChangeListener(this);
+                        view.setVisibility(visibilityValue);
+                    }
+
+                    @Override
+                    public void onViewDetachedFromWindow(View v) {}
+                });
+            }
+        }
+
     }
 
     public void setIsInMerge(boolean isInMerge) {
@@ -541,7 +584,7 @@
     @NonNull
     private Map<View, String> getDrawerLayoutMap() {
         if (mOpenDrawerLayouts == null) {
-            mOpenDrawerLayouts = new HashMap<View, String>(4);
+            mOpenDrawerLayouts = new HashMap<>(4);
         }
         return mOpenDrawerLayouts;
     }
diff --git a/bridge/src/android/view/IWindowManagerImpl.java b/bridge/src/android/view/IWindowManagerImpl.java
index b1d9151..efa8a9a 100644
--- a/bridge/src/android/view/IWindowManagerImpl.java
+++ b/bridge/src/android/view/IWindowManagerImpl.java
@@ -16,32 +16,15 @@
 
 package android.view;
 
-import android.app.IAssistDataReceiver;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.GraphicBuffer;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
-import android.view.RemoteAnimationAdapter;
-
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.policy.IShortcutService;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
 
 /**
  * Basic implementation of {@link IWindowManager} so that {@link Display} (and
  * {@link Display_Delegate}) can return a valid instance.
  */
-public class IWindowManagerImpl implements IWindowManager {
+public class IWindowManagerImpl extends IWindowManager.Default {
 
     private final Configuration mConfig;
     private final DisplayMetrics mMetrics;
@@ -70,500 +53,8 @@
     }
 
     @Override
-    public boolean hasNavigationBar() {
+    public boolean hasNavigationBar(int displayId) {
+        // TODO(multi-display): Change it once we need it per display.
         return mHasNavigationBar;
     }
-
-    // ---- unused implementation of IWindowManager ----
-
-    @Override
-    public int getNavBarPosition() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public void addWindowToken(IBinder arg0, int arg1, int arg2) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void clearForcedDisplaySize(int displayId) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void clearForcedDisplayDensityForUser(int displayId, int userId) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setOverscan(int displayId, int left, int top, int right, int bottom)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void closeSystemDialogs(String arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void startFreezingScreen(int exitAnim, int enterAnim) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void stopFreezingScreen() {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void disableKeyguard(IBinder arg0, String arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void executeAppTransition() throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void exitKeyguardSecurely(IOnKeyguardExitResult arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void freezeRotation(int arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public float getAnimationScale(int arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public float[] getAnimationScales() throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public int getPendingAppTransition() throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public boolean inputMethodClientHasFocus(IInputMethodClient arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean isKeyguardLocked() throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean isKeyguardSecure() throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean isViewServerRunning() throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public IWindowSession openSession(IWindowSessionCallback argn1, IInputMethodClient arg0,
-            IInputContext arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void overridePendingAppTransition(String arg0, int arg1, int arg2,
-            IRemoteCallback startedCallback) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
-            int startHeight) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void overridePendingAppTransitionClipReveal(int startX, int startY,
-            int startWidth, int startHeight) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
-            IRemoteCallback startedCallback, boolean scaleUp) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX,
-            int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
-            boolean scaleUp) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void overridePendingAppTransitionInPlace(String packageName, int anim) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void overridePendingAppTransitionMultiThumbFuture(
-            IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
-            boolean scaleUp) throws RemoteException {
-
-    }
-
-    @Override
-    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
-            IRemoteCallback callback0, IRemoteCallback callback1, boolean scaleUp) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter adapter) {
-    }
-
-    @Override
-    public void prepareAppTransition(int arg0, boolean arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void reenableKeyguard(IBinder arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void removeWindowToken(IBinder arg0, int arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public boolean requestAssistScreenshot(IAssistDataReceiver receiver)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void setAnimationScale(int arg0, float arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void setAnimationScales(float[] arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public float getCurrentAnimatorScale() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public void setEventDispatching(boolean arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setFocusedApp(IBinder arg0, boolean arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void getInitialDisplaySize(int displayId, Point size) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void getBaseDisplaySize(int displayId, Point size) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setForcedDisplaySize(int displayId, int arg0, int arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public int getInitialDisplayDensity(int displayId) {
-        return -1;
-    }
-
-    @Override
-    public int getBaseDisplayDensity(int displayId) {
-        return -1;
-    }
-
-    @Override
-    public void setForcedDisplayDensityForUser(int displayId, int density, int userId)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setForcedDisplayScalingMode(int displayId, int mode) {
-    }
-
-    @Override
-    public void setInTouchMode(boolean arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public int[] setNewDisplayOverrideConfiguration(Configuration arg0, int displayId)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void refreshScreenCaptureDisabled(int userId) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void updateRotation(boolean arg0, boolean arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setStrictModeVisualIndicatorPreference(String arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void showStrictModeViolation(boolean arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public boolean startViewServer(int arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void statusBarVisibilityChanged(int arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setRecentsVisibility(boolean visible) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setPipVisibility(boolean visible) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setShelfHeight(boolean visible, int shelfHeight) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
-    }
-
-    @Override
-    public boolean stopViewServer() throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void thawRotation() throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public Configuration updateOrientationFromAppTokens(Configuration arg0, IBinder arg1, int arg2)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public int watchRotation(IRotationWatcher arg0, int arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public void removeRotationWatcher(IRotationWatcher arg0) throws RemoteException {
-    }
-
-    @Override
-    public IBinder asBinder() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public int getPreferredOptionsPanelGravity() throws RemoteException {
-        return Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-    }
-
-    @Override
-    public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message)
-            throws RemoteException {
-    }
-
-    @Override
-    public void setSwitchingUser(boolean switching) throws RemoteException {
-    }
-
-    @Override
-    public void lockNow(Bundle options) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public boolean isSafeModeEnabled() {
-        return false;
-    }
-
-    @Override
-    public boolean isRotationFrozen() throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void enableScreenIfNeeded() throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public boolean clearWindowContentFrameStats(IBinder token) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public WindowContentFrameStats getWindowContentFrameStats(IBinder token)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public int getDockedStackSide() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public void endProlongedAnimations() {
-    }
-
-    @Override
-    public void registerDockedStackListener(IDockedStackListener listener) throws RemoteException {
-    }
-
-    @Override
-    public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) throws RemoteException {
-    }
-
-    @Override
-    public void setResizeDimLayer(boolean visible, int targetStackId, float alpha)
-            throws RemoteException {
-    }
-
-    @Override
-    public void setDockedStackDividerTouchRegion(Rect touchableRegion) throws RemoteException {
-    }
-
-    @Override
-    public void requestAppKeyboardShortcuts(
-            IResultReceiver receiver, int deviceId) throws RemoteException {
-    }
-
-    @Override
-    public void getStableInsets(int displayId, Rect outInsets) throws RemoteException {
-    }
-
-    @Override
-    public void registerShortcutKey(long shortcutCode, IShortcutService service)
-        throws RemoteException {}
-
-    @Override
-    public void createInputConsumer(IBinder token, String name, InputChannel inputChannel)
-            throws RemoteException {}
-
-    @Override
-    public boolean destroyInputConsumer(String name) throws RemoteException {
-        return false;
-    }
-
-    @Override
-    public Bitmap screenshotWallpaper() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public Region getCurrentImeTouchRegion() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public boolean registerWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
-            int displayId) throws RemoteException {
-        return false;
-    }
-
-    @Override
-    public void unregisterWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
-            int displayId) throws RemoteException {
-    }
-
-    @Override
-    public void startWindowTrace() throws RemoteException {
-    }
-
-    @Override
-    public void stopWindowTrace() throws RemoteException {
-    }
-
-    @Override
-    public boolean isWindowTraceEnabled() throws RemoteException {
-        return false;
-    }
-
-    @Override
-    public void requestUserActivityNotification() throws RemoteException {
-    }
-
-    @Override
-    public void dontOverrideDisplayInfo(int displayId) throws RemoteException {
-    }
 }
diff --git a/bridge/src/android/view/LayoutInflater_Delegate.java b/bridge/src/android/view/LayoutInflater_Delegate.java
index cec6bb3..3f364f9 100644
--- a/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -233,4 +233,15 @@
 
         LayoutInflater.consumeChildElements(parser);
     }
+
+    @LayoutlibDelegate
+    /* package */ static void initPrecompiledViews(LayoutInflater thisInflater) {
+        initPrecompiledViews(thisInflater, false);
+    }
+
+    @LayoutlibDelegate
+    /* package */ static void initPrecompiledViews(LayoutInflater thisInflater,
+            boolean enablePrecompiledViews) {
+        thisInflater.initPrecompiledViews_Original(enablePrecompiledViews);
+    }
 }
diff --git a/bridge/src/android/view/PointerIcon_Delegate.java b/bridge/src/android/view/PointerIcon_Delegate.java
index 4a5ea9b..84c75f7 100644
--- a/bridge/src/android/view/PointerIcon_Delegate.java
+++ b/bridge/src/android/view/PointerIcon_Delegate.java
@@ -30,4 +30,9 @@
         // PointerIcon would not be displayed by layoutlib anyway, so we always return the null
         // icon.
     }
+
+    @LayoutlibDelegate
+    /*package*/ static void registerDisplayListener(Context context) {
+        // Ignore this as we do not have a DisplayManager
+    }
 }
diff --git a/bridge/src/android/view/SurfaceControl_Delegate.java b/bridge/src/android/view/SurfaceControl_Delegate.java
new file mode 100644
index 0000000..24838b2
--- /dev/null
+++ b/bridge/src/android/view/SurfaceControl_Delegate.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+public class SurfaceControl_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<SurfaceControl_Delegate> sManager =
+            new DelegateManager<>(SurfaceControl_Delegate.class);
+    private static long sFinalizer = -1;
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeCreateTransaction() {
+        return sManager.addNewDelegate(new SurfaceControl_Delegate());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nativeGetNativeTransactionFinalizer() {
+        synchronized (SurfaceControl_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
+    }
+}
diff --git a/bridge/src/android/view/ViewGroup_Delegate.java b/bridge/src/android/view/ViewGroup_Delegate.java
index 10a4f31..a34ef22 100644
--- a/bridge/src/android/view/ViewGroup_Delegate.java
+++ b/bridge/src/android/view/ViewGroup_Delegate.java
@@ -16,9 +16,12 @@
 
 package android.view;
 
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.resources.Density;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
@@ -27,6 +30,7 @@
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.view.animation.Transformation;
+import android.view.shadow.HighQualityShadowPainter;
 
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
@@ -65,11 +69,32 @@
 
     private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
             Outline outline) {
+        boolean highQualityShadow = false;
+        boolean enableShadow = true;
         float elevation = getElevation(child, parent);
-        if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
-            RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha());
+        Context bridgeContext = parent.getContext();
+        if (bridgeContext instanceof BridgeContext) {
+            highQualityShadow = ((BridgeContext) bridgeContext).getLayoutlibCallback()
+                    .getFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW);
+            enableShadow = ((BridgeContext) bridgeContext).getLayoutlibCallback()
+                    .getFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW);
+        }
+
+        if (!enableShadow) {
             return;
         }
+
+        if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
+            if (highQualityShadow) {
+                float densityDpi = bridgeContext.getResources().getDisplayMetrics().densityDpi;
+                HighQualityShadowPainter.paintRectShadow(
+                        parent, outline, elevation, canvas, child.getAlpha(), densityDpi);
+            } else {
+                RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha());
+            }
+            return;
+        }
+
         BufferedImage shadow = null;
         if (outline.mPath != null) {
             shadow = getPathShadow(outline, canvas, elevation, child.getAlpha());
@@ -83,7 +108,7 @@
         Rect clipBounds = canvas.getClipBounds();
         Rect newBounds = new Rect(clipBounds);
         newBounds.inset((int)-elevation, (int)-elevation);
-        canvas.clipRect(newBounds, Op.REPLACE);
+        canvas.clipRectUnion(newBounds);
         canvas.drawBitmap(bitmap, 0, 0, null);
         canvas.restore();
     }
diff --git a/bridge/src/android/view/accessibility/AccessibilityManager.java b/bridge/src/android/view/accessibility/AccessibilityManager.java
index 84b4064..d5e60a9 100644
--- a/bridge/src/android/view/accessibility/AccessibilityManager.java
+++ b/bridge/src/android/view/accessibility/AccessibilityManager.java
@@ -151,7 +151,7 @@
                 public void setState(int state) {
                 }
 
-                public void notifyServicesStateChanged() {
+                public void notifyServicesStateChanged(long updatedUiTimeout) {
                 }
 
                 public void setRelevantEventTypes(int eventTypes) {
diff --git a/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java b/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
index dc4f9c8..85dc5d3 100644
--- a/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
+++ b/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
@@ -21,7 +21,7 @@
  */
 public class InputMethodManager_Accessor {
 
-    public static void resetInstance() {
-        InputMethodManager.sInstance = null;
+    public static void tearDownEditMode() {
+        InputMethodManager.tearDownEditMode();
     }
 }
diff --git a/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java b/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
index b2a183b..5dd25b4 100644
--- a/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
+++ b/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
@@ -16,12 +16,8 @@
 
 package android.view.inputmethod;
 
-import com.android.internal.view.IInputMethodManager;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.os.Looper;
-
 
 /**
  * Delegate used to provide new implementation of a select few methods of {@link InputMethodManager}
@@ -35,15 +31,7 @@
     // ---- Overridden methods ----
 
     @LayoutlibDelegate
-    /*package*/ static InputMethodManager getInstance() {
-        synchronized (InputMethodManager.class) {
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm == null) {
-                imm = new InputMethodManager(ReflectionUtils.createProxy(IInputMethodManager.class),
-                        Looper.getMainLooper());
-                InputMethodManager.sInstance = imm;
-            }
-            return imm;
-        }
+    /*package*/ static boolean isInEditMode() {
+        return true;
     }
 }
diff --git a/bridge/src/android/view/math/Math3DHelper.java b/bridge/src/android/view/math/Math3DHelper.java
new file mode 100644
index 0000000..7359c4c
--- /dev/null
+++ b/bridge/src/android/view/math/Math3DHelper.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.math;
+
+public class Math3DHelper {
+
+    private static final float EPSILON = 0.0000001f;
+
+    private Math3DHelper() { }
+
+    /**
+     * Calculates [p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2];
+     *
+     * @param d - dimension in which the poly is represented (supports 2 or 3D)
+     * @return float[]{t2, t, p1} or float[]{Float.NaN}
+     */
+    public static float[] rayIntersectPoly(float[] poly, int polyLength, float px, float py,
+            float dx, float dy, int d) {
+        int p1 = polyLength - 1;
+        for (int p2 = 0; p2 < polyLength; p2++) {
+            float p1x = poly[p1 * d + 0];
+            float p1y = poly[p1 * d + 1];
+            float p2x = poly[p2 * d + 0];
+            float p2y = poly[p2 * d + 1];
+            float div = (dx * (p1y - p2y) + dy * (p2x - p1x));
+            if (div != 0) {
+                float t = (dx * (p1y - py) + dy * (px - p1x)) / div;
+                if (t >= 0 && t <= 1) {
+                    float t2 = (p1x * (py - p2y)
+                            + p2x * (p1y - py)
+                            + px * (p2y - p1y))
+                            / div;
+                    if (t2 > 0) {
+                        return new float[]{t2, t, p1};
+                    }
+                }
+            }
+            p1 = p2;
+        }
+        return new float[]{Float.NaN};
+    }
+
+    public static void centroid2d(float[] poly, int len, float[] ret) {
+        float sumx = 0;
+        float sumy = 0;
+        int p1 = len - 1;
+        float area = 0;
+        for (int p2 = 0; p2 < len; p2++) {
+            float x1 = poly[p1 * 2 + 0];
+            float y1 = poly[p1 * 2 + 1];
+            float x2 = poly[p2 * 2 + 0];
+            float y2 = poly[p2 * 2 + 1];
+            float a = (x1 * y2 - x2 * y1);
+            sumx += (x1 + x2) * a;
+            sumy += (y1 + y2) * a;
+            area += a;
+            p1 = p2;
+        }
+        float centroidx = sumx / (3 * area);
+        float centroidy = sumy / (3 * area);
+        ret[0] = centroidx;
+        ret[1] = centroidy;
+    }
+
+    public static void centroid3d(float[] poly, int len, float[] ret) {
+        int n = len - 1;
+        double area = 0;
+        double cx = 0;
+        double cy = 0;
+        double cz = 0;
+        for (int i = 1; i < n; i++) {
+            int k = i + 1;
+            float a0 = poly[i * 3 + 0] - poly[0 * 3 + 0];
+            float a1 = poly[i * 3 + 1] - poly[0 * 3 + 1];
+            float a2 = poly[i * 3 + 2] - poly[0 * 3 + 2];
+            float b0 = poly[k * 3 + 0] - poly[0 * 3 + 0];
+            float b1 = poly[k * 3 + 1] - poly[0 * 3 + 1];
+            float b2 = poly[k * 3 + 2] - poly[0 * 3 + 2];
+            float c0 = a1 * b2 - b1 * a2;
+            float c1 = a2 * b0 - b2 * a0;
+            float c2 = a0 * b1 - b0 * a1;
+            double areaOfTriangle = Math.sqrt(c0 * c0 + c1 * c1 + c2 * c2);
+            area += areaOfTriangle;
+            cx += areaOfTriangle * (poly[i * 3 + 0] + poly[k * 3 + 0] + poly[0 * 3 + 0]);
+            cy += areaOfTriangle * (poly[i * 3 + 1] + poly[k * 3 + 1] + poly[0 * 3 + 1]);
+            cz += areaOfTriangle * (poly[i * 3 + 2] + poly[k * 3 + 2] + poly[0 * 3 + 2]);
+        }
+        ret[0] = (float) (cx / (3 * area));
+        ret[1] = (float) (cy / (3 * area));
+        ret[2] = (float) (cz / (3 * area));
+    }
+
+    public final static int min(int x1, int x2, int x3) {
+        return (x1 > x2) ? ((x2 > x3) ? x3 : x2) : ((x1 > x3) ? x3 : x1);
+    }
+
+    public final static int max(int x1, int x2, int x3) {
+        return (x1 < x2) ? ((x2 < x3) ? x3 : x2) : ((x1 < x3) ? x3 : x1);
+    }
+
+    private static void xsort(float[] points, int pointsLength) {
+        quicksortX(points, 0, pointsLength - 1);
+    }
+
+    public static int hull(float[] points, int pointsLength, float[] retPoly) {
+        xsort(points, pointsLength);
+        int n = pointsLength;
+        float[] lUpper = new float[n * 2];
+        lUpper[0] = points[0];
+        lUpper[1] = points[1];
+        lUpper[2] = points[2];
+        lUpper[3] = points[3];
+
+        int lUpperSize = 2;
+
+        for (int i = 2; i < n; i++) {
+            lUpper[lUpperSize * 2 + 0] = points[i * 2 + 0];
+            lUpper[lUpperSize * 2 + 1] = points[i * 2 + 1];
+            lUpperSize++;
+
+            while (lUpperSize > 2 && !rightTurn(
+                    lUpper[(lUpperSize - 3) * 2], lUpper[(lUpperSize - 3) * 2 + 1],
+                    lUpper[(lUpperSize - 2) * 2], lUpper[(lUpperSize - 2) * 2 + 1],
+                    lUpper[(lUpperSize - 1) * 2], lUpper[(lUpperSize - 1) * 2 + 1])) {
+                // Remove the middle point of the three last
+                lUpper[(lUpperSize - 2) * 2 + 0] = lUpper[(lUpperSize - 1) * 2 + 0];
+                lUpper[(lUpperSize - 2) * 2 + 1] = lUpper[(lUpperSize - 1) * 2 + 1];
+                lUpperSize--;
+            }
+        }
+
+        float[] lLower = new float[n * 2];
+        lLower[0] = points[(n - 1) * 2 + 0];
+        lLower[1] = points[(n - 1) * 2 + 1];
+        lLower[2] = points[(n - 2) * 2 + 0];
+        lLower[3] = points[(n - 2) * 2 + 1];
+
+        int lLowerSize = 2;
+
+        for (int i = n - 3; i >= 0; i--) {
+            lLower[lLowerSize * 2 + 0] = points[i * 2 + 0];
+            lLower[lLowerSize * 2 + 1] = points[i * 2 + 1];
+            lLowerSize++;
+
+            while (lLowerSize > 2 && !rightTurn(
+                    lLower[(lLowerSize - 3) * 2], lLower[(lLowerSize - 3) * 2 + 1],
+                    lLower[(lLowerSize - 2) * 2], lLower[(lLowerSize - 2) * 2 + 1],
+                    lLower[(lLowerSize - 1) * 2], lLower[(lLowerSize - 1) * 2 + 1])) {
+                // Remove the middle point of the three last
+                lLower[(lLowerSize - 2) * 2 + 0] = lLower[(lLowerSize - 1) * 2 + 0];
+                lLower[(lLowerSize - 2) * 2 + 1] = lLower[(lLowerSize - 1) * 2 + 1];
+                lLowerSize--;
+            }
+        }
+        int count = 0;
+
+        for (int i = 0; i < lUpperSize; i++) {
+            retPoly[count * 2 + 0] = lUpper[i * 2 + 0];
+            retPoly[count * 2 + 1] = lUpper[i * 2 + 1];
+            count++;
+        }
+
+        for (int i = 1; i < lLowerSize - 1; i++) {
+            retPoly[count * 2 + 0] = lLower[i * 2 + 0];
+            retPoly[count * 2 + 1] = lLower[i * 2 + 1];
+            count++;
+        }
+
+        return count;
+    }
+
+    private static boolean rightTurn(float ax, float ay, float bx, float by, float cx, float cy) {
+        return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > 0.00001;
+    }
+
+    /**
+     * Calculates the intersection of poly1 with poly2 and puts in poly2
+     * @return number of point in poly2
+     */
+    public static int intersection(
+            float[] poly1, int poly1length, float[] poly2, int poly2length) {
+        makeClockwise(poly1, poly1length);
+        makeClockwise(poly2, poly2length);
+        float[] poly = new float[(poly1length * poly2length + 2) * 2];
+        int count = 0;
+        int pcount = 0;
+        for (int i = 0; i < poly1length; i++) {
+            if (pointInsidePolygon(poly1[i * 2], poly1[i * 2 + 1], poly2, poly2length)) {
+                poly[count * 2] = poly1[i * 2];
+                poly[count * 2 + 1] = poly1[i * 2 + 1];
+                count++;
+                pcount++;
+            }
+        }
+        int fromP1 = pcount;
+        for (int i = 0; i < poly2length; i++) {
+            if (pointInsidePolygon(poly2[i * 2], poly2[i * 2 + 1], poly1, poly1length)) {
+                poly[count * 2] = poly2[i * 2];
+                poly[count * 2 + 1] = poly2[i * 2 + 1];
+                count++;
+            }
+        }
+        int fromP2 = count - fromP1;
+        if (fromP1 == poly1length) { // use p1
+            for (int i = 0; i < poly1length; i++) {
+                poly2[i * 2] = poly1[i * 2];
+                poly2[i * 2 + 1] = poly1[i * 2 + 1];
+            }
+            return poly1length;
+        }
+        if (fromP2 == poly2length) { // use p2
+            return poly2length;
+        }
+        float[] intersection = new float[2];
+        for (int i = 0; i < poly2length; i++) {
+            for (int j = 0; j < poly1length; j++) {
+                int i1_by_2 = i * 2;
+                int i2_by_2 = ((i + 1) % poly2length) * 2;
+                int j1_by_2 = j * 2;
+                int j2_by_2 = ((j + 1) % poly1length) * 2;
+                boolean found = lineIntersection(
+                        poly2[i1_by_2], poly2[i1_by_2 + 1],
+                        poly2[i2_by_2], poly2[i2_by_2 + 1],
+                        poly1[j1_by_2], poly1[j1_by_2 + 1],
+                        poly1[j2_by_2], poly1[j2_by_2 + 1], intersection);
+                if (found) {
+                    poly[count * 2] = intersection[0];
+                    poly[count * 2 + 1] = intersection[1];
+                    count++;
+                } else {
+                    float dx = poly2[i * 2] - poly1[j * 2];
+                    float dy = poly2[i * 2 + 1] - poly1[j * 2 + 1];
+
+                    if (dx * dx + dy * dy < 0.01) {
+                        poly[count * 2] = poly2[i * 2];
+                        poly[count * 2 + 1] = poly2[i * 2 + 1];
+                        count++;
+                    }
+                }
+            }
+        }
+        if (count == 0) {
+            return 0;
+        }
+        float avgx = 0;
+        float avgy = 0;
+        for (int i = 0; i < count; i++) {
+            avgx += poly[i * 2];
+            avgy += poly[i * 2 + 1];
+        }
+        avgx /= count;
+        avgy /= count;
+
+        float[] ctr = new float[] { avgx, avgy };
+        sort(poly, count, ctr);
+        int size = count;
+
+        poly2[0] = poly[0];
+        poly2[1] = poly[1];
+
+        count = 1;
+        for (int i = 1; i < size; i++) {
+            float dx = poly[i * 2] - poly[(i - 1) * 2];
+            float dy = poly[i * 2 + 1] - poly[(i - 1) * 2 + 1];
+            if (dx * dx + dy * dy >= 0.01) {
+                poly2[count * 2] = poly[i * 2];
+                poly2[count * 2 + 1] = poly[i * 2 + 1];
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public static void sort(float[] poly, int polyLength, float[] ctr) {
+        quicksortCirc(poly, 0, polyLength - 1, ctr);
+    }
+
+    public static float angle(float x1, float y1, float[] ctr) {
+        return -(float) Math.atan2(x1 - ctr[0], y1 - ctr[1]);
+    }
+
+    private static void swap(float[] points, int i, int j) {
+        float x = points[i * 2];
+        float y = points[i * 2 + 1];
+        points[i * 2] = points[j * 2];
+        points[i * 2 + 1] = points[j * 2 + 1];
+        points[j * 2] = x;
+        points[j * 2 + 1] = y;
+    }
+
+    private static void quicksortCirc(float[] points, int low, int high, float[] ctr) {
+        int i = low, j = high;
+        int p = low + (high - low) / 2;
+        float pivot = angle(points[p * 2], points[p * 2 + 1], ctr);
+        while (i <= j) {
+            while (angle(points[i * 2], points[i * 2 + 1], ctr) < pivot) {
+                i++;
+            }
+            while (angle(points[j * 2], points[j * 2 + 1], ctr) > pivot) {
+                j--;
+            }
+
+            if (i <= j) {
+                swap(points, i, j);
+                i++;
+                j--;
+            }
+        }
+        if (low < j) {
+            quicksortCirc(points, low, j, ctr);
+        }
+        if (i < high) {
+            quicksortCirc(points, i, high, ctr);
+        }
+    }
+
+    private static void quicksortX(float[] points, int low, int high) {
+        int i = low, j = high;
+        int p = low + (high - low) / 2;
+        float pivot = points[p * 2];
+        while (i <= j) {
+            while (points[i * 2] < pivot) {
+                i++;
+            }
+            while (points[j * 2] > pivot) {
+                j--;
+            }
+
+            if (i <= j) {
+                swap(points, i, j);
+                i++;
+                j--;
+            }
+        }
+        if (low < j) {
+            quicksortX(points, low, j);
+        }
+        if (i < high) {
+            quicksortX(points, i, high);
+        }
+    }
+
+    private static boolean pointInsidePolygon(float x, float y, float[] poly, int len) {
+        boolean c = false;
+        float testx = x;
+        float testy = y;
+        for (int i = 0, j = len - 1; i < len; j = i++) {
+            if (((poly[i * 2 + 1] > testy) != (poly[j * 2 + 1] > testy)) &&
+                    (testx < (poly[j * 2] - poly[i * 2]) * (testy - poly[i * 2 + 1])
+                            / (poly[j * 2 + 1] - poly[i * 2 + 1]) + poly[i * 2])) {
+                c = !c;
+            }
+        }
+        return c;
+    }
+
+    private static void makeClockwise(float[] polygon, int len) {
+        if (polygon == null || len == 0) {
+            return;
+        }
+        if (!isClockwise(polygon, len)) {
+            reverse(polygon, len);
+        }
+    }
+
+    private static boolean isClockwise(float[] polygon, int len) {
+        float sum = 0;
+        float p1x = polygon[(len - 1) * 2];
+        float p1y = polygon[(len - 1) * 2 + 1];
+        for (int i = 0; i < len; i++) {
+
+            float p2x = polygon[i * 2];
+            float p2y = polygon[i * 2 + 1];
+            sum += p1x * p2y - p2x * p1y;
+            p1x = p2x;
+            p1y = p2y;
+        }
+        return sum < 0;
+    }
+
+    private static void reverse(float[] polygon, int len) {
+        int n = len / 2;
+        for (int i = 0; i < n; i++) {
+            float tmp0 = polygon[i * 2];
+            float tmp1 = polygon[i * 2 + 1];
+            int k = len - 1 - i;
+            polygon[i * 2] = polygon[k * 2];
+            polygon[i * 2 + 1] = polygon[k * 2 + 1];
+            polygon[k * 2] = tmp0;
+            polygon[k * 2 + 1] = tmp1;
+        }
+    }
+
+    /**
+     * Intersects two lines in parametric form.
+     */
+    private static final boolean lineIntersection(
+            float x1, float y1,
+            float x2, float y2,
+            float x3, float y3,
+            float x4, float y4,
+            float[] ret) {
+
+        float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+        if (d == 0.000f) {
+            return false;
+        }
+
+        float dx = (x1 * y2 - y1 * x2);
+        float dy = (x3 * y4 - y3 * x4);
+        float x = (dx * (x3 - x4) - (x1 - x2) * dy) / d;
+        float y = (dx * (y3 - y4) - (y1 - y2) * dy) / d;
+
+        if (((x - x1) * (x - x2) > EPSILON)
+                || ((x - x3) * (x - x4) > EPSILON)
+                || ((y - y1) * (y - y2) > EPSILON)
+                || ((y - y3) * (y - y4) > EPSILON)) {
+
+            return false;
+        }
+        ret[0] = x;
+        ret[1] = y;
+        return true;
+    }
+
+    /**
+     * Imagine a donut shaped image and trying to create triangles from its centroid (like
+     * cutting a pie). This function performs such action (and also edge-case triangle strips
+     * generation) then returns the resulting triangle strips.
+     *
+     * @param retstrips - the resulting triangle strips
+     */
+    public static void donutPie2(float[] penumbra, int penumbraLength,
+            float[] umbra, int umbraLength, int rays, int layers, float strength,
+            float[] retstrips) {
+        int rings = layers + 1;
+
+        double step = Math.PI * 2 / rays;
+        float[] retxy = new float[2];
+        centroid2d(umbra, umbraLength, retxy);
+        float cx = retxy[0];
+        float cy = retxy[1];
+
+        float[] t1 = new float[rays];
+        float[] t2 = new float[rays];
+
+        for (int i = 0; i < rays; i++) {
+            float dx = (float) Math.sin(Math.PI / 4 + step * i);
+            float dy = (float) Math.cos(Math.PI / 4 + step * i);
+            t2[i] = rayIntersectPoly(umbra, umbraLength, cx, cy, dx, dy, 2)[0];
+            t1[i] = rayIntersectPoly(penumbra, penumbraLength, cx, cy, dx, dy, 2)[0];
+        }
+
+        int p = 0;
+        // Calc the vertex
+        for (int r = 0; r < layers; r++) {
+            int startp = p;
+            for (int i = 0; i < rays; i++) {
+                float dx = (float) Math.sin(Math.PI / 4 + step * i);
+                float dy = (float) Math.cos(Math.PI / 4 + step * i);
+
+                for (int j = r; j < (r + 2); j++) {
+                    float jf = j / (float) (rings - 1);
+                    float t = t1[i] + jf * (t2[i] - t1[i]);
+                    float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
+
+                    retstrips[p * 3] = dx * t + cx;
+                    retstrips[p * 3 + 1] = dy * t + cy;
+                    retstrips[p * 3 + 2] = jf * op * strength;
+
+                    p++;
+                }
+            }
+            retstrips[p * 3] = retstrips[startp * 3];
+            retstrips[p * 3 + 1] = retstrips[startp * 3 + 1];
+            retstrips[p * 3 + 2] = retstrips[startp * 3 + 2];
+            p++;
+            startp++;
+            retstrips[p * 3] = retstrips[startp * 3];
+            retstrips[p * 3 + 1] = retstrips[startp * 3 + 1];
+            retstrips[p * 3 + 2] = retstrips[startp * 3 + 2];
+            p++;
+        }
+        int oldp = p - 1;
+        retstrips[p * 3] = retstrips[oldp * 3];
+        retstrips[p * 3 + 1] = retstrips[oldp * 3 + 1];
+        retstrips[p * 3 + 2] = retstrips[oldp * 3 + 2];
+        p+=2;
+
+        oldp = p;
+        for (int k = 0; k < rays; k++) {
+            int i = k / 2;
+            if ((k & 1) == 1) { // traverse the inside in a zig zag pattern
+                // for strips
+                i = rays - i - 1;
+            }
+            float dx = (float) Math.sin(Math.PI / 4 + step * i);
+            float dy = (float) Math.cos(Math.PI / 4 + step * i);
+
+            float jf = 1;
+
+            float t = t1[i] + jf * (t2[i] - t1[i]);
+            float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
+
+            retstrips[p * 3] = dx * t + cx;
+            retstrips[p * 3 + 1] = dy * t + cy;
+            retstrips[p * 3 + 2] = jf * op * strength;
+            p++;
+        }
+        p = oldp - 1;
+        retstrips[p * 3] = retstrips[oldp * 3];
+        retstrips[p * 3 + 1] = retstrips[oldp * 3 + 1];
+        retstrips[p * 3 + 2] = retstrips[oldp * 3 + 2];
+    }
+
+    /**
+     * @return Rect bound of flattened (ignoring z). LTRB
+     * @param dimension - 2D or 3D
+     */
+    public static float[] flatBound(float[] poly, int dimension) {
+        int polySize = poly.length/dimension;
+        float left = poly[0];
+        float right = poly[0];
+        float top = poly[1];
+        float bottom = poly[1];
+
+        for (int i = 0; i < polySize; i++) {
+            float x = poly[i * dimension + 0];
+            float y = poly[i * dimension + 1];
+
+            if (left > x) {
+                left = x;
+            } else if (right < x) {
+                right = x;
+            }
+
+            if (top > y) {
+                top = y;
+            } else if (bottom < y) {
+                bottom = y;
+            }
+        }
+        return new float[]{left, top, right, bottom};
+    }
+
+    /**
+     * Translate the polygon to x and y
+     * @param dimension in what dimension is polygon represented (supports 2 or 3D).
+     */
+    public static void translate(float[] poly, float translateX, float translateY, int dimension) {
+        int polySize = poly.length/dimension;
+
+        for (int i = 0; i < polySize; i++) {
+            poly[i * dimension + 0] += translateX;
+            poly[i * dimension + 1] += translateY;
+        }
+    }
+
+}
+
diff --git a/bridge/src/android/view/shadow/AmbientShadowBitmapGenerator.java b/bridge/src/android/view/shadow/AmbientShadowBitmapGenerator.java
new file mode 100644
index 0000000..57fa2d6
--- /dev/null
+++ b/bridge/src/android/view/shadow/AmbientShadowBitmapGenerator.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
+import android.graphics.Bitmap;
+import android.view.math.Math3DHelper;
+
+/**
+ * Generates ambient shadow bitmap
+ */
+class AmbientShadowBitmapGenerator {
+
+    private final AmbientShadowConfig mShadowConfig;
+    private final TriangleBuffer mTriangleBuffer;
+    private final AmbientShadowVertexCalculator mCalculator;
+
+    private float mTranslateX;
+    private float mTranslateY;
+
+    private boolean mValid;
+
+    public AmbientShadowBitmapGenerator(AmbientShadowConfig shadowConfig) {
+        mShadowConfig = shadowConfig;
+
+        mTriangleBuffer = new TriangleBuffer();
+        mTriangleBuffer.setSize(mShadowConfig.getWidth(), mShadowConfig.getHeight(), 0);
+
+        mCalculator = new AmbientShadowVertexCalculator(mShadowConfig);
+    }
+
+    /**
+     * Populate vertices and fill the triangle buffers. To be called before {@link #getBitmap()}
+     */
+    public void populateShadow() {
+        try {
+            mValid = mCalculator.generateVertex(mShadowConfig.getPolygon());
+            if (!mValid) {
+                Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while " +
+                                "drawing ambient shadow", null, null);
+                return;
+            }
+
+            float[] shadowBounds = Math3DHelper.flatBound(mCalculator.getVertex(), 2);
+            if (shadowBounds[0] < 0) {
+                // translate to right by the offset amount.
+                mTranslateX = shadowBounds[0] * -1;
+            } else if (shadowBounds[2] > mShadowConfig.getWidth()) {
+                // translate to left by the offset amount.
+                mTranslateX = shadowBounds[2] - mShadowConfig.getWidth();
+            }
+
+            if (shadowBounds[1] < 0) {
+                mTranslateY = shadowBounds[1] * -1;
+            } else if (shadowBounds[3] > mShadowConfig.getHeight()) {
+                mTranslateY = shadowBounds[3] - mShadowConfig.getHeight();
+            }
+
+            Math3DHelper.translate(mCalculator.getVertex(), mTranslateX, mTranslateY, 2);
+
+            mTriangleBuffer.drawTriangles(mCalculator.getIndex(), mCalculator.getVertex(),
+                    mCalculator.getColor(), mShadowConfig.getShadowStrength());
+        } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
+            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
+                            "ambient shadow",
+                    mathError);
+        } catch (Exception ex) {
+            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Error while drawing shadow",
+                    ex);
+        }
+    }
+
+    public boolean isValid() {
+        return mValid;
+    }
+
+    public Bitmap getBitmap() {
+        return mTriangleBuffer.getImage();
+    }
+
+    public float getTranslateX() {
+        return mTranslateX;
+    }
+
+    public float getTranslateY() {
+        return mTranslateY;
+    }
+}
diff --git a/bridge/src/android/view/shadow/AmbientShadowConfig.java b/bridge/src/android/view/shadow/AmbientShadowConfig.java
new file mode 100644
index 0000000..b06bd0a
--- /dev/null
+++ b/bridge/src/android/view/shadow/AmbientShadowConfig.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+/**
+ * Model for ambient shadow rendering. Assumes light sources from centroid of the object.
+ */
+class AmbientShadowConfig {
+
+    private final int mWidth;
+    private final int mHeight;
+
+    private final float mEdgeScale;
+    private final float mShadowBoundRatio;
+    private final float mShadowStrength;
+
+    private final float[] mPolygon;
+
+    private final int mRays;
+    private final int mLayers;
+
+    private AmbientShadowConfig(Builder builder) {
+        mEdgeScale = builder.mEdgeScale;
+        mShadowBoundRatio = builder.mShadowBoundRatio;
+        mShadowStrength = builder.mShadowStrength;
+        mRays = builder.mRays;
+        mLayers = builder.mLayers;
+        mWidth = builder.mWidth;
+        mHeight = builder.mHeight;
+        mPolygon = builder.mPolygon;
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Returns scales intensity of the edge of the shadow (opacity) [0-100]
+     */
+    public float getEdgeScale() {
+        return mEdgeScale;
+    }
+
+    /**
+     * Returns scales the area (in xy) of the shadow [0-1]
+     */
+    public float getShadowBoundRatio() {
+        return mShadowBoundRatio;
+    }
+
+    /**
+     * Returns scales the intensity of the entire shadow (opacity) [0-1]
+     */
+    public float getShadowStrength() {
+        return mShadowStrength;
+    }
+
+    /**
+     * Returns opaque polygon to cast shadow
+     */
+    public float[] getPolygon() {
+        return mPolygon;
+    }
+
+    /**
+     * Returns # of rays to use in ray tracing. It determines the accuracy of outline (bounds) of
+     * the shadow.
+     */
+    public int getRays() {
+        return mRays;
+    }
+
+    /**
+     * Returns # of layers. It determines the intensity of the pen-umbra.
+     */
+    public int getLayers() {
+        return mLayers;
+    }
+
+    public static class Builder {
+
+        private float mEdgeScale;
+        private float mShadowBoundRatio;
+        private float mShadowStrength;
+        private int mRays;
+        private int mLayers;
+
+        private float[] mPolygon;
+
+        private int mWidth;
+        private int mHeight;
+
+        public Builder setEdgeScale(float edgeScale) {
+            mEdgeScale = edgeScale;
+            return this;
+        }
+
+        public Builder setShadowBoundRatio(float shadowBoundRatio) {
+            mShadowBoundRatio = shadowBoundRatio;
+            return this;
+        }
+
+        public Builder setShadowStrength(float shadowStrength) {
+            mShadowStrength = shadowStrength;
+            return this;
+        }
+
+        public Builder setRays(int rays) {
+            mRays = rays;
+            return this;
+        }
+
+        public Builder setLayers(int layers) {
+            mLayers = layers;
+            return this;
+        }
+
+        public Builder setSize(int width, int height) {
+            mWidth = width;
+            mHeight = height;
+            return this;
+        }
+
+        public Builder setPolygon(float[] polygon) {
+            mPolygon = polygon;
+            return this;
+        }
+
+        public AmbientShadowConfig build() {
+            return new AmbientShadowConfig(this);
+        }
+    }
+}
diff --git a/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java b/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
new file mode 100644
index 0000000..3bf747c
--- /dev/null
+++ b/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+import android.view.math.Math3DHelper;
+
+/**
+ * Generates vertices, colours, and indices required for ambient shadow. Ambient shadows are
+ * assumed to be raycasted from the centroid of the polygon, and reaches upto a ratio based on
+ * the polygon's z-height.
+ */
+class AmbientShadowVertexCalculator {
+
+    private final float[] mVertex;
+    private final float[] mColor;
+    private final int[] mIndex;
+    private final AmbientShadowConfig mConfig;
+
+    public AmbientShadowVertexCalculator(AmbientShadowConfig config) {
+        mConfig = config;
+
+        int rings = mConfig.getLayers() + 1;
+        int size = mConfig.getRays() * rings;
+
+        mVertex = new float[size * 2];
+        mColor = new float[size * 4];
+        mIndex = new int[(size * 2 + (mConfig.getRays() - 2)) * 3];
+    }
+
+    /**
+     * Generates vertex using the polygon info
+     * @param polygon 3d polygon info in format : {x1, y1, z1, x2, y2, z2 ...}
+     * @return true if vertices are generated with right colour/index. False otherwise.
+     */
+    public boolean generateVertex(float[] polygon) {
+        // Despite us not using z coord, we want calculations in 3d space as our polygon is using
+        // 3d coord system.
+        float[] centroidxy = new float[3];
+        int polygonLength = polygon.length/3;
+
+        Math3DHelper.centroid3d(polygon, polygonLength, centroidxy);
+
+        float cx = centroidxy[0];
+        float cy = centroidxy[1];
+
+        Rays rays = new Rays(mConfig.getRays());
+        int raysLength = rays.dx.length;
+        float rayDist[] = new float[mConfig.getRays()];
+
+        float[] rayHeights = new float[mConfig.getRays()];
+
+        for (int i = 0; i < raysLength; i++) {
+            float dx = rays.dx[i];
+            float dy = rays.dy[i];
+
+            float[] intersection = Math3DHelper.rayIntersectPoly(polygon, polygonLength, cx, cy,
+                    dx, dy, 3);
+            if (intersection.length == 1) {
+                return false;
+            }
+            rayDist[i] = intersection[0];
+            int index = (int) (intersection[2] * 3);
+            int index2 = (int) (((intersection[2] + 1) % polygonLength) * 3);
+            float h1 = polygon[index + 2] * mConfig.getShadowBoundRatio();
+            float h2 = polygon[index2 + 2] * mConfig.getShadowBoundRatio();
+            rayHeights[i] = h1 + intersection[1] * (h2 - h1);
+        }
+
+        int rings = mConfig.getLayers() + 1;
+        for (int i = 0; i < raysLength; i++) {
+            float dx = rays.dx[i];
+            float dy = rays.dy[i];
+            float cast = rayDist[i] * rayHeights[i];
+
+            float opacity = .8f * (0.5f / (mConfig.getEdgeScale() / 10f));
+            for (int j = 0; j < rings; j++) {
+                int p = i * rings + j;
+                float jf = j / (float) (rings - 1);
+                float t = rayDist[i] + jf * (cast - rayDist[i]);
+
+                mVertex[p * 2 + 0] = dx * t + cx;
+                mVertex[p * 2 + 1] = dy * t + cy;
+                // TODO: we might be able to optimize this in the future.
+                mColor[p * 4 + 0] = 0;
+                mColor[p * 4 + 1] = 0;
+                mColor[p * 4 + 2] = 0;
+                mColor[p * 4 + 3] = (1 - jf) * opacity;
+            }
+        }
+
+        int k = 0;
+        for (int i = 0; i < mConfig.getRays(); i++) {
+            for (int j = 0; j < mConfig.getLayers(); j++) {
+                int r1 = j + rings * i;
+                int r2 = j + rings * ((i + 1) % mConfig.getRays());
+
+                mIndex[k * 3 + 0] = r1;
+                mIndex[k * 3 + 1] = r1 + 1;
+                mIndex[k * 3 + 2] = r2;
+                k++;
+                mIndex[k * 3 + 0] = r2;
+                mIndex[k * 3 + 1] = r1 + 1;
+                mIndex[k * 3 + 2] = r2 + 1;
+                k++;
+            }
+        }
+        int ringOffset = 0;
+        for (int i = 1; i < mConfig.getRays() - 1; i++, k++) {
+            mIndex[k * 3 + 0] = ringOffset;
+            mIndex[k * 3 + 1] = ringOffset + rings * i;
+            mIndex[k * 3 + 2] = ringOffset + rings * (1 + i);
+        }
+        return true;
+    }
+
+    public int[] getIndex() {
+        return mIndex;
+    }
+
+    /**
+     * @return list of vertices in 2d in format : {x1, y1, x2, y2 ...}
+     */
+    public float[] getVertex() {
+        return mVertex;
+    }
+
+    public float[] getColor() {
+        return mColor;
+    }
+
+    private static class Rays {
+        public final float[] dx;
+        public final float[] dy;
+        public final double deltaAngle;
+
+        public Rays(int rays) {
+            dx = new float[rays];
+            dy = new float[rays];
+            deltaAngle = 2 * Math.PI / rays;
+
+            for (int i = 0; i < rays; i++) {
+                dx[i] = (float) Math.sin(deltaAngle * i);
+                dy[i] = (float) Math.cos(deltaAngle * i);
+            }
+        }
+    }
+
+}
diff --git a/bridge/src/android/view/shadow/HighQualityShadowPainter.java b/bridge/src/android/view/shadow/HighQualityShadowPainter.java
new file mode 100644
index 0000000..dd555c5
--- /dev/null
+++ b/bridge/src/android/view/shadow/HighQualityShadowPainter.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.view.ViewGroup;
+
+import static android.view.shadow.ShadowConstants.MIN_ALPHA;
+import static android.view.shadow.ShadowConstants.SCALE_DOWN;
+
+public class HighQualityShadowPainter {
+
+    private HighQualityShadowPainter() { }
+
+    /**
+     * Draws simple Rect shadow
+     */
+    public static void paintRectShadow(ViewGroup parent, Outline outline, float elevation,
+            Canvas canvas, float alpha, float densityDpi) {
+
+        if (!validate(elevation, densityDpi)) {
+            return;
+        }
+
+        int width = parent.getWidth() / SCALE_DOWN;
+        int height = parent.getHeight() / SCALE_DOWN;
+
+        Rect rectOriginal = new Rect();
+        Rect rectScaled = new Rect();
+        if (!outline.getRect(rectScaled) || alpha < MIN_ALPHA) {
+            // If alpha below MIN_ALPHA it's invisible (based on manual test). Save some perf.
+            return;
+        }
+
+        outline.getRect(rectOriginal);
+
+        rectScaled.left /= SCALE_DOWN;
+        rectScaled.right /= SCALE_DOWN;
+        rectScaled.top /= SCALE_DOWN;
+        rectScaled.bottom /= SCALE_DOWN;
+        float radius = outline.getRadius() / SCALE_DOWN;
+
+        if (radius > rectScaled.width() || radius > rectScaled.height()) {
+            // Rounded edge generation fails if radius is bigger than drawing box.
+            return;
+        }
+
+        // ensure alpha doesn't go over 1
+        alpha = (alpha > 1.0f) ? 1.0f : alpha;
+        float[] poly = getPoly(rectScaled, elevation / SCALE_DOWN, radius);
+
+        paintAmbientShadow(poly, canvas, width, height, alpha, rectOriginal, radius);
+        paintSpotShadow(poly, rectScaled, elevation / SCALE_DOWN,
+                canvas, densityDpi, width, height, alpha, rectOriginal, radius);
+    }
+
+    /**
+     * High quality shadow does not work well with object that is too high in elevation. Check if
+     * the object elevation is reasonable and returns true if shadow will work well. False other
+     * wise.
+     */
+    private static boolean validate(float elevation, float densityDpi) {
+        float scaledElevationPx = elevation / SCALE_DOWN;
+        float scaledSpotLightHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP *
+                (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
+        if (scaledElevationPx > scaledSpotLightHeightPx) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @param polygon - polygon of the shadow caster
+     * @param canvas - canvas to draw
+     * @param width - scaled canvas (parent) width
+     * @param height - scaled canvas (parent) height
+     * @param alpha - 0-1 scale
+     * @param shadowCasterOutline - unscaled original shadow caster outline.
+     * @param radius
+     */
+    private static void paintAmbientShadow(float[] polygon, Canvas canvas, int width, int height,
+            float alpha, Rect shadowCasterOutline, float radius) {
+        // TODO: Consider re-using the triangle buffer here since the world stays consistent.
+        // TODO: Reduce the buffer size based on shadow bounds.
+
+        AmbientShadowConfig config = new AmbientShadowConfig.Builder()
+                .setSize(width, height)
+                .setPolygon(polygon)
+                .setEdgeScale(ShadowConstants.AMBIENT_SHADOW_EDGE_SCALE)
+                .setShadowBoundRatio(ShadowConstants.AMBIENT_SHADOW_SHADOW_BOUND)
+                .setShadowStrength(ShadowConstants.AMBIENT_SHADOW_STRENGTH * alpha)
+                .setRays(ShadowConstants.AMBIENT_SHADOW_RAYS)
+                .setLayers(ShadowConstants.AMBIENT_SHADOW_LAYERS)
+                .build();
+
+        AmbientShadowBitmapGenerator generator = new AmbientShadowBitmapGenerator(config);
+        generator.populateShadow();
+
+        if (!generator.isValid()) {
+            return;
+        }
+
+        drawScaled(
+                canvas, generator.getBitmap(), (int) generator.getTranslateX(),
+                (int) generator.getTranslateY(), width, height,
+                shadowCasterOutline, radius);
+    }
+
+    /**
+     * @param poly - polygon of the shadow caster
+     * @param rectBound - scaled bounds of shadow caster.
+     * @param canvas - canvas to draw
+     * @param width - scaled canvas (parent) width
+     * @param height - scaled canvas (parent) height
+     * @param alpha - 0-1 scale
+     * @param shadowCasterOutline - unscaled original shadow caster outline.
+     * @param radius
+     */
+    private static void paintSpotShadow(float[] poly, Rect rectBound, float elevation, Canvas canvas,
+            float densityDpi, int width, int height, float alpha, Rect shadowCasterOutline,
+            float radius) {
+
+        // TODO: Use alpha later
+        float lightZHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP * (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
+        if (lightZHeightPx - elevation < ShadowConstants.SPOT_SHADOW_LIGHT_Z_EPSILON) {
+            // If the view is above or too close to the light source then return.
+            // This is done to somewhat simulate android behaviour.
+            return;
+        }
+
+        float lightX = (rectBound.left + rectBound.right) / 2;
+        float lightY = rectBound.top;
+        // Light shouldn't be bigger than the object by too much.
+        int dynamicLightRadius = Math.min(rectBound.width(), rectBound.height());
+
+        SpotShadowConfig config = new SpotShadowConfig.Builder()
+                .setSize(width, height)
+                .setLayers(ShadowConstants.SPOT_SHADOW_LAYERS)
+                .setRays(ShadowConstants.SPOT_SHADOW_RAYS)
+                .setLightCoord(lightX, lightY, lightZHeightPx)
+                .setLightRadius(dynamicLightRadius)
+                .setLightSourcePoints(ShadowConstants.SPOT_SHADOW_LIGHT_SOURCE_POINTS)
+                .setShadowStrength(ShadowConstants.SPOT_SHADOW_STRENGTH * alpha)
+                .setPolygon(poly, poly.length / ShadowConstants.COORDINATE_SIZE)
+                .build();
+
+        SpotShadowBitmapGenerator generator = new SpotShadowBitmapGenerator(config);
+        generator.populateShadow();
+
+        if (!generator.validate()) {
+            return;
+        }
+
+        drawScaled(canvas, generator.getBitmap(), (int) generator.getTranslateX(),
+                (int) generator.getTranslateY(), width, height, shadowCasterOutline, radius);
+    }
+
+    /**
+     * Draw the bitmap scaled up.
+     * @param translateX - offset in x axis by which the bitmap is shifted.
+     * @param translateY - offset in y axis by which the bitmap is shifted.
+     * @param width  - scaled width of canvas (parent)
+     * @param height - scaled height of canvas (parent)
+     * @param shadowCaster - unscaled outline of shadow caster
+     * @param radius
+     */
+    private static void drawScaled(Canvas canvas, Bitmap bitmap, int translateX, int translateY,
+            int width, int height, Rect shadowCaster, float radius) {
+        int unscaledTranslateX = translateX * SCALE_DOWN;
+        int unscaledTranslateY = translateY * SCALE_DOWN;
+
+        // To the canvas
+        Rect dest = new Rect(
+                -unscaledTranslateX,
+                -unscaledTranslateY,
+                (width * SCALE_DOWN) - unscaledTranslateX,
+                (height * SCALE_DOWN) - unscaledTranslateY);
+        Rect destSrc = new Rect(0, 0, width, height);
+
+        if (radius > 0) {
+            // Rounded edge.
+            int save = canvas.save();
+            canvas.drawBitmap(bitmap, destSrc, dest, null);
+            canvas.restoreToCount(save);
+            return;
+        }
+
+        /**
+         * ----------------------------------
+         * |                                |
+         * |              top               |
+         * |                                |
+         * ----------------------------------
+         * |      |                 |       |
+         * | left |  shadow caster  | right |
+         * |      |                 |       |
+         * ----------------------------------
+         * |                                |
+         * |            bottom              |
+         * |                                |
+         * ----------------------------------
+         *
+         * dest == top + left + shadow caster + right + bottom
+         * Visually, canvas.drawBitmap(bitmap, destSrc, dest, paint) would achieve the same result.
+         */
+        Rect left = new Rect(dest.left, shadowCaster.top, shadowCaster.left, shadowCaster.bottom);
+        int leftScaled = left.width() / SCALE_DOWN + destSrc.left;
+
+        Rect top = new Rect(dest.left, dest.top, dest.right, shadowCaster.top);
+        int topScaled = top.height() / SCALE_DOWN + destSrc.top;
+
+        Rect right = new Rect(shadowCaster.right, shadowCaster.top, dest.right,
+                shadowCaster.bottom);
+        int rightScaled = (shadowCaster.right + unscaledTranslateX) / SCALE_DOWN + destSrc.left;
+
+        Rect bottom = new Rect(dest.left, shadowCaster.bottom, dest.right, dest.bottom);
+        int bottomScaled = (bottom.bottom - bottom.height()) / SCALE_DOWN + destSrc.top;
+
+        // calculate parts of the middle ground that can be ignored.
+        Rect leftSrc = new Rect(destSrc.left, topScaled, leftScaled, bottomScaled);
+        Rect topSrc = new Rect(destSrc.left, destSrc.top, destSrc.right, topScaled);
+        Rect rightSrc = new Rect(rightScaled, topScaled, destSrc.right, bottomScaled);
+        Rect bottomSrc = new Rect(destSrc.left, bottomScaled, destSrc.right, destSrc.bottom);
+
+        int save = canvas.save();
+        Paint paint = new Paint();
+        canvas.drawBitmap(bitmap, leftSrc, left, paint);
+        canvas.drawBitmap(bitmap, topSrc, top, paint);
+        canvas.drawBitmap(bitmap, rightSrc, right, paint);
+        canvas.drawBitmap(bitmap, bottomSrc, bottom, paint);
+        canvas.restoreToCount(save);
+    }
+
+    private static float[] getPoly(Rect rect, float elevation, float radius) {
+        if (radius <= 0) {
+            float[] poly = new float[ShadowConstants.RECT_VERTICES_SIZE * ShadowConstants.COORDINATE_SIZE];
+
+            poly[0] = poly[9] = rect.left;
+            poly[1] = poly[4] = rect.top;
+            poly[3] = poly[6] = rect.right;
+            poly[7] = poly[10] = rect.bottom;
+            poly[2] = poly[5] = poly[8] = poly[11] = elevation;
+
+            return poly;
+        }
+
+        return buildRoundedEdges(rect, elevation, radius);
+    }
+
+    private static float[] buildRoundedEdges(
+            Rect rect, float elevation, float radius) {
+
+        float[] roundedEdgeVertices = new float[(ShadowConstants.SPLICE_ROUNDED_EDGE + 1) * 4 * 3];
+        int index = 0;
+        // 1.0 LT. From theta 0 to pi/2 in K division.
+        for (int i = 0; i <= ShadowConstants.SPLICE_ROUNDED_EDGE; i++) {
+            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
+            float x = (float) (rect.left + (radius - radius * Math.cos(theta)));
+            float y = (float) (rect.top + (radius - radius * Math.sin(theta)));
+            roundedEdgeVertices[index++] = x;
+            roundedEdgeVertices[index++] = y;
+            roundedEdgeVertices[index++] = elevation;
+        }
+
+        // 2.0 RT
+        for (int i = ShadowConstants.SPLICE_ROUNDED_EDGE; i >= 0; i--) {
+            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
+            float x = (float) (rect.right - (radius - radius * Math.cos(theta)));
+            float y = (float) (rect.top + (radius - radius * Math.sin(theta)));
+            roundedEdgeVertices[index++] = x;
+            roundedEdgeVertices[index++] = y;
+            roundedEdgeVertices[index++] = elevation;
+        }
+
+        // 3.0 RB
+        for (int i = 0; i <= ShadowConstants.SPLICE_ROUNDED_EDGE; i++) {
+            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
+            float x = (float) (rect.right - (radius - radius * Math.cos(theta)));
+            float y = (float) (rect.bottom - (radius - radius * Math.sin(theta)));
+            roundedEdgeVertices[index++] = x;
+            roundedEdgeVertices[index++] = y;
+            roundedEdgeVertices[index++] = elevation;
+        }
+
+        // 4.0 LB
+        for (int i = ShadowConstants.SPLICE_ROUNDED_EDGE; i >= 0; i--) {
+            double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
+            float x = (float) (rect.left + (radius - radius * Math.cos(theta)));
+            float y = (float) (rect.bottom - (radius - radius * Math.sin(theta)));
+            roundedEdgeVertices[index++] = x;
+            roundedEdgeVertices[index++] = y;
+            roundedEdgeVertices[index++] = elevation;
+        }
+
+        return roundedEdgeVertices;
+    }
+}
diff --git a/bridge/src/android/view/shadow/ShadowConstants.java b/bridge/src/android/view/shadow/ShadowConstants.java
new file mode 100644
index 0000000..83617d8
--- /dev/null
+++ b/bridge/src/android/view/shadow/ShadowConstants.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+/**
+ * Constant values for shadow related configuration
+ */
+class ShadowConstants {
+
+    /**
+     * This is used as a factor by which to scale down the shadow bitmap. If we have world
+     * Width x Height, shadow bitmap will be Width/SCALE_DOWN x Height/SCALE_DOWN and during
+     * canvas draw the shadow will be scaled up, resulting faster perf (due to smaller bitmap) but
+     * blurrier (lower quality) shadow.
+     */
+    public static final int SCALE_DOWN = 5;
+
+    public static final float MIN_ALPHA = 0.2f;
+
+    public static final int SPOT_SHADOW_RAYS = 40;
+    public static final int SPOT_SHADOW_LAYERS = 1;
+    public static final int SPOT_SHADOW_LIGHT_SOURCE_POINTS = 4;
+    public static final int SPOT_SHADOW_LIGHT_Z_HEIGHT_DP = 50 / SCALE_DOWN;
+    public static final int SPOT_SHADOW_LIGHT_Z_EPSILON = 10 / SCALE_DOWN;
+    public static final float SPOT_SHADOW_STRENGTH = 0.3f;
+
+    public static final float AMBIENT_SHADOW_EDGE_SCALE = 60f;
+    public static final float AMBIENT_SHADOW_SHADOW_BOUND = 0.02f * SCALE_DOWN;
+    public static final int AMBIENT_SHADOW_RAYS = 120;
+    public static final int AMBIENT_SHADOW_LAYERS = 1;
+    public static final float AMBIENT_SHADOW_STRENGTH = 1.0f;
+
+    public static final int COORDINATE_SIZE = 3;
+    public static final int RECT_VERTICES_SIZE = 4;
+
+    public static final int SPLICE_ROUNDED_EDGE = 5;
+}
diff --git a/bridge/src/android/view/shadow/SpotShadowBitmapGenerator.java b/bridge/src/android/view/shadow/SpotShadowBitmapGenerator.java
new file mode 100644
index 0000000..1aa2258
--- /dev/null
+++ b/bridge/src/android/view/shadow/SpotShadowBitmapGenerator.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+
+import android.graphics.Bitmap;
+import android.view.math.Math3DHelper;
+
+/**
+ * Generate spot shadow bitmap.
+ */
+class SpotShadowBitmapGenerator {
+
+    private final SpotShadowConfig mShadowConfig;
+    private final TriangleBuffer mTriangle;
+    private float[] mStrips;
+    private float[] mLightSources;
+    private float mTranslateX;
+    private float mTranslateY;
+
+    public SpotShadowBitmapGenerator(SpotShadowConfig config) {
+        // TODO: Reduce the buffer size based on shadow bounds.
+        mTriangle = new TriangleBuffer();
+        mShadowConfig = config;
+        // For now assume no change to the world size
+        mTriangle.setSize(config.getWidth(), config.getHeight(), 0);
+    }
+
+    /**
+     * Populate the shadow bitmap.
+     */
+    public void populateShadow() {
+        try {
+            mLightSources = SpotShadowVertexCalculator.calculateLight(
+                    mShadowConfig.getLightRadius(),
+                    mShadowConfig.getLightSourcePoints(),
+                    mShadowConfig.getLightCoord()[0],
+                    mShadowConfig.getLightCoord()[1],
+                    mShadowConfig.getLightCoord()[2]);
+
+            mStrips = new float[3 * SpotShadowVertexCalculator.getStripSize(
+                    mShadowConfig.getRays(),
+                    mShadowConfig.getLayers())];
+
+            if (SpotShadowVertexCalculator.calculateShadow(
+                    mLightSources,
+                    mShadowConfig.getLightSourcePoints(),
+                    mShadowConfig.getPoly(),
+                    mShadowConfig.getPolyLength(),
+                    mShadowConfig.getRays(),
+                    mShadowConfig.getLayers(),
+                    mShadowConfig.getShadowStrength(),
+                    mStrips) != 1) {
+                return;
+            }
+
+            // Bit of a hack to re-adjust spot shadow to fit correctly within parent canvas.
+            // Problem is that outline passed is not a final position, which throws off our
+            // whereas our shadow rendering algorithm, which requires pre-set range for
+            // optimization purposes.
+            float[] shadowBounds = Math3DHelper.flatBound(mStrips, 3);
+
+            if ((shadowBounds[2] - shadowBounds[0]) > mShadowConfig.getWidth() ||
+                    (shadowBounds[3] - shadowBounds[1]) > mShadowConfig.getHeight()) {
+                // Spot shadow to be casted is larger than the parent canvas,
+                // We'll let ambient shadow do the trick and skip spot shadow here.
+                return;
+            }
+
+            mTranslateX = 0;
+            mTranslateY = 0;
+            if (shadowBounds[0] < 0) {
+                // translate to right by the offset amount.
+                mTranslateX = shadowBounds[0] * -1;
+            } else if (shadowBounds[2] > mShadowConfig.getWidth()) {
+                // translate to left by the offset amount.
+                mTranslateX = shadowBounds[2] - mShadowConfig.getWidth();
+            }
+
+            if (shadowBounds[1] < 0) {
+                mTranslateY = shadowBounds[1] * -1;
+            } else if (shadowBounds[3] > mShadowConfig.getHeight()) {
+                mTranslateY = shadowBounds[3] - mShadowConfig.getHeight();
+            }
+            Math3DHelper.translate(mStrips, mTranslateX, mTranslateY, 3);
+
+            mTriangle.drawTriangles(mStrips, mShadowConfig.getShadowStrength());
+        } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
+            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Arithmetic error while drawing " +
+                            "spot shadow",
+                    mathError);
+        } catch (Exception ex) {
+            Bridge.getLog().warning(LayoutLog.TAG_INFO,  "Error while drawing shadow",
+                    ex);
+        }
+    }
+
+    public float getTranslateX() {
+        return mTranslateX;
+    }
+
+    public float getTranslateY() {
+        return mTranslateY;
+    }
+
+    public void clear() {
+        mTriangle.clear();
+    }
+
+    /**
+     * @return true if generated shadow poly is valid. False otherwise.
+     */
+    public boolean validate() {
+        return mStrips != null && mStrips.length >= 9;
+    }
+
+    /**
+     * @return the bitmap of shadow after it's populated
+     */
+    public Bitmap getBitmap() {
+        return mTriangle.getImage();
+    }
+
+    @VisibleForTesting
+    public float[] getStrips() {
+        return mStrips;
+    }
+
+    @VisibleForTesting
+    public void updateLightSource(float x, float y) {
+        mShadowConfig.setLightCoord(x, y);
+    }
+}
diff --git a/bridge/src/android/view/shadow/SpotShadowConfig.java b/bridge/src/android/view/shadow/SpotShadowConfig.java
new file mode 100644
index 0000000..56a7524
--- /dev/null
+++ b/bridge/src/android/view/shadow/SpotShadowConfig.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+
+/**
+ * Model for spot shadow rendering. Assumes single light, single object.
+ */
+class SpotShadowConfig {
+    private final int mWidth;
+    private final int mHeight;
+
+    // No need to be final but making it immutable for now.
+    private final int mLightRadius;
+    private final int mLightSourcePoints;
+
+    // No need to be final but making it immutable for now.
+    private final int mRays;
+    private final int mLayers;
+
+    // No need to be final but making it immutable for now.
+    private final float[] mPoly;
+    private final int mPolyLength;
+
+    private float[] mLightCoord;
+
+    private final float mShadowStrength;
+
+    private SpotShadowConfig(SpotShadowConfig.Builder builder) {
+        mWidth = builder.mWidth;
+        mHeight = builder.mHeight;
+        mLightRadius = builder.mLightRadius;
+        mLightSourcePoints = builder.mLightSourcePoints;
+        mRays = builder.mRays;
+        mLayers = builder.mLayers;
+        mPoly = builder.mPoly;
+        mPolyLength = builder.mPolyLength;
+
+        mLightCoord = new float[3];
+        mLightCoord[0] = builder.mLightX;
+        mLightCoord[1] = builder.mLightY;
+        mLightCoord[2] = builder.mLightHeight;
+        mShadowStrength = builder.mShadowStrength;
+    }
+
+    /**
+     * World width / height
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * @return number of light source points to ray trace
+     */
+    public int getLightSourcePoints() {
+        return mLightSourcePoints;
+    }
+
+    /**
+     * @return size of the light source radius (light source is always generated as a circular shape)
+     */
+    public int getLightRadius() {
+        return mLightRadius;
+    }
+
+    /**
+     * @return object that casts shadow. xyz coordinates.
+     */
+    public float[] getPoly() {
+        return mPoly;
+    }
+
+    /**
+     * @return # of vertices in the object {@link #getPoly()} that casts shadow.
+     */
+    public int getPolyLength() {
+        return mPolyLength;
+    }
+
+    /**
+     * @return number of rays to use in raytracing. It determines the accuracy of outline (bounds) of
+     * the shadow.
+     */
+    public int getRays() {
+        return mRays;
+    }
+
+    /**
+     * @return number of layers. It determines the intensity of pen-umbra
+     */
+    public int getLayers() {
+        return mLayers;
+    }
+
+    /**
+     * Update the light source coord.
+     * @param x - x in {@link #getWidth()} coordinate
+     * @param y - y in {@link #getHeight()} coordinate
+     */
+    public void setLightCoord(float x, float y) {
+        mLightCoord[0] = x;
+        mLightCoord[1] = y;
+    }
+
+    /**
+     * @return shadow intensity from 0 to 1
+     */
+    public float getShadowStrength() {
+        return mShadowStrength;
+    }
+
+    public float[] getLightCoord() {
+        return mLightCoord;
+    }
+
+    public static class Builder {
+
+        private int mWidth;
+        private int mHeight;
+
+        // No need to be final but making it immutable for now.
+        private int mLightRadius;
+        private int mLightSourcePoints;
+
+        // No need to be final but making it immutable for now.
+        private int mRays;
+        private int mLayers;
+
+        // No need to be final but making it immutable for now.
+        private float[] mPoly;
+        private int mPolyLength;
+
+        private float mLightX;
+        private float mLightY;
+        private float mLightHeight;
+
+        private float mShadowStrength;
+
+        /**
+         * @param shadowStrength from 0 to 1
+         */
+        public Builder setShadowStrength(float shadowStrength) {
+            this.mShadowStrength = shadowStrength;
+            return this;
+        }
+
+        public Builder setSize(int width, int height) {
+            mWidth = width;
+            mHeight = height;
+            return this;
+        }
+
+        public Builder setLightRadius(int mLightRadius) {
+            this.mLightRadius = mLightRadius;
+            return this;
+        }
+
+        public Builder setLightSourcePoints(int mLightSourcePoints) {
+            this.mLightSourcePoints = mLightSourcePoints;
+            return this;
+        }
+
+        public Builder setRays(int mRays) {
+            this.mRays = mRays;
+            return this;
+        }
+
+        public Builder setLayers(int mLayers) {
+            this.mLayers = mLayers;
+            return this;
+        }
+
+        public Builder setPolygon(float[] poly, int polyLength) {
+            this.mPoly = poly;
+            this.mPolyLength = polyLength;
+            return this;
+        }
+
+        public Builder setLightCoord(float lightX, float lightY, float lightHeight) {
+            this.mLightX = lightX;
+            this.mLightY = lightY;
+            this.mLightHeight = lightHeight;
+            return this;
+        }
+
+        public SpotShadowConfig build() {
+            return new SpotShadowConfig(this);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java b/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
new file mode 100644
index 0000000..2e4191b
--- /dev/null
+++ b/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+import android.view.math.Math3DHelper;
+
+/**
+ * Generates the vertices required for spot shadow and all other shadow-related rendering.
+ */
+class SpotShadowVertexCalculator {
+
+    private SpotShadowVertexCalculator() { }
+
+    /**
+     * Create evenly distributed circular light source points from x and y (on flat z plane).
+     * This is useful for ray tracing the shadow points later. Format : (x1,y1,z1,x2,y2,z2 ...)
+     *
+     * @param radius - radius of the light source
+     * @param points - how many light source points to generate
+     * @param x - center X of the light source
+     * @param y - center Y of the light source
+     * @param height - how high (z depth) the light should be
+     * @return float points (x,y,z) of light source points.
+     */
+    public static float[] calculateLight(float radius, int points, float x, float y, float height) {
+        float[] ret = new float[points * 3];
+        for (int i = 0; i < points; i++) {
+            double angle = 2 * i * Math.PI / points;
+            ret[i * 3] = (float) Math.sin(angle) * radius + x;
+            ret[i * 3 + 1] = (float) Math.cos(angle) * radius + y;
+            ret[i * 3 + 2] = (height);
+        }
+
+        return ret;
+    }
+
+    /**
+     * @param rays - Number of rays to use for tracing
+     * @param layers - Number of layers for shadow rendering.
+     * @return size required for shadow vertices mData array based on # of rays and layers
+     */
+    public static int getStripSize(int rays, int layers){
+        return  (2 + rays + ((layers) * 2 * (rays + 1)));
+    }
+
+    /**
+     * Generate shadow vertices based on params. Format : (x1,y1,z1,x2,y2,z2 ...)
+     * Precondition : Light poly must be evenly distributed on a flat surface
+     * Precondition : Poly vertices must be a convex
+     * Precondition : Light height must be higher than any poly vertices
+     *
+     * @param lightPoly - Vertices of a light source.
+     * @param lightPolyLength - Size of the vertices (usually lightPoly.length/3 unless w is
+     * included)
+     * @param poly - Vertices of opaque object casting shadow
+     * @param polyLength - Size of the vertices
+     * @param rays - Number of rays to use for tracing. It determines accuracy of the outline
+     * (bounds) of the shadow
+     * @param layers - Number of layers for shadow. It determines intensity of pen-umbra
+     * @param strength - Strength of the shadow overall [0-1]
+     * @param retstrips - Array mData to be filled in format : {x1, y1, z1, x2, y2, z2}
+     * @return 1 if successful, error code otherwise.
+     */
+    public static int calculateShadow(
+            float[] lightPoly,
+            int lightPolyLength,
+            float[] poly,
+            int polyLength,
+            int rays,
+            int layers,
+            float strength,
+            float[] retstrips) {
+        float[] shadowRegion = new float[lightPolyLength * polyLength * 2];
+        float[] outline = new float[polyLength * 2];
+        float[] umbra = new float[polyLength * lightPolyLength * 2];
+        int umbraLength = 0;
+
+        int k = 0;
+        for (int j = 0; j < lightPolyLength; j++) {
+            int m = 0;
+            for (int i = 0; i < polyLength; i++) {
+                float t = lightPoly[j * 3 + 2] - poly[i * 3 + 2];
+                if (t == 0) {
+                    return 0;
+                }
+                t = lightPoly[j * 3 + 2] / t;
+                float x = lightPoly[j * 3] - t * (lightPoly[j * 3] - poly[i * 3]);
+                float y = lightPoly[j * 3 + 1] - t * (lightPoly[j * 3 + 1] - poly[i * 3 + 1]);
+
+                shadowRegion[k * 2] = x;
+                shadowRegion[k * 2 + 1] = y;
+                outline[m * 2] = x;
+                outline[m * 2 + 1] = y;
+
+                k++;
+                m++;
+            }
+
+            if (umbraLength == 0) {
+                for (int i = 0; i < polyLength * 2; i++) {
+                    umbra[i] = outline[i];
+                }
+                umbraLength = polyLength;
+            } else {
+                umbraLength = Math3DHelper.intersection(outline, polyLength, umbra, umbraLength);
+                if (umbraLength == 0) {
+                    break;
+                }
+
+            }
+        }
+        int shadowRegionLength = k;
+
+        float[] penumbra = new float[k * 2];
+        int penumbraLength = Math3DHelper.hull(shadowRegion, shadowRegionLength, penumbra);
+        if (umbraLength < 3) {// no real umbra make a fake one
+            float[] p = new float[3];
+            Math3DHelper.centroid3d(lightPoly, lightPolyLength, p);
+            float[] centShadow = new float[polyLength * 2];
+            for (int i = 0; i < polyLength; i++) {
+                float t = p[2] - poly[i * 3 + 2];
+                if (t == 0) {
+                    return 0;
+                }
+                t = p[2] / t;
+                float x = p[0] - t * (p[0] - poly[i * 3]);
+                float y = p[1] - t * (p[1] - poly[i * 3 + 1]);
+
+                centShadow[i * 2] = x;
+                centShadow[i * 2 + 1] = y;
+            }
+            float[] c = new float[2];
+            Math3DHelper.centroid2d(centShadow, polyLength, c);
+            for (int i = 0; i < polyLength; i++) {
+                centShadow[i * 2] = (c[0] * 9 + centShadow[i * 2]) / 10;
+                centShadow[i * 2 + 1] = (c[1] * 9 + centShadow[i * 2 + 1]) / 10;
+            }
+            umbra = centShadow; // fake umbra
+            umbraLength = polyLength; // same size as the original polygon
+        }
+
+        Math3DHelper.donutPie2(penumbra, penumbraLength, umbra, umbraLength, rays,
+                layers, strength, retstrips);
+        return 1;
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/android/view/shadow/TriangleBuffer.java b/bridge/src/android/view/shadow/TriangleBuffer.java
new file mode 100644
index 0000000..8db8e1c
--- /dev/null
+++ b/bridge/src/android/view/shadow/TriangleBuffer.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.shadow;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+
+import java.util.Arrays;
+
+import static android.view.math.Math3DHelper.max;
+import static android.view.math.Math3DHelper.min;
+
+/**
+ * 2D Triangle buffer element that colours using z value. (z scale set).
+ */
+class TriangleBuffer {
+    int mWidth;
+    int mHeight;
+    int mImgWidth;
+    int mImgHeight;
+    int mBorder;
+    Bitmap mBitmap;
+    int mData[];
+    private float mMinX;
+    private float mMaxX;
+    private float mMinY;
+    private float mMaxY;
+
+    public void setSize(int width, int height, int border) {
+        if (mWidth == width && mHeight == height) {
+            return;
+        }
+        mWidth = width-2*border;
+        mHeight = height-2*border;
+        mBorder = border;
+        mImgWidth = width;
+        mImgHeight = height;
+
+        setScale(0, width, 0, height);
+
+        mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+        mData = new int[width * height];
+    }
+
+    public void drawTriangles(int[] index, float[] vert, float[] color,float scale) {
+        int indexSize = index.length / 3;
+        for (int i = 0; i < indexSize; i++) {
+            int vIndex = index[i * 3 + 0];
+            float vx = vert[vIndex * 2 + 0];
+            float vy = vert[vIndex * 2 + 1];
+            float c =  scale*color[vIndex * 4 + 3];
+            float fx3 = vx, fy3 = vy, fz3 = c;
+
+            vIndex = index[i * 3 + 1];
+            vx = vert[vIndex * 2 + 0];
+            vy = vert[vIndex * 2 + 1];
+            c =  scale*color[vIndex * 4 + 3];
+            float fx2 = vx, fy2 = vy, fz2 = c;
+
+            vIndex = index[i * 3 + 2];
+            vx = vert[vIndex * 2 + 0];
+            vy = vert[vIndex * 2 + 1];
+            c =  scale*color[vIndex * 4 + 3];
+            float fx1 = vx, fy1 = vy, fz1 = c;
+
+            triangleZBuffMin(mData, mImgWidth, mImgHeight, fx3, fy3, fz3, fx2, fy2,
+                    fz2, fx1, fy1, fz1);
+            triangleZBuffMin(mData, mImgWidth, mImgHeight, fx1, fy1, fz1, fx2, fy2,
+                    fz2, fx3, fy3, fz3);
+        }
+        mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
+    }
+
+    public void drawTriangles(float[] strip,float scale) {
+        for (int i = 0; i < strip.length-8; i+=3) {
+            float fx3 = strip[i], fy3 = strip[i+1], fz3 = scale* strip[i+2];
+            float fx2 = strip[i+3], fy2 = strip[i+4], fz2 = scale* strip[i+5];
+            float fx1 = strip[i+6], fy1 = strip[i+7], fz1 = scale* strip[i+8];
+
+            if (fx1*(fy2-fy3)+fx2*(fy3-fy1)+fx3*(fy1-fy2) ==0) {
+                continue;
+            }
+            triangleZBuffMin(mData, mImgWidth, mImgHeight, fx3, fy3, fz3, fx2, fy2,
+                    fz2, fx1, fy1, fz1);
+        }
+        mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
+    }
+
+    public Bitmap getImage() {
+        return mBitmap;
+    }
+
+    private static void triangleZBuffMin(int[] buff, int w, int h, float fx3,
+            float fy3, float fz3, float fx2, float fy2, float fz2, float fx1,
+            float fy1, float fz1) {
+        if (((fx1 - fx2) * (fy3 - fy2) - (fy1 - fy2) * (fx3 - fx2)) < 0) {
+            float tmpx = fx1;
+            float tmpy = fy1;
+            float tmpz = fz1;
+            fx1 = fx2;
+            fy1 = fy2;
+            fz1 = fz2;
+            fx2 = tmpx;
+            fy2 = tmpy;
+            fz2 = tmpz;
+        }
+        // using maxmima
+        // solve([x1*dx+y1*dy+zoff=z1,x2*dx+y2*dy+zoff=z2,x3*dx+y3*dy+zoff=z3],[dx,dy,zoff]);
+        double d = (fx1 * (fy3 - fy2) - fx2 * fy3 + fx3 * fy2 + (fx2 - fx3) * fy1);
+        if (d == 0) {
+            return;
+        }
+        float dx = (float) (-(fy1 * (fz3 - fz2) - fy2 * fz3 + fy3 * fz2 + (fy2 - fy3)
+                * fz1) / d);
+        float dy = (float) ((fx1 * (fz3 - fz2) - fx2 * fz3 + fx3 * fz2 + (fx2 - fx3)
+                * fz1) / d);
+        float zoff = (float) ((fx1 * (fy3 * fz2 - fy2 * fz3) + fy1
+                * (fx2 * fz3 - fx3 * fz2) + (fx3 * fy2 - fx2 * fy3) * fz1) / d);
+
+        // 28.4 fixed-point coordinates
+        int y1 = (int) (16.0f * fy1 + .5f);
+        int y2 = (int) (16.0f * fy2 + .5f);
+        int y3 = (int) (16.0f * fy3 + .5f);
+
+        int x1 = (int) (16.0f * fx1 + .5f);
+        int x2 = (int) (16.0f * fx2 + .5f);
+        int x3 = (int) (16.0f * fx3 + .5f);
+
+        int dx12 = x1 - x2;
+        int dx23 = x2 - x3;
+        int dx31 = x3 - x1;
+
+        int dy12 = y1 - y2;
+        int dy23 = y2 - y3;
+        int dy31 = y3 - y1;
+
+        int fdx12 = dx12 << 4;
+        int fdx23 = dx23 << 4;
+        int fdx31 = dx31 << 4;
+
+        int fdy12 = dy12 << 4;
+        int fdy23 = dy23 << 4;
+        int fdy31 = dy31 << 4;
+
+        int minx = (min(x1, x2, x3) + 0xF) >> 4;
+        int maxx = (max(x1, x2, x3) + 0xF) >> 4;
+        int miny = (min(y1, y2, y3) + 0xF) >> 4;
+        int maxy = (max(y1, y2, y3) + 0xF) >> 4;
+
+        if (miny < 0) {
+            miny = 0;
+        }
+        if (minx < 0) {
+            minx = 0;
+        }
+        if (maxx > w) {
+            maxx = w;
+        }
+        if (maxy > h) {
+            maxy = h;
+        }
+        int off = miny * w;
+
+        int c1 = dy12 * x1 - dx12 * y1;
+        int c2 = dy23 * x2 - dx23 * y2;
+        int c3 = dy31 * x3 - dx31 * y3;
+
+        if (dy12 < 0 || (dy12 == 0 && dx12 > 0)) {
+            c1++;
+        }
+        if (dy23 < 0 || (dy23 == 0 && dx23 > 0)) {
+            c2++;
+        }
+        if (dy31 < 0 || (dy31 == 0 && dx31 > 0)) {
+            c3++;
+        }
+        int cy1 = c1 + dx12 * (miny << 4) - dy12 * (minx << 4);
+        int cy2 = c2 + dx23 * (miny << 4) - dy23 * (minx << 4);
+        int cy3 = c3 + dx31 * (miny << 4) - dy31 * (minx << 4);
+
+        for (int y = miny; y < maxy; y++) {
+            int cx1 = cy1;
+            int cx2 = cy2;
+            int cx3 = cy3;
+            float p = zoff + dy * y;
+            for (int x = minx; x < maxx; x++) {
+                if (cx1 > 0 && cx2 > 0 && cx3 > 0) {
+                    int point = x + off;
+                    float zval = p + dx * x;
+                    buff[point] = ((int) (zval * 255)) << 24;
+                }
+                cx1 -= fdy12;
+                cx2 -= fdy23;
+                cx3 -= fdy31;
+            }
+            cy1 += fdx12;
+            cy2 += fdx23;
+            cy3 += fdx31;
+            off += w;
+        }
+    }
+
+    private void setScale(float minx, float maxx, float miny, float maxy) {
+        mMinX = minx;
+        mMaxX = maxx;
+        mMinY = miny;
+        mMaxY = maxy;
+    }
+
+    public void clear() {
+        Arrays.fill(mData, 0);
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 5dca8e7..4072bf3 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -21,6 +21,8 @@
 import com.android.ide.common.rendering.api.Features;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.Result.Status;
 import com.android.ide.common.rendering.api.SessionParams;
@@ -30,15 +32,16 @@
 import com.android.layoutlib.bridge.util.DynamicIdMap;
 import com.android.ninepatch.NinePatchChunk;
 import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.Nullable;
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 import com.android.util.Pair;
 
-import android.annotation.NonNull;
 import android.content.res.BridgeAssetManager;
 import android.graphics.Bitmap;
 import android.graphics.FontFamily_Delegate;
 import android.graphics.Typeface;
+import android.graphics.Typeface_Builder_Delegate;
 import android.graphics.Typeface_Delegate;
 import android.icu.util.ULocale;
 import android.os.Looper;
@@ -61,6 +64,8 @@
 
 import libcore.io.MemoryMappedFile_Delegate;
 
+import static android.graphics.Typeface.DEFAULT_FAMILY;
+import static android.graphics.Typeface.RESOLVE_BY_FONT_TABLE;
 import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
 
 /**
@@ -141,6 +146,8 @@
      */
     private static LayoutLog sCurrentLog = sDefaultLog;
 
+    public static boolean sIsTypefaceInitialized;
+
     private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR;
 
     @Override
@@ -165,6 +172,7 @@
     @Override
     public boolean init(Map<String,String> platformProperties,
             File fontLocation,
+            String icuDataPath,
             Map<String, Map<String, Integer>> enumValueMap,
             LayoutLog log) {
         sPlatformProperties = platformProperties;
@@ -353,8 +361,9 @@
         BridgeAssetManager.clearSystem();
 
         // dispose of the default typeface.
-        Typeface_Delegate.resetDefaults();
-        Typeface.sDynamicTypefaceCache.evictAll();
+        if (sIsTypefaceInitialized) {
+            Typeface.sDynamicTypefaceCache.evictAll();
+        }
         sProject9PatchCache.clear();
         sProjectBitmapCache.clear();
 
@@ -493,10 +502,15 @@
      * will do the clean-up, and make the thread unable to do further scene actions.
      */
     public synchronized static void prepareThread() {
-        // we need to make sure the Looper has been initialized for this thread.
-        // this is required for View that creates Handler objects.
+        // We need to make sure the Looper has been initialized for this thread.
+        // This is required for View that creates Handler objects.
         if (Looper.myLooper() == null) {
-            Looper.prepareMainLooper();
+            synchronized (Looper.class) {
+                // Check if the main looper has been prepared already.
+                if (Looper.getMainLooper() == null) {
+                    Looper.prepareMainLooper();
+                }
+            }
         }
     }
 
@@ -530,17 +544,20 @@
 
     /**
      * Returns details of a framework resource from its integer value.
-     * @param value the integer value
-     * @return a Pair containing the resource type and name, or null if the id
-     *     does not match any resource.
+     *
+     * <p>TODO(namespaces): remove this and just do all id resolution through the callback.
      */
-    @SuppressWarnings("deprecation")
-    public static Pair<ResourceType, String> resolveResourceId(int value) {
+    @Nullable
+    public static ResourceReference resolveResourceId(int value) {
         Pair<ResourceType, String> pair = sRMap.get(value);
         if (pair == null) {
             pair = sDynamicIds.resolveId(value);
         }
-        return pair;
+
+        if (pair != null) {
+            return new ResourceReference(ResourceNamespace.ANDROID, pair.getFirst(), pair.getSecond());
+        }
+        return null;
     }
 
     /**
@@ -550,24 +567,18 @@
      *
      * @param type the type of the resource
      * @param name the name of the resource.
-     *
-     * @return an {@link Integer} containing the resource id.
+     * @return an int containing the resource id.
      */
-    @NonNull
-    public static Integer getResourceId(ResourceType type, String name) {
+    public static int getResourceId(ResourceType type, String name) {
         Map<String, Integer> map = sRevRMap.get(type);
-        Integer value = null;
-        if (map != null) {
-            value = map.get(name);
-        }
-
+        Integer value = map == null ? null : map.get(name);
         return value == null ? sDynamicIds.getId(type, name) : value;
-
     }
 
     /**
      * Returns the list of possible enums for a given attribute name.
      */
+    @Nullable
     public static Map<String, Integer> getEnumValues(String attributeName) {
         if (sEnumValueMap != null) {
             return sEnumValueMap.get(attributeName);
@@ -669,4 +680,14 @@
             sFramework9PatchCache.put(value, new SoftReference<>(ninePatch));
         }
     }
+
+    @Override
+    public void clearFontCache(String path) {
+        if (sIsTypefaceInitialized) {
+            final String key =
+                    Typeface_Builder_Delegate.createAssetUid(BridgeAssetManager.initSystem(), path,
+                            0, null, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, DEFAULT_FAMILY);
+            Typeface.sDynamicTypefaceCache.remove(key);
+        }
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index dd2668f..842d82a 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -17,11 +17,12 @@
 package com.android.layoutlib.bridge;
 
 import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.tools.layoutlib.java.System_Delegate;
-import com.android.util.PropertiesMap;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -72,8 +73,19 @@
     }
 
     @Override
-    public Map<Object, PropertiesMap> getDefaultProperties() {
-        return mSession != null ? mSession.getDefaultProperties() : Collections.emptyMap();
+    public Map<Object, Map<ResourceReference, ResourceValue>> getDefaultNamespacedProperties() {
+        return mSession != null ? mSession.getDefaultNamespacedProperties() :
+                Collections.emptyMap();
+    }
+
+    @Override
+    public Map<Object, String> getDefaultStyles() {
+        return mSession != null ? mSession.getDefaultStyles() : Collections.emptyMap();
+    }
+
+    @Override
+    public Map<Object, ResourceReference> getDefaultNamespacedStyles() {
+        return mSession != null ? mSession.getDefaultNamespacedStyles() : Collections.emptyMap();
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
index faaf105..2dfbd24 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
@@ -18,13 +18,13 @@
 
 import com.android.layoutlib.bridge.impl.RenderAction;
 
-import android.icu.util.ULocale;
+import android.os.LocaleList;
 
 import java.util.Locale;
 
 /**
- * This class provides an alternate implementation for {@code java.util.Locale#toLanguageTag}
- * which is only available after Java 6.
+ * This class provides an alternate implementation for {@code java.util.Locale#adjustLanguageCode}
+ * which is not available in openJDK. It also overrides the getDefault method.
  *
  * The create tool re-writes references to the above mentioned method to this one. Hence it's
  * imperative that this class is not deleted unless the create tool is modified.
@@ -32,10 +32,6 @@
 @SuppressWarnings("UnusedDeclaration")
 public class AndroidLocale {
 
-    public static String toLanguageTag(Locale locale)  {
-        return ULocale.forLocale(locale).toLanguageTag();
-    }
-
     public static String adjustLanguageCode(String languageCode) {
         String adjusted = languageCode.toLowerCase(Locale.US);
         // Map new language codes to the obsolete language
@@ -51,20 +47,12 @@
         return adjusted;
     }
 
-    public static Locale forLanguageTag(String tag) {
-        return ULocale.forLanguageTag(tag).toLocale();
-    }
-
-    public static String getScript(Locale locale) {
-        return ULocale.forLocale(locale).getScript();
-    }
-
     public static Locale getDefault() {
         BridgeContext context = RenderAction.getCurrentContext();
         if (context != null) {
-            Locale locale = context.getConfiguration().locale;
-            if (locale != null) {
-                return locale;
+            LocaleList localeList = context.getConfiguration().getLocales();
+            if (!localeList.isEmpty()) {
+                return localeList.get(0);
             }
         }
         return Locale.getDefault();
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index c827f17..d8bef78 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -40,7 +40,7 @@
  */
 public final class BridgeContentProvider implements IContentProvider {
     @Override
-    public ContentProviderResult[] applyBatch(String callingPackage,
+    public ContentProviderResult[] applyBatch(String callingPackage, String authority,
             ArrayList<ContentProviderOperation> arg0)
             throws RemoteException, OperationApplicationException {
         // TODO Auto-generated method stub
@@ -55,8 +55,8 @@
     }
 
     @Override
-    public Bundle call(String callingPackage, String arg0, String arg1, Bundle arg2)
-            throws RemoteException {
+    public Bundle call(String callingPackage, String authority, String arg0, String arg1,
+            Bundle arg2) throws RemoteException {
         // TODO Auto-generated method stub
         return null;
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index b33344c..8e80a53 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -26,6 +26,7 @@
 import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.ResourceValueImpl;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.BridgeConstants;
@@ -34,8 +35,6 @@
 import com.android.layoutlib.bridge.impl.Stack;
 import com.android.resources.ResourceType;
 import com.android.util.Pair;
-import com.android.util.PropertiesMap;
-import com.android.util.PropertiesMap.Property;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -101,11 +100,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 import static android.os._Original_Build.VERSION_CODES.JELLY_BEAN_MR1;
 import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
@@ -120,26 +119,25 @@
     private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
     private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3);
 
-    private static final Resolver LEGACY_NAMESPACE_RESOLVER =
-            Collections.singletonMap(SdkConstants.TOOLS_PREFIX, SdkConstants.TOOLS_URI)::get;
-
     static {
-        FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue(
-                ResourceType.BOOL, "animateFirstView", "false", false));
+        FRAMEWORK_PATCHED_VALUES.put("animateFirstView",
+                new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.BOOL,
+                        "animateFirstView", "false"));
         FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
-                new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false));
+                new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.BOOL,
+                        "animateLayoutChanges", "false"));
 
 
         FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
-                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout",
-                        "text_edit_suggestion_item", true));
+                new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.LAYOUT,
+                        "textEditSuggestionItemLayout", "text_edit_suggestion_item"));
         FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
-                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout",
-                        "text_edit_suggestion_container", true));
+                new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.LAYOUT,
+                        "textEditSuggestionContainerLayout", "text_edit_suggestion_container"));
         FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
-                new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle",
-                        "TextAppearance.Holo.SuggestionHighlight", true));
-
+                new ResourceValueImpl(ResourceNamespace.ANDROID, ResourceType.STYLE,
+                        "textEditSuggestionHighlightStyle",
+                        "TextAppearance.Holo.SuggestionHighlight"));
     }
 
     /** The map adds cookies to each view so that IDE can link xml tags to views. */
@@ -165,13 +163,9 @@
 
     private Resources.Theme mTheme;
 
-    private final Map<Object, PropertiesMap> mDefaultPropMaps = new IdentityHashMap<>();
-
-    // maps for dynamically generated id representing style objects (StyleResourceValue)
-    @Nullable
-    private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
-    private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
-    private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
+    private final Map<Object, Map<ResourceReference, ResourceValue>> mDefaultPropMaps =
+            new IdentityHashMap<>();
+    private final Map<Object, ResourceReference> mDefaultStyleMap = new IdentityHashMap<>();
 
     // cache for TypedArray generated from StyleResourceValue object
     private TypedArrayCache mTypedArrayCache;
@@ -185,6 +179,8 @@
     private IBinder mBinder;
     private PackageManager mPackageManager;
     private Boolean mIsThemeAppCompat;
+    private final ResourceNamespace mAppCompatNamespace;
+    private final Map<Key<?>, Object> mUserData = new HashMap<>();
 
     /**
      * Some applications that target both pre API 17 and post API 17, set the newer attrs to
@@ -245,6 +241,16 @@
 
         mWindowManager = new WindowManagerImpl(mMetrics);
         mDisplayManager = new DisplayManager(this);
+
+        if (mLayoutlibCallback.isResourceNamespacingRequired()) {
+            if (mLayoutlibCallback.hasAndroidXAppCompat()) {
+                mAppCompatNamespace = ResourceNamespace.APPCOMPAT;
+            } else {
+                mAppCompatNamespace = ResourceNamespace.APPCOMPAT_LEGACY;
+            }
+        } else {
+            mAppCompatNamespace = ResourceNamespace.RES_AUTO;
+        }
     }
 
     /**
@@ -266,10 +272,15 @@
     }
 
     /**
-     * Disposes the {@link Resources} singleton.
+     * Disposes the {@link Resources} singleton and the AssetRepository inside BridgeAssetManager.
      */
     public void disposeResources() {
         Resources_Delegate.disposeSystem();
+
+        // The BridgeAssetManager pointed to by the mAssets field is a long-lived object, but
+        // the AssetRepository is not. To prevent it from leaking clear a reference to it from
+        // the BridgeAssetManager.
+        mAssets.releaseAssetRepository();
     }
 
     public void setBridgeInflater(BridgeInflater inflater) {
@@ -308,10 +319,14 @@
         return mRenderResources;
     }
 
-    public Map<Object, PropertiesMap> getDefaultProperties() {
+    public Map<Object, Map<ResourceReference, ResourceValue>> getDefaultProperties() {
         return mDefaultPropMaps;
     }
 
+    public Map<Object, ResourceReference> getDefaultNamespacedStyles() {
+        return mDefaultStyleMap;
+    }
+
     public Configuration getConfiguration() {
         return mConfig;
     }
@@ -357,19 +372,16 @@
     }
 
     public boolean resolveThemeAttribute(int resId, TypedValue outValue, boolean resolveRefs) {
-        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(resId);
-        boolean isFrameworkRes = true;
+        ResourceReference resourceInfo = Bridge.resolveResourceId(resId);
         if (resourceInfo == null) {
             resourceInfo = mLayoutlibCallback.resolveResourceId(resId);
-            isFrameworkRes = false;
         }
 
-        if (resourceInfo == null) {
+        if (resourceInfo == null || resourceInfo.getResourceType() != ResourceType.ATTR) {
             return false;
         }
 
-        ResourceValue value = mRenderResources.findItemInTheme(resourceInfo.getSecond(),
-                isFrameworkRes);
+        ResourceValue value = mRenderResources.findItemInTheme(resourceInfo);
         if (resolveRefs) {
             value = mRenderResources.resolveResValue(value);
         }
@@ -395,20 +407,9 @@
             else if (stringValue.charAt(0) == '@') {
                 outValue.type = TypedValue.TYPE_REFERENCE;
             }
-
         }
 
-        int a;
-        // if this is a framework value.
-        if (value.isFramework()) {
-            // look for idName in the android R classes.
-            // use 0 a default res value as it's not a valid id value.
-            a = getFrameworkResourceValue(value.getResourceType(), value.getName(), 0 /*defValue*/);
-        } else {
-            // look for idName in the project R class.
-            // use 0 a default res value as it's not a valid id value.
-            a = getProjectResourceValue(value.getResourceType(), value.getName(), 0 /*defValue*/);
-        }
+        int a = getResourceId(value.asReference(), 0 /*defValue*/);
 
         if (a != 0) {
             outValue.resourceId = a;
@@ -423,10 +424,10 @@
 
     public ResourceReference resolveId(int id) {
         // first get the String related to this id in the framework
-        Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+        ResourceReference resourceInfo = Bridge.resolveResourceId(id);
 
         if (resourceInfo != null) {
-            return new ResourceReference(resourceInfo.getSecond(), true);
+            return resourceInfo;
         }
 
         // didn't find a match in the framework? look in the project.
@@ -434,30 +435,29 @@
             resourceInfo = mLayoutlibCallback.resolveResourceId(id);
 
             if (resourceInfo != null) {
-                return new ResourceReference(resourceInfo.getSecond(), false);
+                return resourceInfo;
             }
         }
 
-        // The base value for R.style is 0x01030000 and the custom style is 0x02030000.
-        // So, if the second byte is 03, it's probably a style.
-        if ((id >> 16 & 0xFF) == 0x03) {
-            return getStyleByDynamicId(id);
-        }
         return null;
     }
 
-    public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent,
+    public Pair<View, Boolean> inflateView(ResourceReference layout, ViewGroup parent,
             @SuppressWarnings("SameParameterValue") boolean attachToRoot,
             boolean skipCallbackParser) {
-        boolean isPlatformLayout = resource.isFramework();
+        boolean isPlatformLayout = layout.getNamespace().equals(ResourceNamespace.ANDROID);
 
         if (!isPlatformLayout && !skipCallbackParser) {
             // check if the project callback can provide us with a custom parser.
-            ILayoutPullParser parser = getParser(resource);
+            ILayoutPullParser parser = null;
+            ResourceValue layoutValue = mRenderResources.getResolvedResource(layout);
+            if (layoutValue != null) {
+                parser = getLayoutlibCallback().getParser(layoutValue);
+            }
 
             if (parser != null) {
-                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser,
-                        this, resource.isFramework());
+                BridgeXmlBlockParser blockParser =
+                        new BridgeXmlBlockParser(parser, this, layout.getNamespace());
                 try {
                     pushParser(blockParser);
                     return Pair.of(
@@ -469,58 +469,42 @@
             }
         }
 
-        ResourceValue resValue;
-        if (resource instanceof ResourceValue) {
-            resValue = (ResourceValue) resource;
-        } else {
-            if (isPlatformLayout) {
-                resValue = mRenderResources.getFrameworkResource(ResourceType.LAYOUT,
-                        resource.getName());
-            } else {
-                resValue = mRenderResources.getProjectResource(ResourceType.LAYOUT,
-                        resource.getName());
-            }
-        }
+        ResourceValue resValue = mRenderResources.getResolvedResource(layout);
 
         if (resValue != null) {
+            String path = resValue.getValue();
+            // We need to create a pull parser around the layout XML file, and then
+            // give that to our XmlBlockParser.
+            try {
+                XmlPullParser parser = ParserFactory.create(path, true);
+                if (parser != null) {
+                    // Set the layout ref to have correct view cookies.
+                    mBridgeInflater.setResourceReference(layout);
 
-            File xml = new File(resValue.getValue());
-            if (xml.isFile()) {
-                // we need to create a pull parser around the layout XML file, and then
-                // give that to our XmlBlockParser
-                try {
-                    XmlPullParser parser = ParserFactory.create(xml, true);
-
-                    // set the resource ref to have correct view cookies
-                    mBridgeInflater.setResourceReference(resource);
-
-                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser,
-                            this, resource.isFramework());
+                    BridgeXmlBlockParser blockParser =
+                            new BridgeXmlBlockParser(parser, this, layout.getNamespace());
                     try {
                         pushParser(blockParser);
-                        return Pair.of(
-                                mBridgeInflater.inflate(blockParser, parent, attachToRoot),
+                        return Pair.of(mBridgeInflater.inflate(blockParser, parent, attachToRoot),
                                 Boolean.FALSE);
                     } finally {
                         popParser();
                     }
-                } catch (XmlPullParserException e) {
+                } else {
                     Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                            "Failed to configure parser for " + xml, e, null /*data*/);
-                    // we'll return null below.
-                } catch (FileNotFoundException e) {
-                    // this shouldn't happen since we check above.
-                } finally {
-                    mBridgeInflater.setResourceReference(null);
+                            String.format("File %s is missing!", path), null);
                 }
-            } else {
+            } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        String.format("File %s is missing!", xml), null);
+                        "Failed to parse file " + path, e, null /*data*/);
+                // we'll return null below.
+            } finally {
+                mBridgeInflater.setResourceReference(null);
             }
         } else {
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     String.format("Layout %s%s does not exist.", isPlatformLayout ? "android:" : "",
-                            resource.getName()), null);
+                            layout.getName()), null);
         }
 
         return Pair.of(null, Boolean.FALSE);
@@ -556,17 +540,6 @@
         return isThemeAppCompat;
     }
 
-    @SuppressWarnings("deprecation")
-    private ILayoutPullParser getParser(ResourceReference resource) {
-        ILayoutPullParser parser;
-        if (resource instanceof ResourceValue) {
-            parser = mLayoutlibCallback.getParser((ResourceValue) resource);
-        } else {
-            parser = mLayoutlibCallback.getParser(resource.getName());
-        }
-        return parser;
-    }
-
     // ------------ Context methods
 
     @Override
@@ -638,6 +611,7 @@
             case AUTOFILL_MANAGER_SERVICE:
             case AUDIO_SERVICE:
             case TEXT_CLASSIFICATION_SERVICE:
+            case CONTENT_CAPTURE_MANAGER_SERVICE:
                 return null;
             default:
                 assert false : "Unsupported Service: " + service;
@@ -651,7 +625,6 @@
         return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass);
     }
 
-
     /**
      * Same as Context#obtainStyledAttributes. We do not override the base method to give the
      * original Context the chance to override the theme when needed.
@@ -668,7 +641,7 @@
                 // In some cases, style may not be a dynamic id, so we do a full search.
                 ResourceReference ref = resolveId(resId);
                 if (ref != null) {
-                    style = mRenderResources.getStyle(ref.getName(), ref.isFramework());
+                    style = mRenderResources.getStyle(ref);
                 }
             }
 
@@ -685,7 +658,7 @@
 
         List<StyleResourceValue> currentThemes = mRenderResources.getAllThemes();
 
-        Pair<BridgeTypedArray, PropertiesMap> typeArrayAndPropertiesPair =
+        Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> typeArrayAndPropertiesPair =
                 mTypedArrayCache.get(attrs, currentThemes, resId);
 
         if (typeArrayAndPropertiesPair == null) {
@@ -697,7 +670,7 @@
             BridgeXmlBlockParser parser = getCurrentParser();
             Object key = parser != null ? parser.getViewCookie() : null;
             if (key != null) {
-                PropertiesMap defaultPropMap = mDefaultPropMaps.get(key);
+                Map<ResourceReference, ResourceValue> defaultPropMap = mDefaultPropMaps.get(key);
                 if (defaultPropMap == null) {
                     defaultPropMap = typeArrayAndPropertiesPair.getSecond();
                     mDefaultPropMaps.put(key, defaultPropMap);
@@ -717,71 +690,65 @@
     public BridgeTypedArray internalObtainStyledAttributes(@Nullable AttributeSet set, int[] attrs,
             int defStyleAttr, int defStyleRes) {
 
-        PropertiesMap defaultPropMap = null;
-        boolean isPlatformFile = true;
+        Map<ResourceReference, ResourceValue> defaultPropMap = null;
+        Object key = null;
 
-        // TODO(namespaces): We need to figure out how to keep track of the namespace of the current
-        // layout file.
-        ResourceNamespace currentFileNamespace = ResourceNamespace.TODO;
-
-        // TODO(namespaces): get this through the callback, only in non-namespaced projects.
-        ResourceNamespace.Resolver resolver = LEGACY_NAMESPACE_RESOLVER;
+        ResourceNamespace currentFileNamespace;
+        ResourceNamespace.Resolver resolver;
 
         // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
         if (set instanceof BridgeXmlBlockParser) {
             BridgeXmlBlockParser parser;
             parser = (BridgeXmlBlockParser)set;
 
-            isPlatformFile = parser.isPlatformFile();
-
-            Object key = parser.getViewCookie();
+            key = parser.getViewCookie();
             if (key != null) {
-                defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap());
+                defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new HashMap<>());
             }
 
-            resolver = parser::getNamespace;
-            currentFileNamespace = ResourceNamespace.fromBoolean(parser.isPlatformFile());
+            currentFileNamespace = parser.getFileResourceNamespace();
+            resolver = new XmlPullParserResolver(parser, mLayoutlibCallback.getImplicitNamespaces());
         } else if (set instanceof BridgeLayoutParamsMapAttributes) {
-            // this is only for temp layout params generated dynamically, so this is never
-            // platform content.
-            isPlatformFile = false;
-        } else if (set != null) { // null parser is ok
+            // This is for temp layout params generated dynamically in MockView. The set contains
+            // hardcoded values and we don't need to worry about resolving them.
+            currentFileNamespace = ResourceNamespace.RES_AUTO;
+            resolver = Resolver.EMPTY_RESOLVER;
+        } else if (set != null) {
             // really this should not be happening since its instantiated in Bridge
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     "Parser is not a BridgeXmlBlockParser!", null);
             return null;
+        } else {
+            // `set` is null, so there will be no values to resolve.
+            currentFileNamespace = ResourceNamespace.RES_AUTO;
+            resolver = Resolver.EMPTY_RESOLVER;
         }
 
         List<AttributeHolder> attributeList = searchAttrs(attrs);
 
         BridgeTypedArray ta =
-                Resources_Delegate.newTypeArray(mSystemResources, attrs.length, isPlatformFile);
+                Resources_Delegate.newTypeArray(mSystemResources, attrs.length);
 
-        // look for a custom style.
-        String customStyle = null;
-        if (set != null) {
-            customStyle = set.getAttributeValue(null, "style");
-        }
-
+        // Look for a custom style.
         StyleResourceValue customStyleValues = null;
-        if (customStyle != null) {
-            ResourceValue item = mRenderResources.findResValue(customStyle,
-                    isPlatformFile /*forceFrameworkOnly*/);
+        if (set != null) {
+            String customStyle = set.getAttributeValue(null, "style");
+            if (customStyle != null) {
+                ResourceValue resolved = mRenderResources.resolveResValue(
+                        new UnresolvedResourceValue(customStyle, currentFileNamespace, resolver));
 
-            // resolve it in case it links to something else
-            item = mRenderResources.resolveResValue(item);
-
-            if (item instanceof StyleResourceValue) {
-                customStyleValues = (StyleResourceValue)item;
+                if (resolved instanceof StyleResourceValue) {
+                    customStyleValues = (StyleResourceValue) resolved;
+                }
             }
         }
 
-        // resolve the defStyleAttr value into a IStyleResourceValue
+        // resolve the defStyleAttr value into a StyleResourceValue
         StyleResourceValue defStyleValues = null;
 
         if (defStyleAttr != 0) {
             // get the name from the int.
-            Pair<String, Boolean> defStyleAttribute = searchAttr(defStyleAttr);
+            ResourceReference defStyleAttribute = searchAttr(defStyleAttr);
 
             if (defStyleAttribute == null) {
                 // This should be rare. Happens trying to map R.style.foo to @style/foo fails.
@@ -790,25 +757,18 @@
                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
                         "Failed to find the style corresponding to the id " + defStyleAttr, null);
             } else {
-                String defStyleName = defStyleAttribute.getFirst();
-
                 // look for the style in the current theme, and its parent:
-                ResourceValue item = mRenderResources.findItemInTheme(defStyleName,
-                        defStyleAttribute.getSecond());
+                ResourceValue item = mRenderResources.findItemInTheme(defStyleAttribute);
 
                 if (item != null) {
+                    if (key != null) {
+                        mDefaultStyleMap.put(key, defStyleAttribute);
+                    }
                     // item is a reference to a style entry. Search for it.
-                    item = mRenderResources.findResValue(item.getValue(), item.isFramework());
                     item = mRenderResources.resolveResValue(item);
                     if (item instanceof StyleResourceValue) {
                         defStyleValues = (StyleResourceValue) item;
                     }
-                    if (defaultPropMap != null) {
-                        if (defStyleAttribute.getSecond()) {
-                            defStyleName = "android:" + defStyleName;
-                        }
-                        defaultPropMap.put("style", new Property(defStyleName, item.getValue()));
-                    }
                 }
             }
         }
@@ -818,21 +778,18 @@
             if (item != null) {
                 defStyleValues = item;
             } else {
-                boolean isFrameworkRes = true;
-                Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+                ResourceReference value = Bridge.resolveResourceId(defStyleRes);
                 if (value == null) {
                     value = mLayoutlibCallback.resolveResourceId(defStyleRes);
-                    isFrameworkRes = false;
                 }
 
                 if (value != null) {
-                    if ((value.getFirst() == ResourceType.STYLE)) {
+                    if ((value.getResourceType() == ResourceType.STYLE)) {
                         // look for the style in all resources:
-                        item = mRenderResources.getStyle(value.getSecond(), isFrameworkRes);
+                        item = mRenderResources.getStyle(value);
                         if (item != null) {
-                            if (defaultPropMap != null) {
-                                String name = item.getName();
-                                defaultPropMap.put("style", new Property(name, name));
+                            if (key != null) {
+                                mDefaultStyleMap.put(key, item.asReference());
                             }
 
                             defStyleValues = item;
@@ -840,14 +797,14 @@
                             Bridge.getLog().error(null,
                                     String.format(
                                             "Style with id 0x%x (resolved to '%s') does not exist.",
-                                            defStyleRes, value.getSecond()),
+                                            defStyleRes, value.getName()),
                                     null);
                         }
                     } else {
                         Bridge.getLog().error(null,
                                 String.format(
                                         "Resource id 0x%x is not of type STYLE (instead %s)",
-                                        defStyleRes, value.getFirst().toString()),
+                                        defStyleRes, value.getResourceType().name()),
                                 null);
                     }
                 } else {
@@ -860,8 +817,6 @@
             }
         }
 
-        String appNamespace = mLayoutlibCallback.getNamespace();
-
         if (attributeList != null) {
             for (int index = 0 ; index < attributeList.size() ; index++) {
                 AttributeHolder attributeHolder = attributeList.get(index);
@@ -870,17 +825,15 @@
                     continue;
                 }
 
-                String attrName = attributeHolder.name;
-                boolean frameworkAttr = attributeHolder.isFramework;
+                String attrName = attributeHolder.getName();
                 String value = null;
                 if (set != null) {
                     value = set.getAttributeValue(
-                            frameworkAttr ? BridgeConstants.NS_RESOURCES : appNamespace,
-                                    attrName);
+                            attributeHolder.getNamespace().getXmlNamespaceUri(), attrName);
 
                     // if this is an app attribute, and the first get fails, try with the
                     // new res-auto namespace as well
-                    if (!frameworkAttr && value == null) {
+                    if (attributeHolder.getNamespace() != ResourceNamespace.ANDROID && value == null) {
                         value = set.getAttributeValue(BridgeConstants.NS_APP_RES_AUTO, attrName);
                     }
                 }
@@ -893,42 +846,41 @@
                 ResourceValue defaultValue = null;
                 if (defaultPropMap != null || value == null) {
                     // look for the value in the custom style first (and its parent if needed)
+                    ResourceReference attrRef = attributeHolder.asReference();
                     if (customStyleValues != null) {
-                        defaultValue = mRenderResources.findItemInStyle(customStyleValues, attrName,
-                                frameworkAttr);
+                        defaultValue =
+                                mRenderResources.findItemInStyle(customStyleValues, attrRef);
                     }
 
                     // then look for the value in the default Style (and its parent if needed)
                     if (defaultValue == null && defStyleValues != null) {
-                        defaultValue = mRenderResources.findItemInStyle(defStyleValues, attrName,
-                                frameworkAttr);
+                        defaultValue =
+                                mRenderResources.findItemInStyle(defStyleValues, attrRef);
                     }
 
                     // if the item is not present in the defStyle, we look in the main theme (and
                     // its parent themes)
                     if (defaultValue == null) {
-                        defaultValue = mRenderResources.findItemInTheme(attrName, frameworkAttr);
+                        defaultValue =
+                                mRenderResources.findItemInTheme(attrRef);
                     }
 
                     // if we found a value, we make sure this doesn't reference another value.
                     // So we resolve it.
                     if (defaultValue != null) {
-                        String preResolve = defaultValue.getValue();
-                        defaultValue = mRenderResources.resolveResValue(defaultValue);
-
                         if (defaultPropMap != null) {
-                            defaultPropMap.put(
-                                    frameworkAttr ? SdkConstants.PREFIX_ANDROID + attrName :
-                                            attrName, new Property(preResolve, defaultValue.getValue()));
+                            defaultPropMap.put(attrRef, defaultValue);
                         }
+
+                        defaultValue = mRenderResources.resolveResValue(defaultValue);
                     }
                 }
-                // Done calculating the defaultValue
+                // Done calculating the defaultValue.
 
-                // if there's no direct value for this attribute in the XML, we look for default
+                // If there's no direct value for this attribute in the XML, we look for default
                 // values in the widget defStyle, and then in the theme.
                 if (value == null) {
-                    if (frameworkAttr) {
+                    if (attributeHolder.getNamespace() == ResourceNamespace.ANDROID) {
                         // For some framework values, layoutlib patches the actual value in the
                         // theme when it helps to improve the final preview. In most cases
                         // we just disable animations.
@@ -938,7 +890,7 @@
                         }
                     }
 
-                    // if we found a value, we make sure this doesn't reference another value.
+                    // If we found a value, we make sure this doesn't reference another value.
                     // So we resolve it.
                     if (defaultValue != null) {
                         // If the value is a reference to another theme attribute that doesn't
@@ -949,13 +901,17 @@
                             // fail to resolve when using old themes (they haven't been backported).
                             // Since this is an artifact caused by us using always the latest
                             // code, we check for some of those values and replace them here.
+                            ResourceReference reference = defaultValue.getReference();
                             defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName);
 
+                            // Only log a warning if the referenced value isn't one of the RTL
+                            // attributes, or the app targets old API.
                             if (defaultValue == null &&
                                     (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 ||
                                     !attrName.equals(RTL_ATTRS.get(val)))) {
-                                // Only log a warning if the referenced value isn't one of the RTL
-                                // attributes, or the app targets old API.
+                                if (reference != null) {
+                                    val = reference.getResourceUrl().toString();
+                                }
                                 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
                                         String.format("Failed to find '%s' in current theme.", val),
                                         val);
@@ -963,22 +919,21 @@
                         }
                     }
 
-                    ta.bridgeSetValue(index, attrName, frameworkAttr, attributeHolder.resourceId,
+                    ta.bridgeSetValue(
+                            index,
+                            attrName, attributeHolder.getNamespace(),
+                            attributeHolder.getResourceId(),
                             defaultValue);
                 } else {
                     // There is a value in the XML, but we need to resolve it in case it's
                     // referencing another resource or a theme value.
-                    ResourceValue dummy =
-                            new ResourceValue(
-                                    currentFileNamespace,
-                                    null,
-                                    attrName,
-                                    value);
-                    dummy.setNamespaceLookup(resolver);
-
                     ta.bridgeSetValue(
-                            index, attrName, frameworkAttr, attributeHolder.resourceId,
-                            mRenderResources.resolveResValue(dummy));
+                            index,
+                            attrName, attributeHolder.getNamespace(),
+                            attributeHolder.getResourceId(),
+                            mRenderResources.resolveResValue(
+                                    new UnresolvedResourceValue(
+                                            value, currentFileNamespace, resolver)));
                 }
             }
         }
@@ -1019,14 +974,14 @@
      *
      * @see #obtainStyledAttributes(int, int[])
      */
-    private Pair<BridgeTypedArray, PropertiesMap> createStyleBasedTypedArray(
+    private Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> createStyleBasedTypedArray(
             @Nullable StyleResourceValue style, int[] attrs) throws Resources.NotFoundException {
         List<AttributeHolder> attributes = searchAttrs(attrs);
 
         BridgeTypedArray ta =
-                Resources_Delegate.newTypeArray(mSystemResources, attrs.length, false);
+                Resources_Delegate.newTypeArray(mSystemResources, attrs.length);
 
-        PropertiesMap defaultPropMap = new PropertiesMap();
+        Map<ResourceReference, ResourceValue> defaultPropMap = new HashMap<>();
         // for each attribute, get its name so that we can search it in the style
         for (int i = 0; i < attrs.length; i++) {
             AttributeHolder attrHolder = attributes.get(i);
@@ -1034,24 +989,20 @@
             if (attrHolder != null) {
                 // look for the value in the given style
                 ResourceValue resValue;
-                String attrName = attrHolder.name;
-                boolean frameworkAttr = attrHolder.isFramework;
                 if (style != null) {
-                    resValue = mRenderResources.findItemInStyle(style, attrName, frameworkAttr);
+                    resValue = mRenderResources.findItemInStyle(style, attrHolder.asReference());
                 } else {
-                    resValue = mRenderResources.findItemInTheme(attrName, frameworkAttr);
+                    resValue = mRenderResources.findItemInTheme(attrHolder.asReference());
                 }
 
                 if (resValue != null) {
-                    // Add it to defaultPropMap before resolving
-                    String preResolve = resValue.getValue();
+                    defaultPropMap.put(attrHolder.asReference(), resValue);
                     // resolve it to make sure there are no references left.
                     resValue = mRenderResources.resolveResValue(resValue);
-                    ta.bridgeSetValue(i, attrName, frameworkAttr, attrHolder.resourceId,
+                    ta.bridgeSetValue(
+                            i, attrHolder.getName(), attrHolder.getNamespace(),
+                            attrHolder.getResourceId(),
                             resValue);
-                    defaultPropMap.put(
-                            frameworkAttr ? SdkConstants.ANDROID_PREFIX + attrName : attrName,
-                            new Property(preResolve, resValue.getValue()));
                 }
             }
         }
@@ -1074,16 +1025,13 @@
 
         // for each attribute, get its name so that we can search it in the style
         for (int id : attributeIds) {
-            Pair<ResourceType, String> resolvedResource = Bridge.resolveResourceId(id);
-            boolean isFramework = false;
-            if (resolvedResource != null) {
-                isFramework = true;
-            } else {
-                resolvedResource = mLayoutlibCallback.resolveResourceId(id);
+            ResourceReference refForId = Bridge.resolveResourceId(id);
+            if (refForId == null) {
+                refForId = mLayoutlibCallback.resolveResourceId(id);
             }
 
-            if (resolvedResource != null) {
-                results.add(new AttributeHolder(id, resolvedResource.getSecond(), isFramework));
+            if (refForId != null) {
+                results.add(new AttributeHolder(id, refForId));
             } else {
                 results.add(null);
             }
@@ -1094,74 +1042,61 @@
 
     /**
      * Searches for the attribute referenced by its internal id.
-     *
-     * @param attr An attribute reference given to obtainStyledAttributes such as defStyle.
-     * @return A (name, isFramework) pair describing the attribute if found. Returns null
-     *         if nothing is found.
      */
-    private Pair<String, Boolean> searchAttr(int attr) {
-        Pair<ResourceType, String> info = Bridge.resolveResourceId(attr);
-        if (info != null) {
-            return Pair.of(info.getSecond(), Boolean.TRUE);
+    private ResourceReference searchAttr(int attrId) {
+        ResourceReference attr = Bridge.resolveResourceId(attrId);
+        if (attr == null) {
+            attr = mLayoutlibCallback.resolveResourceId(attrId);
         }
 
-        info = mLayoutlibCallback.resolveResourceId(attr);
-        if (info != null) {
-            return Pair.of(info.getSecond(), Boolean.FALSE);
-        }
-
-        return null;
+        return attr;
     }
 
+    /**
+     * Maps a given style to a numeric id.
+     *
+     * <p>For now Bridge handles numeric ids (both fixed and dynamic) for framework and the callback
+     * for non-framework. TODO(namespaces): teach the IDE about fixed framework ids and handle this
+     * all in the callback.
+     */
     public int getDynamicIdByStyle(StyleResourceValue resValue) {
-        if (mDynamicIdToStyleMap == null) {
-            // create the maps.
-            mDynamicIdToStyleMap = new HashMap<>();
-            mStyleToDynamicIdMap = new HashMap<>();
+        if (resValue.isFramework()) {
+            return Bridge.getResourceId(resValue.getResourceType(), resValue.getName());
+        } else {
+            return mLayoutlibCallback.getOrGenerateResourceId(resValue.asReference());
         }
-
-        // look for an existing id
-        Integer id = mStyleToDynamicIdMap.get(resValue);
-
-        if (id == null) {
-            // generate a new id
-            id = ++mDynamicIdGenerator;
-
-            // and add it to the maps.
-            mDynamicIdToStyleMap.put(id, resValue);
-            mStyleToDynamicIdMap.put(resValue, id);
-        }
-
-        return id;
     }
 
-    private StyleResourceValue getStyleByDynamicId(int i) {
-        if (mDynamicIdToStyleMap != null) {
-            return mDynamicIdToStyleMap.get(i);
+    /**
+     * Maps a numeric id back to {@link StyleResourceValue}.
+     *
+     * <p>For now framework numeric ids are handled by Bridge, so try there first and fall back to
+     * the callback, which manages ids for non-framework resources. TODO(namespaces): manage all
+     * ids in the IDE.
+     *
+     * <p>Once we the resource for the given id, we ask the IDE to get the
+     * {@link StyleResourceValue} for it.
+     */
+    @Nullable
+    private StyleResourceValue getStyleByDynamicId(int id) {
+        ResourceReference reference = Bridge.resolveResourceId(id);
+        if (reference == null) {
+            reference = mLayoutlibCallback.resolveResourceId(id);
         }
 
-        return null;
-    }
-
-    public int getFrameworkResourceValue(ResourceType resType, String resName, int defValue) {
-        if (getRenderResources().getFrameworkResource(resType, resName) != null) {
-            // Bridge.getResourceId creates a new resource id if an existing one isn't found. So,
-            // we check for the existence of the resource before calling it.
-            return Bridge.getResourceId(resType, resName);
+        if (reference == null) {
+            return null;
         }
 
-        return defValue;
+        return mRenderResources.getStyle(reference);
     }
 
-    public int getProjectResourceValue(ResourceType resType, String resName, int defValue) {
-        // getResourceId creates a new resource id if an existing resource id isn't found. So, we
-        // check for the existence of the resource before calling it.
-        if (getRenderResources().getProjectResource(resType, resName) != null) {
-            if (mLayoutlibCallback != null) {
-                Integer value = mLayoutlibCallback.getResourceId(resType, resName);
-                if (value != null) {
-                    return value;
-                }
+    public int getResourceId(@NonNull ResourceReference resource, int defValue) {
+        if (getRenderResources().getUnresolvedResource(resource) != null) {
+            if (resource.getNamespace().equals(ResourceNamespace.ANDROID)) {
+                return Bridge.getResourceId(resource.getResourceType(), resource.getName());
+            } else if (mLayoutlibCallback != null) {
+                return mLayoutlibCallback.getOrGenerateResourceId(resource);
             }
         }
 
@@ -1175,6 +1110,40 @@
         return context;
     }
 
+    /**
+     * Returns the Framework attr resource reference with the given name.
+     */
+    @NonNull
+    public static ResourceReference createFrameworkAttrReference(@NonNull String name) {
+        return createFrameworkResourceReference(ResourceType.ATTR, name);
+    }
+
+    /**
+     * Returns the Framework resource reference with the given type and name.
+     */
+    @NonNull
+    public static ResourceReference createFrameworkResourceReference(@NonNull ResourceType type,
+            @NonNull String name) {
+        return new ResourceReference(ResourceNamespace.ANDROID, type, name);
+    }
+
+    /**
+     * Returns the AppCompat attr resource reference with the given name.
+     */
+    @NonNull
+    public ResourceReference createAppCompatAttrReference(@NonNull String name) {
+        return createAppCompatResourceReference(ResourceType.ATTR, name);
+    }
+
+    /**
+     * Returns the AppCompat resource reference with the given type and name.
+     */
+    @NonNull
+    public ResourceReference createAppCompatResourceReference(@NonNull ResourceType type,
+            @NonNull String name) {
+        return new ResourceReference(mAppCompatNamespace, type, name);
+    }
+
     public IBinder getBinder() {
         if (mBinder == null) {
             // create a dummy binder. We only need it be not null.
@@ -1244,6 +1213,17 @@
     }
 
     @Override
+    public boolean bindService(Intent arg0, int arg1, Executor arg2, ServiceConnection arg3) {
+        return false;
+    }
+
+    @Override
+    public boolean bindIsolatedService(Intent arg0,
+            int arg1, String arg2, Executor arg3, ServiceConnection arg4) {
+        return false;
+    }
+
+    @Override
     public int checkCallingOrSelfPermission(String arg0) {
         // pass
         return 0;
@@ -1904,6 +1884,12 @@
     }
 
     @Override
+    public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+            int importance) {
+        // pass
+    }
+
+    @Override
     public void unbindService(ServiceConnection arg0) {
         // pass
 
@@ -1956,6 +1942,12 @@
     }
 
     @Override
+    public int getDisplayId() {
+        // pass
+        return 0;
+    }
+
+    @Override
     public void updateDisplay(int displayId) {
         // pass
     }
@@ -2034,15 +2026,66 @@
         return true;
     }
 
-    private class AttributeHolder {
-        private int resourceId;
-        private String name;
-        private boolean isFramework;
+    public <T> void putUserData(@NonNull Key<T> key, @Nullable T data) {
+        mUserData.put(key, data);
+    }
 
-        private AttributeHolder(int resourceId, String name, boolean isFramework) {
-            this.resourceId = resourceId;
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> T getUserData(@NonNull Key<T> key) {
+        return (T) mUserData.get(key);
+    }
+
+    /**
+     * No two Key instances are considered equal.
+     *
+     * @param <T> the type of values associated with the key
+     */
+    public static final class Key<T> {
+        private final String name;
+
+        @NonNull
+        public static <T> Key<T> create(@NonNull String name) {
+            return new Key<T>(name);
+        }
+
+        private Key(@NonNull String name) {
             this.name = name;
-            this.isFramework = isFramework;
+        }
+
+        /** For debugging only. */
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    private class AttributeHolder {
+        private final int resourceId;
+        @NonNull private final ResourceReference reference;
+
+        private AttributeHolder(int resourceId, @NonNull ResourceReference reference) {
+            this.resourceId = resourceId;
+            this.reference = reference;
+        }
+
+        @NonNull
+        private ResourceReference asReference() {
+            return reference;
+        }
+
+        private int getResourceId() {
+            return resourceId;
+        }
+
+        @NonNull
+        private String getName() {
+            return reference.getName();
+        }
+
+        @NonNull
+        private ResourceNamespace getNamespace() {
+            return reference.getNamespace();
         }
     }
 
@@ -2064,18 +2107,20 @@
 
         private Map<int[],
                 Map<List<StyleResourceValue>,
-                        Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache;
+                        Map<Integer, Pair<BridgeTypedArray,
+                                Map<ResourceReference, ResourceValue>>>>> mCache;
 
         private TypedArrayCache() {
             mCache = new IdentityHashMap<>();
         }
 
-        public Pair<BridgeTypedArray, PropertiesMap> get(int[] attrs,
+        public Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> get(int[] attrs,
                 List<StyleResourceValue> themes, int resId) {
-            Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
+            Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference,
+                    ResourceValue>>>>
                     cacheFromThemes = mCache.get(attrs);
             if (cacheFromThemes != null) {
-                Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
+                Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>>> cacheFromResId =
                         cacheFromThemes.get(themes);
                 if (cacheFromResId != null) {
                     return cacheFromResId.get(resId);
@@ -2085,10 +2130,11 @@
         }
 
         public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
-                Pair<BridgeTypedArray, PropertiesMap> value) {
-            Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
+                Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>> value) {
+            Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference,
+                    ResourceValue>>>>
                     cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>());
-            Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
+            Map<Integer, Pair<BridgeTypedArray, Map<ResourceReference, ResourceValue>>> cacheFromResId =
                     cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>());
             cacheFromResId.put(resId, value);
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 73a9a62..b0b09b2 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -152,7 +152,12 @@
     }
 
     @Override
-    public boolean isPermissionReviewModeEnabled() {
+    public boolean arePermissionsIndividuallyControlled() {
+        return false;
+    }
+
+    @Override
+    public boolean isWirelessConsentModeEnabled() {
         return false;
     }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index b87ca3b..0b5f14c 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -16,16 +16,17 @@
 
 package com.android.layoutlib.bridge.android;
 
+import android.os.BatterySaverPolicyConfig;
 import android.os.IBinder;
 import android.os.IPowerManager;
 import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
 import android.os.PowerSaveState;
 import android.os.RemoteException;
 import android.os.WorkSource;
 
 /**
  * Fake implementation of IPowerManager.
- *
  */
 public class BridgePowerManager implements IPowerManager {
 
@@ -40,10 +41,32 @@
     }
 
     @Override
-    public boolean setPowerSaveMode(boolean mode) throws RemoteException {
+    public boolean setPowerSaveModeEnabled(boolean mode) throws RemoteException {
         return false;
     }
 
+    @Override
+    public boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold)
+            throws RemoteException {
+        return false;
+    }
+
+    @Override
+    public boolean setAdaptivePowerSavePolicy(BatterySaverPolicyConfig config)
+            throws RemoteException {
+        return false;
+    }
+
+    @Override
+    public boolean setAdaptivePowerSaveEnabled(boolean enabled) throws RemoteException {
+        return false;
+    }
+
+    @Override
+    public int getPowerSaveModeTrigger() {
+        return 0;
+    }
+
     public PowerSaveState getPowerSaveState(int serviceType) {
         return null;
     }
@@ -138,7 +161,8 @@
     }
 
     @Override
-    public void wakeUp(long time, String reason, String opPackageName) throws RemoteException {
+    public void wakeUp(long time, @WakeReason int reason, String details , String opPackageName)
+            throws RemoteException {
         // pass for now.
     }
 
@@ -168,7 +192,17 @@
     }
 
     @Override
+    public int getLastSleepReason() {
+        return PowerManager.GO_TO_SLEEP_REASON_TIMEOUT;
+    }
+
+    @Override
     public void setDozeAfterScreenOff(boolean mode) throws RemoteException {
         // pass for now.
     }
+
+    @Override
+    public boolean forceSuspend() {
+        return false;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index d50117e..0269352 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layoutlib.bridge.android;
 
-
 import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -28,6 +28,7 @@
 import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
 import android.util.BridgeXmlPullAttributes;
+import android.util.ResolvingAttributeSet;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -38,12 +39,11 @@
  * It delegates to both an instance of {@link XmlPullParser} and an instance of
  * XmlPullAttributes (for the {@link AttributeSet} part).
  */
-public class BridgeXmlBlockParser implements XmlResourceParser {
-
-    private final XmlPullParser mParser;
-    private final AttributeSet mAttrib;
-    private final BridgeContext mContext;
-    private final boolean mPlatformFile;
+public class BridgeXmlBlockParser implements XmlResourceParser, ResolvingAttributeSet {
+    @NonNull private final XmlPullParser mParser;
+    @NonNull private final ResolvingAttributeSet mAttrib;
+    @Nullable private final BridgeContext mContext;
+    @NonNull private final ResourceNamespace mFileResourceNamespace;
 
     private boolean mStarted = false;
     private int mEventType = START_DOCUMENT;
@@ -52,22 +52,24 @@
 
     /**
      * Builds a {@link BridgeXmlBlockParser}.
-     * @param parser The XmlPullParser to get the content from.
+     * @param parser XmlPullParser to get the content from.
      * @param context the Context.
-     * @param platformFile Indicates whether the the file is a platform file or not.
+     * @param fileNamespace namespace of the file being parsed.
      */
-    public BridgeXmlBlockParser(@NonNull XmlPullParser parser, @Nullable BridgeContext context,
-            boolean platformFile) {
+    public BridgeXmlBlockParser(
+            @NonNull XmlPullParser parser,
+            @Nullable BridgeContext context,
+            @NonNull ResourceNamespace fileNamespace) {
         if (ParserFactory.LOG_PARSER) {
             System.out.println("CRTE " + parser.toString());
         }
 
         mParser = parser;
         mContext = context;
-        mPlatformFile = platformFile;
+        mFileResourceNamespace = fileNamespace;
 
         if (mContext != null) {
-            mAttrib = new BridgeXmlPullAttributes(parser, context, mPlatformFile);
+            mAttrib = new BridgeXmlPullAttributes(parser, context, mFileResourceNamespace);
             mContext.pushParser(this);
             mPopped = false;
         }
@@ -80,8 +82,9 @@
         return mParser;
     }
 
-    public boolean isPlatformFile() {
-        return mPlatformFile;
+    @NonNull
+    public ResourceNamespace getFileResourceNamespace() {
+        return mFileResourceNamespace;
     }
 
     public Object getViewCookie() {
@@ -309,10 +312,6 @@
         if (ev == END_TAG && mParser.getDepth() == 1) {
             // done with parser remove it from the context stack.
             ensurePopped();
-
-            if (ParserFactory.LOG_PARSER) {
-                System.out.println("");
-            }
         }
 
         mEventType = ev;
@@ -493,4 +492,10 @@
         return mAttrib.getStyleAttribute();
     }
 
+    @Override
+    @Nullable
+    public ResourceValue getResolvedAttributeValue(@Nullable String namespace,
+            @NonNull String name) {
+        return mAttrib.getResolvedAttributeValue(namespace, name);
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java b/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java
index cd971ee..86ec798 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java
@@ -13,15 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layoutlib.bridge.android;
 
-import android.util.AttributeSet;
+import com.android.ide.common.rendering.api.ResourceValue;
+
+import android.util.ResolvingAttributeSet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 /**
  * Empty {@link AttributeSet}
  */
-class NopAttributeSet implements AttributeSet {
+class NopAttributeSet implements ResolvingAttributeSet {
     @Override
     public int getAttributeCount() {
         return 0;
@@ -140,4 +144,11 @@
     public int getStyleAttribute() {
         return 0;
     }
+
+    @Override
+    @Nullable
+    public ResourceValue getResolvedAttributeValue(@Nullable String namespace,
+            @NonNull String name) {
+        return null;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index a20652f..a2f5976 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.android;
 
+import com.android.ide.common.rendering.api.IImageFactory;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderParams;
 import com.android.ide.common.rendering.api.SessionParams.Key;
@@ -65,6 +66,27 @@
     public static final Key<String> FLAG_KEY_ADAPTIVE_ICON_MASK_PATH =
             new Key<>("adaptiveIconMaskPath", String.class);
 
+    /**
+     * When enabled, Layoutlib will resize the output image to whatever size
+     * is returned by {@link IImageFactory#getImage(int, int)}. The default
+     * behaviour when this is false is to crop the image to the size of the image
+     * returned by {@link IImageFactory#getImage(int, int)}.
+     */
+    public static final Key<Boolean> FLAG_KEY_RESULT_IMAGE_AUTO_SCALE =
+            new Key<Boolean>("enableResultImageAutoScale", Boolean.class);
+
+    /**
+     * Enables Ray Traced shadows in layoutlib.
+     */
+    public static final Key<Boolean> FLAG_RENDER_HIGH_QUALITY_SHADOW =
+            new Key<>("renderHighQualityShadow", Boolean.class);
+
+    /**
+     * Flags to enable shadows in layoutlib.
+     */
+    public static final Key<Boolean> FLAG_ENABLE_SHADOW =
+            new Key<>("enableShadow", Boolean.class);
+
     // Disallow instances.
     private RenderParamsFlags() {}
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/UnresolvedResourceValue.java b/bridge/src/com/android/layoutlib/bridge/android/UnresolvedResourceValue.java
new file mode 100644
index 0000000..b562db7
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/android/UnresolvedResourceValue.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.layoutlib.bridge.android;
+
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceValueImpl;
+import com.android.resources.ResourceType;
+
+import android.annotation.NonNull;
+
+/**
+ * Special subclass that layoutlib uses to start the resolution process and recognize if the
+ * resolution failed.
+ */
+public class UnresolvedResourceValue extends ResourceValueImpl {
+    public UnresolvedResourceValue(
+            @NonNull String value,
+            @NonNull ResourceNamespace namespace,
+            @NonNull ResourceNamespace.Resolver namespaceResolver) {
+        super(namespace, ResourceType.STRING, "layoutlib", value);
+        setNamespaceResolver(namespaceResolver);
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/XmlPullParserResolver.java b/bridge/src/com/android/layoutlib/bridge/android/XmlPullParserResolver.java
new file mode 100644
index 0000000..7ae5bce
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/android/XmlPullParserResolver.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android;
+
+import com.android.ide.common.rendering.api.ResourceNamespace;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * A {@link ResourceNamespace.Resolver} that delegates to the given {@link XmlPullParser} and falls
+ * back to the "implicit" resolver which is assumed to contain namespaces predefined for the file
+ * being parsed.
+ *
+ * <p>Note that the parser will start giving different results as the underlying parser moves in the
+ * input, so it should either be discarded or reused with care.
+ */
+public class XmlPullParserResolver implements ResourceNamespace.Resolver {
+    private final XmlPullParser mParser;
+    private final ResourceNamespace.Resolver mImplicitNamespacesResolver;
+
+    public XmlPullParserResolver(
+            @NonNull XmlPullParser parser,
+            @NonNull ResourceNamespace.Resolver implicitNamespacesResolver) {
+        mParser = parser;
+        mImplicitNamespacesResolver = implicitNamespacesResolver;
+    }
+
+    @Override
+    @Nullable
+    public String prefixToUri(@NonNull String namespacePrefix) {
+        String result = mParser.getNamespace(namespacePrefix);
+        if (result == null) {
+            result = mImplicitNamespacesResolver.prefixToUri(namespacePrefix);
+        }
+
+        return result;
+    }
+
+    @Override
+    @Nullable
+    public String uriToPrefix(@NonNull String namespaceUri) {
+        // This is needed when creating new XML snippets, we don't need to support that.
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
index aedcc48..0c2ef8b 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
@@ -33,19 +33,19 @@
 public class DesignLibUtil {
     public static final String[] CN_COORDINATOR_LAYOUT = {
             "android.support.design.widget.CoordinatorLayout",
-            "androidx.widget.CoordinatorLayout"
+            "androidx.coordinatorlayout.widget.CoordinatorLayout"
     };
     public static final String[] CN_APPBAR_LAYOUT = {
             "android.support.design.widget.AppBarLayout",
-            "androidx.design.widget.AppBarLayout"
+            "com.google.android.material.widget.AppBarLayout"
     };
     public static final String[] CN_COLLAPSING_TOOLBAR_LAYOUT = {
             "android.support.design.widget.CollapsingToolbarLayout",
-            "androidx.design.widget.CollapsingToolbarLayout"
+            "com.google.android.material.widget.CollapsingToolbarLayout"
     };
     public static final String[] CN_TOOLBAR = {
             "android.support.v7.widget.Toolbar",
-            "androidx.widget.Toolbar"
+            "androidx.appcompat.widget.Toolbar"
     };
 
     /**
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
index 4b9f674..1124a21 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
@@ -35,7 +35,7 @@
 
     public static final String[] CN_DRAWER_LAYOUT = {
             "android.support.v4.widget.DrawerLayout",
-            "androidx.widget.DrawerLayout"
+            "androidx.drawerlayout.widget.DrawerLayout"
     };
 
     public static void openDrawer(View drawerLayout, @Nullable String drawerGravity) {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
index 138d914..da2d3ee 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
@@ -34,12 +34,12 @@
 
     public static final String[] CN_FRAGMENT_TAB_HOST = {
             "android.support.v4.app.FragmentTabHost",
-            "androidx.app.FragmentTabHost"
+            "androidx.fragment.app.FragmentTabHost"
     };
 
     private static final String[] CN_FRAGMENT_MANAGER = {
             "android.support.v4.app.FragmentManager",
-            "androidx.app.FragmentManager"
+            "androidx.fragment.app.FragmentManager"
     };
 
     /**
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index bf9a7e5..894937c 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -18,7 +18,6 @@
 
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.internal.widget.RecyclerView;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
@@ -41,7 +40,7 @@
 public class RecyclerViewUtil {
     public static final String[] CN_RECYCLER_VIEW = {
             "android.support.v7.widget.RecyclerView",
-            "androidx.widget.RecyclerView"
+            "androidx.recyclerview.widget.RecyclerView"
     };
 
     private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class};
@@ -55,7 +54,8 @@
      */
     public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
             @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) {
-        String recyclerViewClassName = recyclerView.getClass().getName();
+        String recyclerViewClassName =
+                ReflectionUtils.getParentClass(recyclerView, RecyclerViewUtil.CN_RECYCLER_VIEW);
         String adapterClassName = recyclerViewClassName + "$Adapter";
         String layoutMgrClassName = recyclerViewClassName + "$LayoutManager";
 
@@ -98,7 +98,7 @@
             @NonNull String linearLayoutMgrClassName, @NonNull LayoutlibCallback callback)
             throws ReflectionException {
         try {
-            return callback.loadView(linearLayoutMgrClassName, LLM_CONSTRUCTOR_SIGNATURE,
+            return callback.loadClass(linearLayoutMgrClassName, LLM_CONSTRUCTOR_SIGNATURE,
                     new Object[]{context});
         } catch (Exception e) {
             throw new ReflectionException(e);
@@ -112,14 +112,14 @@
 
     @Nullable
     private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback,
-            @NonNull String layoutMgrClassName) throws ReflectionException {
+            @NonNull String adapterClassName) throws ReflectionException {
         Boolean ideSupport =
                 layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
         if (ideSupport != Boolean.TRUE) {
             return null;
         }
         try {
-            return layoutlibCallback.loadClass(layoutMgrClassName, new Class[0], new Object[0]);
+            return layoutlibCallback.loadClass(adapterClassName, new Class[0], new Object[0]);
         } catch (Exception e) {
             throw new ReflectionException(e);
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
index fda4693..171edb9 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -18,11 +18,15 @@
 
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -114,14 +118,21 @@
      * Returns a themed wrapper context of {@link BridgeContext} with the theme specified in
      * ?attr/preferenceTheme applied to it.
      */
-    @Nullable
+    @NotNull
     private static Context getThemedContext(@NonNull BridgeContext bridgeContext) {
         RenderResources resources = bridgeContext.getRenderResources();
-        ResourceValue preferenceTheme = resources.findItemInTheme("preferenceTheme", false);
+        ResourceValue preferenceTheme = resources.findItemInTheme(
+                bridgeContext.createAppCompatAttrReference("preferenceTheme"));
 
         if (preferenceTheme != null) {
             // resolve it, if needed.
             preferenceTheme = resources.resolveResValue(preferenceTheme);
+        } else {
+            // The current theme does not define "preferenceTheme" so we will use the default
+            // "PreferenceThemeOverlay" if available.
+            preferenceTheme = resources.getStyle(
+                    bridgeContext.createAppCompatResourceReference(ResourceType.STYLE,
+                            "PreferenceThemeOverlay"));
         }
         if (preferenceTheme instanceof StyleResourceValue) {
             int styleId = bridgeContext.getDynamicIdByStyle(((StyleResourceValue) preferenceTheme));
@@ -130,7 +141,9 @@
             }
         }
 
-        return null;
+        // We were not able to find any preferences theme so return the original Context without
+        // any theme wrapping
+        return bridgeContext;
     }
 
     /**
@@ -223,12 +236,7 @@
 
         try {
             LayoutlibCallback callback = bridgeContext.getLayoutlibCallback();
-
             Context context = getThemedContext(bridgeContext);
-            if (context == null) {
-                // Probably we couldn't find the "preferenceTheme" in the theme
-                return null;
-            }
 
             // Create PreferenceManager
             Object preferenceManager = instantiateClass(callback, preferenceManagerClassName,
@@ -251,22 +259,26 @@
             ArrayList<Object> viewCookie = new ArrayList<>();
             if (parser instanceof BridgeXmlBlockParser) {
                 // Setup a parser that stores the XmlTag
-                parser = new BridgeXmlBlockParser(parser, null, false) {
-                    @Override
-                    public Object getViewCookie() {
-                        return ((BridgeXmlBlockParser) getParser()).getViewCookie();
-                    }
+                parser =
+                        new BridgeXmlBlockParser(
+                                parser,
+                                null,
+                                ((BridgeXmlBlockParser) parser).getFileResourceNamespace()) {
+                            @Override
+                            public Object getViewCookie() {
+                                return ((BridgeXmlBlockParser) getParser()).getViewCookie();
+                            }
 
-                    @Override
-                    public int next() throws XmlPullParserException, IOException {
-                        int ev = super.next();
-                        if (ev == XmlPullParser.START_TAG) {
-                            viewCookie.add(this.getViewCookie());
-                        }
+                            @Override
+                            public int next() throws XmlPullParserException, IOException {
+                                int ev = super.next();
+                                if (ev == XmlPullParser.START_TAG) {
+                                    viewCookie.add(this.getViewCookie());
+                                }
 
-                        return ev;
-                    }
-                };
+                                return ev;
+                            }
+                        };
             }
 
             // Create the PreferenceInflater
@@ -299,4 +311,20 @@
             return null;
         }
     }
+
+    /**
+     * Returns true if the given root tag is any of the support library {@code PreferenceScreen}
+     * tags.
+     */
+    public static boolean isSupportRootTag(@Nullable String rootTag) {
+        if (rootTag != null) {
+            for (String supportPrefix : PREFERENCES_PKG_NAMES) {
+                if (rootTag.equals(supportPrefix + ".PreferenceScreen")) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 136256d1..bf2b527 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -78,4 +78,19 @@
     public Region getCurrentImeTouchRegion() {
         return null;
     }
+
+    @Override
+    public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
+        // pass
+    }
+
+    @Override
+    public void setShouldShowSystemDecors(int displayId, boolean shouldShow) {
+        // pass
+    }
+
+    @Override
+    public void setShouldShowIme(int displayId, boolean shouldShow) {
+        // pass
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index 919d57c..ae218ee 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layoutlib.bridge.bars;
 
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.StyleResourceValue;
@@ -26,7 +26,6 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.ResourceType;
-import com.android.tools.layoutlib.annotations.NotNull;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,23 +43,18 @@
 import java.lang.reflect.Method;
 import java.util.List;
 
-import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
-import static com.android.resources.ResourceType.MENU;
-
-
 /**
  * Assumes that the AppCompat library is present in the project's classpath and creates an
  * actionbar around it.
  */
 public class AppCompatActionBar extends BridgeActionBar {
-
-    private Object mWindowDecorActionBar;
     private static final String[] WINDOW_ACTION_BAR_CLASS_NAMES = {
             "android.support.v7.internal.app.WindowDecorActionBar",
             "android.support.v7.app.WindowDecorActionBar",     // This is used on v23.1.1 and later.
-            "androidx.app.WindowDecorActionBar"                // User from v27
+            "androidx.appcompat.app.WindowDecorActionBar"      // User from v28
     };
 
+    private Object mWindowDecorActionBar;
     private Class<?> mWindowActionBarClass;
 
     /**
@@ -68,9 +62,11 @@
      */
     public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
         super(context, params);
-        int contentRootId = context.getProjectResourceValue(ResourceType.ID,
-                "action_bar_activity_content", 0);
+        ResourceReference resource = context.createAppCompatResourceReference(
+                ResourceType.ID, "action_bar_activity_content");
+        int contentRootId = context.getResourceId(resource, 0);
         View contentView = getDecorContent().findViewById(contentRootId);
+
         if (contentView != null) {
             assert contentView instanceof FrameLayout;
             setContentRoot((FrameLayout) contentView);
@@ -88,22 +84,21 @@
             Object[] constructorArgs = {getDecorContent()};
             LayoutlibCallback callback = params.getLayoutlibCallback();
 
-            // Find the correct WindowActionBar class
+            // Find the correct WindowActionBar class.
             String actionBarClass = null;
-            for  (int i = WINDOW_ACTION_BAR_CLASS_NAMES.length - 1; i >= 0; i--) {
+            for  (int i = WINDOW_ACTION_BAR_CLASS_NAMES.length; --i >= 0;) {
                 actionBarClass = WINDOW_ACTION_BAR_CLASS_NAMES[i];
                 try {
                     callback.findClass(actionBarClass);
-
                     break;
                 } catch (ClassNotFoundException ignore) {
                 }
             }
 
-            mWindowDecorActionBar = callback.loadView(actionBarClass,
-                    constructorParams, constructorArgs);
-            mWindowActionBarClass = mWindowDecorActionBar == null ? null :
-                    mWindowDecorActionBar.getClass();
+            mWindowDecorActionBar =
+                    callback.loadView(actionBarClass, constructorParams, constructorArgs);
+            mWindowActionBarClass =
+                    mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass();
             inflateMenus();
             setupActionBar();
         } catch (Exception e) {
@@ -115,8 +110,8 @@
     @Override
     protected ResourceValue getLayoutResource(BridgeContext context) {
         // We always assume that the app has requested the action bar.
-        return context.getRenderResources().getProjectResource(ResourceType.LAYOUT,
-                "abc_screen_toolbar");
+        return context.getRenderResources().getResolvedResource(
+                context.createAppCompatResourceReference(ResourceType.LAYOUT,"abc_screen_toolbar"));
     }
 
     @Override
@@ -126,7 +121,8 @@
         // https://android.googlesource.com/platform/frameworks/support/+/android-5.1.0_r1/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateBase.java
         Context themedContext = context;
         RenderResources resources = context.getRenderResources();
-        ResourceValue actionBarTheme = resources.findItemInTheme("actionBarTheme", false);
+        ResourceValue actionBarTheme =
+                resources.findItemInTheme(context.createAppCompatAttrReference("actionBarTheme"));
         if (actionBarTheme != null) {
             // resolve it, if needed.
             actionBarTheme = resources.resolveResValue(actionBarTheme);
@@ -157,12 +153,12 @@
     }
 
     @Override
-    protected void setIcon(String icon) {
+    protected void setIcon(ResourceValue icon) {
         // Do this only if the action bar doesn't already have an icon.
-        if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) {
+        if (icon != null && icon.getValue() != null && mWindowDecorActionBar != null) {
             if (invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar)
                     == Boolean.TRUE) {
-                Drawable iconDrawable = getDrawable(icon, false);
+                Drawable iconDrawable = getDrawable(icon);
                 if (iconDrawable != null) {
                     Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class);
                     invoke(setIcon, mWindowDecorActionBar, iconDrawable);
@@ -181,12 +177,12 @@
     }
 
     private void inflateMenus() {
-        List<String> menuNames = getCallBack().getMenuIdNames();
-        if (menuNames.isEmpty()) {
+        List<ResourceReference> menuIds = getCallBack().getMenuIds();
+        if (menuIds.isEmpty()) {
             return;
         }
 
-        if (menuNames.size() > 1) {
+        if (menuIds.size() > 1) {
             // Supporting multiple menus means that we would need to instantiate our own supportlib
             // MenuInflater instances using reflection
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -194,20 +190,12 @@
                     null, null, null);
         }
 
-        String name = menuNames.get(0);
-        int id;
-        if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
-            // Framework menu.
-            name = name.substring(ANDROID_NS_NAME_PREFIX.length());
-            id = mBridgeContext.getFrameworkResourceValue(MENU, name, -1);
-        } else {
-            // Project menu.
-            id = mBridgeContext.getProjectResourceValue(MENU, name, -1);
-        }
+        ResourceReference menuId = menuIds.get(0);
+        int id = mBridgeContext.getResourceId(menuId, -1);
         if (id < 1) {
             return;
         }
-        // Get toolbar decorator
+        // Get toolbar decorator.
         Object mDecorToolbar = getFieldValue(mWindowDecorActionBar, "mDecorToolbar");
         if (mDecorToolbar == null) {
             return;
@@ -246,7 +234,7 @@
      * without having to get all the types for the parameters when we do not need them
      */
     @Nullable
-    private static Method findMethod(@Nullable Class<?> owner, @NotNull String name) {
+    private static Method findMethod(@Nullable Class<?> owner, @NonNull String name) {
         if (owner == null) {
             return null;
         }
@@ -260,7 +248,7 @@
     }
 
     @Nullable
-    private static Object getFieldValue(@Nullable Object instance, @NotNull String name) {
+    private static Object getFieldValue(@Nullable Object instance, @NonNull String name) {
         if (instance == null) {
             return null;
         }
@@ -295,10 +283,9 @@
 
     // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper
     @Nullable
-    private Drawable getDrawable(@NonNull String name, boolean isFramework) {
-        RenderResources res = mBridgeContext.getRenderResources();
-        ResourceValue value = res.findResValue(name, isFramework);
-        value = res.resolveResValue(value);
+    private Drawable getDrawable(@NonNull ResourceValue value) {
+        RenderResources resolver = mBridgeContext.getRenderResources();
+        value = resolver.resolveResValue(value);
         if (value != null) {
             return ResourceHelper.getDrawable(value, mBridgeContext);
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java b/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
index a439e7d..8790cb5 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
@@ -18,7 +18,6 @@
 
 import com.android.ide.common.rendering.api.ActionBarCallback;
 import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
-import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.layoutlib.bridge.MockView;
@@ -60,14 +59,7 @@
             assert false : "Unable to find the layout for Action Bar.";
         }
         else {
-            if (layoutName.isFramework()) {
-                layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
-                        layoutName.getName(), 0);
-            } else {
-                layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
-                        layoutName.getName(), 0);
-
-            }
+            layoutId = context.getResourceId(layoutName.asReference(), 0);
         }
         if (layoutId == 0) {
             assert false : String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
@@ -128,19 +120,12 @@
 
     protected abstract void setTitle(CharSequence title);
     protected abstract void setSubtitle(CharSequence subtitle);
-    protected abstract void setIcon(String icon);
+    protected abstract void setIcon(ResourceValue icon);
     protected abstract void setHomeAsUp(boolean homeAsUp);
 
     private void setTitle() {
-        RenderResources res = mBridgeContext.getRenderResources();
-
         String title = mParams.getAppLabel();
-        ResourceValue titleValue = res.findResValue(title, false);
-        if (titleValue != null && titleValue.getValue() != null) {
-            setTitle(titleValue.getValue());
-        } else {
-            setTitle(title);
-        }
+        setTitle(title);
     }
 
     private void setSutTitle() {
@@ -151,7 +136,7 @@
     }
 
     private void setIcon() {
-        String appIcon = mParams.getAppIcon();
+        ResourceValue appIcon = mParams.getAppIcon();
         if (appIcon != null) {
             setIcon(appIcon);
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 569d7b6..641ca4e 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -33,14 +33,27 @@
     private static final String GINGERBREAD_DIR      = "/bars/v9/";
     private static final String JELLYBEAN_DIR        = "/bars/v18/";
     private static final String KITKAT_DIR           = "/bars/v19/";
-    private static final String DEFAULT_RESOURCE_DIR = "/bars/v21/";
+    private static final String LOLLIPOP_DIR         = "/bars/v21/";
+    private static final String PI_DIR = "/bars/v28/";
 
-    private static final List<String> sDefaultResourceDir =
-            Collections.singletonList(DEFAULT_RESOURCE_DIR);
+
+    private static final List<String> sDefaultResourceDir;
 
     private static final int WHITE = 0xFFFFFFFF;
     private static final int BLACK = 0xFF000000;
 
+    static {
+        sDefaultResourceDir = new ArrayList<>(6);
+        sDefaultResourceDir.add(PI_DIR);
+        sDefaultResourceDir.add("/bars/");
+        // If something is not found in the default directories, we fall back to search in the
+        // old versions
+        sDefaultResourceDir.add(LOLLIPOP_DIR);
+        sDefaultResourceDir.add(KITKAT_DIR);
+        sDefaultResourceDir.add(JELLYBEAN_DIR);
+        sDefaultResourceDir.add(GINGERBREAD_DIR);
+    }
+
     public static boolean showOnScreenNavBar(int platformVersion) {
         return isGreaterOrEqual(platformVersion, ICE_CREAM_SANDWICH);
     }
@@ -55,7 +68,7 @@
         if (platformVersion == 0) {
             return sDefaultResourceDir;
         }
-        List<String> list = new ArrayList<String>(4);
+        List<String> list = new ArrayList<String>(10);
         // Gingerbread - uses custom battery and wifi icons.
         if (platformVersion <= GINGERBREAD) {
             list.add(GINGERBREAD_DIR);
@@ -68,7 +81,12 @@
         if (platformVersion <= KITKAT) {
             list.add(KITKAT_DIR);
         }
-        list.add(DEFAULT_RESOURCE_DIR);
+        // Lollipop - Custom for sysbar and battery
+        if (platformVersion <= LOLLIPOP) {
+            list.add(LOLLIPOP_DIR);
+        }
+
+        list.addAll(sDefaultResourceDir);
 
         return Collections.unmodifiableList(list);
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 369f1c6..332a861 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -23,15 +23,13 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.layoutlib.bridge.resources.IconLoader;
+import com.android.layoutlib.bridge.resources.SysUiResources;
 import com.android.resources.Density;
 import com.android.resources.LayoutDirection;
 import com.android.resources.ResourceType;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
@@ -55,20 +53,16 @@
  * Base "bar" class for the window decor around the the edited layout.
  * This is basically an horizontal layout that loads a given layout on creation (it is read
  * through {@link Class#getResourceAsStream(String)}).
- *
+ * <p>
  * The given layout should be a merge layout so that all the children belong to this class directly.
- *
+ * <p>
  * It also provides a few utility methods to configure the content of the layout.
  */
 abstract class CustomBar extends LinearLayout {
-
-
     private final int mSimulatedPlatformVersion;
 
-    protected abstract TextView getStyleableTextView();
-
-    protected CustomBar(BridgeContext context, int orientation, String layoutPath,
-            String name, int simulatedPlatformVersion) {
+    protected CustomBar(BridgeContext context, int orientation, String layoutName,
+            int simulatedPlatformVersion) {
         super(context);
         mSimulatedPlatformVersion = simulatedPlatformVersion;
         setOrientation(orientation);
@@ -79,89 +73,83 @@
         }
 
         LayoutInflater inflater = LayoutInflater.from(mContext);
-
-        XmlPullParser parser;
+        BridgeXmlBlockParser bridgeParser = loadXml(layoutName);
         try {
-            parser = ParserFactory.create(getClass().getResourceAsStream(layoutPath), name);
-
-            BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(parser, context, false);
-
-            try {
-                inflater.inflate(bridgeParser, this, true);
-            } finally {
-                bridgeParser.ensurePopped();
-            }
-        } catch (XmlPullParserException e) {
-            // Should not happen as the resource is bundled with the jar, and  ParserFactory should
-            // have been initialized.
-            assert false;
+            inflater.inflate(bridgeParser, this, true);
+        } finally {
+            bridgeParser.ensurePopped();
         }
     }
 
-    protected void loadIcon(int index, String iconName, Density density) {
-        loadIcon(index, iconName, density, false);
+    protected abstract TextView getStyleableTextView();
+
+    protected BridgeXmlBlockParser loadXml(String layoutName) {
+        return SysUiResources.loadXml((BridgeContext) mContext, mSimulatedPlatformVersion,
+                layoutName);
     }
 
-    protected void loadIcon(int index, String iconName, Density density, boolean isRtl) {
+    protected ImageView loadIcon(ImageView imageView, String iconName, Density density) {
+        return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName,
+                density, false);
+    }
+
+    protected ImageView loadIcon(int index, String iconName, Density density, boolean isRtl) {
         View child = getChildAt(index);
         if (child instanceof ImageView) {
             ImageView imageView = (ImageView) child;
-
-            LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
-            IconLoader iconLoader = new IconLoader(iconName, density, mSimulatedPlatformVersion,
-                    dir);
-            InputStream stream = iconLoader.getIcon();
-
-            if (stream != null) {
-                density = iconLoader.getDensity();
-                String path = iconLoader.getPath();
-                // look for a cached bitmap
-                Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
-                if (bitmap == null) {
-                    try {
-                        bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
-                        Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
-                    } catch (IOException e) {
-                        return;
-                    }
-                }
-
-                if (bitmap != null) {
-                    BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(),
-                            bitmap);
-                    imageView.setImageDrawable(drawable);
-                }
-            }
+            return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName,
+                    density, isRtl);
         }
+
+        return null;
     }
 
-    protected TextView setText(int index, String string, boolean reference) {
+    protected ImageView loadIcon(ImageView imageView, String iconName, Density density,
+            boolean isRtl) {
+        LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
+        IconLoader iconLoader = new IconLoader(iconName, density, mSimulatedPlatformVersion, dir);
+        InputStream stream = iconLoader.getIcon();
+
+        if (stream != null) {
+            density = iconLoader.getDensity();
+            String path = iconLoader.getPath();
+            // look for a cached bitmap
+            Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
+            if (bitmap == null) {
+                try {
+                    bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
+                    Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
+                } catch (IOException e) {
+                    return imageView;
+                }
+            }
+
+            if (bitmap != null) {
+                BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(), bitmap);
+                imageView.setImageDrawable(drawable);
+            }
+        }
+
+        return imageView;
+    }
+
+    protected TextView setText(int index, String string) {
         View child = getChildAt(index);
         if (child instanceof TextView) {
             TextView textView = (TextView) child;
-            setText(textView, string, reference);
+            textView.setText(string);
             return textView;
         }
 
         return null;
     }
 
-    private void setText(TextView textView, String string, boolean reference) {
-        if (reference) {
-            ResourceValue value = getResourceValue(string);
-            if (value != null) {
-                string = value.getValue();
-            }
-        }
-        textView.setText(string);
-    }
-
     protected void setStyle(String themeEntryName) {
-
         BridgeContext bridgeContext = getContext();
         RenderResources res = bridgeContext.getRenderResources();
 
-        ResourceValue value = res.findItemInTheme(themeEntryName, true /*isFrameworkAttr*/);
+        ResourceValue value =
+                res.findItemInTheme(BridgeContext.createFrameworkAttrReference(themeEntryName));
         value = res.resolveResValue(value);
 
         if (!(value instanceof StyleResourceValue)) {
@@ -171,8 +159,8 @@
         StyleResourceValue style = (StyleResourceValue) value;
 
         // get the background
-        ResourceValue backgroundValue = res.findItemInStyle(style, "background",
-                true /*isFrameworkAttr*/);
+        ResourceValue backgroundValue = res.findItemInStyle(style,
+                BridgeContext.createFrameworkAttrReference("background"));
         backgroundValue = res.resolveResValue(backgroundValue);
         if (backgroundValue != null) {
             Drawable d = ResourceHelper.getDrawable(backgroundValue, bridgeContext);
@@ -184,14 +172,14 @@
         TextView textView = getStyleableTextView();
         if (textView != null) {
             // get the text style
-            ResourceValue textStyleValue = res.findItemInStyle(style, "titleTextStyle",
-                    true /*isFrameworkAttr*/);
+            ResourceValue textStyleValue = res.findItemInStyle(style,
+                    BridgeContext.createFrameworkAttrReference("titleTextStyle"));
             textStyleValue = res.resolveResValue(textStyleValue);
             if (textStyleValue instanceof StyleResourceValue) {
                 StyleResourceValue textStyle = (StyleResourceValue) textStyleValue;
 
-                ResourceValue textSize = res.findItemInStyle(textStyle, "textSize",
-                        true /*isFrameworkAttr*/);
+                ResourceValue textSize = res.findItemInStyle(textStyle,
+                        BridgeContext.createFrameworkAttrReference("textSize"));
                 textSize = res.resolveResValue(textSize);
 
                 if (textSize != null) {
@@ -203,13 +191,12 @@
                     }
                 }
 
-
-                ResourceValue textColor = res.findItemInStyle(textStyle, "textColor",
-                        true);
+                ResourceValue textColor = res.findItemInStyle(textStyle,
+                        BridgeContext.createFrameworkAttrReference("textColor"));
                 textColor = res.resolveResValue(textColor);
                 if (textColor != null) {
-                    ColorStateList stateList = ResourceHelper.getColorStateList(
-                            textColor, bridgeContext, null);
+                    ColorStateList stateList =
+                            ResourceHelper.getColorStateList(textColor, bridgeContext, null);
                     if (stateList != null) {
                         textView.setTextColor(stateList);
                     }
@@ -240,14 +227,14 @@
         }
         RenderResources renderResources = getContext().getRenderResources();
         // First check if the bar is translucent.
-        boolean translucent = ResourceHelper.getBooleanThemeValue(renderResources,
-                translucentAttrName, true, false);
+        boolean translucent = ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources,
+                translucentAttrName, false);
         if (translucent) {
             // Keep in sync with R.color.system_bar_background_semi_transparent from system ui.
             return 0x66000000;  // 40% black.
         }
-        boolean transparent = ResourceHelper.getBooleanThemeValue(renderResources,
-                "windowDrawsSystemBarBackgrounds", true, false);
+        boolean transparent = ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources,
+                "windowDrawsSystemBarBackgrounds", false);
         if (transparent) {
             return getColor(renderResources, colorAttrName);
         }
@@ -255,8 +242,9 @@
     }
 
     private static int getColor(RenderResources renderResources, String attr) {
-        // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
-        ResourceValue resource = renderResources.findItemInTheme(attr, true);
+        // From ?attr/foo to @color/bar. This is most likely an StyleItemResourceValue.
+        ResourceValue resource =
+                renderResources.findItemInTheme(BridgeContext.createFrameworkAttrReference(attr));
         // Form @color/bar to the #AARRGGBB
         resource = renderResources.resolveResValue(resource);
         if (resource != null) {
@@ -277,14 +265,4 @@
         }
         return 0;
     }
-
-    private ResourceValue getResourceValue(String reference) {
-        RenderResources res = getContext().getRenderResources();
-
-        // find the resource
-        ResourceValue value = res.findResValue(reference, false);
-
-        // resolve it if needed
-        return res.resolveResValue(value);
-    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java b/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
index fd49c77..230094e 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layoutlib.bridge.bars;
 
 import com.android.ide.common.rendering.api.RenderResources;
@@ -42,13 +41,12 @@
 import android.widget.ListView;
 import android.widget.RelativeLayout;
 
-import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Creates the ActionBar as done by the framework.
  */
 public class FrameworkActionBar extends BridgeActionBar {
-
     private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
 
     // The Action Bar
@@ -89,11 +87,11 @@
     @Override
     protected ResourceValue getLayoutResource(BridgeContext context) {
         ResourceValue layoutName =
-                context.getRenderResources().findItemInTheme(LAYOUT_ATTR_NAME, true);
+                context.getRenderResources().findItemInTheme(
+                        BridgeContext.createFrameworkAttrReference(LAYOUT_ATTR_NAME));
         if (layoutName != null) {
             // We may need to resolve the reference obtained.
-            layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
-                    layoutName.isFramework());
+            layoutName = context.getRenderResources().dereference(layoutName);
         }
         if (layoutName == null) {
              throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME
@@ -124,7 +122,7 @@
     }
 
     @Override
-    protected void setIcon(String icon) {
+    protected void setIcon(ResourceValue icon) {
         mActionBar.setIcon(icon);
     }
 
@@ -173,7 +171,7 @@
             return false;
         }
         // Copied from android.widget.ActionMenuPresenter.updateMenuView()
-        ArrayList<MenuItemImpl> menus = mActionBar.getMenuBuilder().getNonActionItems();
+        List<MenuItemImpl> menus = mActionBar.getMenuBuilder().getNonActionItems();
         ActionMenuPresenter presenter = mActionBar.getActionMenuPresenter();
         if (presenter == null) {
             assert false : "Failed to create a Presenter for Action Bar Menus.";
@@ -238,7 +236,8 @@
     private int getActionBarHeight() {
         RenderResources resources = mBridgeContext.getRenderResources();
         DisplayMetrics metrics = mBridgeContext.getMetrics();
-        ResourceValue value = resources.findItemInTheme("actionBarSize", true);
+        ResourceValue value = resources.findItemInTheme(
+                BridgeContext.createFrameworkAttrReference("actionBarSize"));
 
         // resolve it
         value = resources.resolveResValue(value);
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java b/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
index 2cdc647..9811af4 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.common.rendering.api.ActionBarCallback;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.internal.R;
 import com.android.internal.app.ToolbarActionBar;
@@ -47,9 +48,6 @@
 import android.widget.Toolbar;
 import android.widget.Toolbar_Accessor;
 
-import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
-import static com.android.resources.ResourceType.MENU;
-
 /**
  * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
  */
@@ -106,7 +104,7 @@
         mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
     }
 
-    public void setIcon(String icon) {
+    public void setIcon(ResourceValue icon) {
         // Nothing to do.
     }
 
@@ -125,17 +123,9 @@
     protected void inflateMenus() {
         MenuInflater inflater = new MenuInflater(getActionMenuContext());
         MenuBuilder menuBuilder = getMenuBuilder();
-        for (String name : mCallback.getMenuIdNames()) {
-            int id;
-            if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
-                // Framework menu.
-                name = name.substring(ANDROID_NS_NAME_PREFIX.length());
-                id = mContext.getFrameworkResourceValue(MENU, name, -1);
-            } else {
-                // Project menu.
-                id = mContext.getProjectResourceValue(MENU, name, -1);
-            }
-            if (id > -1) {
+        for (ResourceReference menuId : mCallback.getMenuIds()) {
+            int id = mContext.getResourceId(menuId, -1);
+            if (id >= 0) {
                 inflater.inflate(id, menuBuilder);
             }
         }
@@ -288,10 +278,10 @@
         }
 
         @Override
-        public void setIcon(String icon) {
+        public void setIcon(ResourceValue icon) {
             // Set the icon only if the action bar doesn't specify an icon.
             if (!mActionBar.hasIcon() && icon != null) {
-                Drawable iconDrawable = getDrawable(icon, false);
+                Drawable iconDrawable = getDrawable(icon);
                 if (iconDrawable != null) {
                     mActionBar.setIcon(iconDrawable);
                 }
@@ -365,15 +355,13 @@
         }
 
         @Nullable
-        private Drawable getDrawable(@NonNull String name, boolean isFramework) {
+        private Drawable getDrawable(@NonNull ResourceValue value) {
             RenderResources res = mContext.getRenderResources();
-            ResourceValue value = res.findResValue(name, isFramework);
             value = res.resolveResValue(value);
             if (value != null) {
                 return ResourceHelper.getDrawable(value, mContext);
             }
             return null;
         }
-
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index dfbc69b..a244e2b 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -38,18 +38,18 @@
     private static final int WIDTH_DEFAULT = 36;
     private static final int WIDTH_SW360 = 40;
     private static final int WIDTH_SW600 = 48;
-    protected static final String LAYOUT_XML = "/bars/navigation_bar.xml";
-    private static final String LAYOUT_600DP_XML = "/bars/navigation_bar600dp.xml";
+    protected static final String LAYOUT_XML = "navigation_bar.xml";
+    private static final String LAYOUT_600DP_XML = "navigation_bar600dp.xml";
 
     public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
-      boolean rtlEnabled, int simulatedPlatformVersion) {
+      boolean rtlEnabled, int simulatedPlatformVersion, boolean quickStepEnabled) {
         this(context, density, orientation, isRtl, rtlEnabled, simulatedPlatformVersion,
-          getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML);
+          getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML, quickStepEnabled);
     }
 
     protected NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
-      boolean rtlEnabled, int simulatedPlatformVersion, String layoutPath) {
-        super(context, orientation, layoutPath, "navigation_bar.xml", simulatedPlatformVersion);
+      boolean rtlEnabled, int simulatedPlatformVersion, String layoutPath, boolean quickStepEnabled) {
+        super(context, orientation, layoutPath, simulatedPlatformVersion);
 
         int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
         setBackgroundColor(color == 0 ? 0xFF000000 : color);
@@ -67,11 +67,17 @@
         }
 
         //noinspection SpellCheckingInspection
-        loadIcon(back, "ic_sysbar_back.png", density, isRtl);
+        loadIcon(back,
+                quickStepEnabled ? "ic_sysbar_back_quick_step.png" : "ic_sysbar_back.png",
+                density, isRtl);
         //noinspection SpellCheckingInspection
-        loadIcon(3, "ic_sysbar_home.png", density, isRtl);
-        //noinspection SpellCheckingInspection
-        loadIcon(recent, "ic_sysbar_recent.png", density, isRtl);
+        loadIcon(3, quickStepEnabled ? "ic_sysbar_home_quick_step.png" : "ic_sysbar_home.png",
+                density,
+                isRtl);
+        if (!quickStepEnabled) {
+            //noinspection SpellCheckingInspection
+            loadIcon(recent, "ic_sysbar_recent.png", density, isRtl);
+        }
         setupNavBar(context, orientation);
     }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 2dc7c65..c653805 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -17,10 +17,12 @@
 package com.android.layoutlib.bridge.bars;
 
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.layoutlib.bridge.resources.IconLoader;
 import com.android.resources.Density;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -37,6 +39,8 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
 
 public class StatusBar extends CustomBar {
 
@@ -64,8 +68,7 @@
     public StatusBar(BridgeContext context, Density density, boolean isRtl, boolean rtlEnabled,
             int simulatedPlatformVersion) {
         // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
-        super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
-                simulatedPlatformVersion);
+        super(context, LinearLayout.HORIZONTAL, "status_bar.xml", simulatedPlatformVersion);
         mSimulatedPlatformVersion = simulatedPlatformVersion;
 
         // FIXME: use FILL_H?
@@ -74,46 +77,65 @@
         int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
         setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
 
+        List<ImageView> icons = new ArrayList<>(2);
+        TextView clockView = null;
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+
+            if (child instanceof ImageView) {
+                icons.add((ImageView) child);
+            } else if (child instanceof TextView) {
+                clockView = (TextView) child;
+            }
+        }
+
+        if (icons.size() != 2 || clockView == null) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to initialize statusbar", null,
+                    null);
+            return;
+        }
+
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
         // We do know the order though.
         // 0 is the spacer
-        loadIcon(1, "stat_sys_wifi_signal_4_fully."
+        loadIcon(icons.get(0), "stat_sys_wifi_signal_4_fully."
                         + Config.getWifiIconType(simulatedPlatformVersion), density);
-        loadIcon(2, "stat_sys_battery_100.png", density);
-        setText(3, Config.getTime(simulatedPlatformVersion), false)
-                .setTextColor(Config.getTimeColor(simulatedPlatformVersion));
+        loadIcon(icons.get(1), "stat_sys_battery_100.png", density);
+        clockView.setText(Config.getTime(simulatedPlatformVersion));
+        clockView.setTextColor(Config.getTimeColor(simulatedPlatformVersion));
     }
 
     @Override
-    protected void loadIcon(int index, String iconName, Density density) {
+    protected ImageView loadIcon(ImageView imageView, String iconName, Density density) {
         if (!iconName.endsWith(".xml")) {
-            super.loadIcon(index, iconName, density);
-            return;
+            return super.loadIcon(imageView, iconName, density);
         }
-        View child = getChildAt(index);
-        if (child instanceof ImageView) {
-            ImageView imageView = (ImageView) child;
-            // The xml is stored only in xhdpi.
-            IconLoader iconLoader = new IconLoader(iconName, Density.XHIGH,
-                    mSimulatedPlatformVersion, null);
-            InputStream stream = iconLoader.getIcon();
 
-            if (stream != null) {
-                try {
-                    BridgeXmlBlockParser parser = new BridgeXmlBlockParser(
-                            ParserFactory.create(stream, null), (BridgeContext) mContext, true);
-                    imageView.setImageDrawable(
-                            Drawable.createFromXml(mContext.getResources(), parser));
-                } catch (XmlPullParserException e) {
-                    Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
-                            null);
-                } catch (IOException e) {
-                    Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
-                            null);
-                }
+        // The xml is stored only in xhdpi.
+        IconLoader iconLoader = new IconLoader(iconName, Density.XHIGH,
+                mSimulatedPlatformVersion, null);
+        InputStream stream = iconLoader.getIcon();
+
+        if (stream != null) {
+            try {
+                BridgeXmlBlockParser parser =
+                        new BridgeXmlBlockParser(
+                                ParserFactory.create(stream, iconName),
+                                (BridgeContext) mContext,
+                                ResourceNamespace.ANDROID);
+                imageView.setImageDrawable(
+                        Drawable.createFromXml(mContext.getResources(), parser));
+            } catch (XmlPullParserException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
+                        null);
+            } catch (IOException e) {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
+                        null);
             }
         }
+
+        return imageView;
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java b/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
index 0435280..523f140 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/ThemePreviewNavigationBar.java
@@ -45,7 +45,7 @@
                 ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
                         View.LAYOUT_DIRECTION_RTL,
                 (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
-                0, LAYOUT_XML);
+                0, LAYOUT_XML, false);
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index 4fe1001..16578fb 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -28,13 +28,12 @@
     private TextView mTextView;
 
     public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion) {
-        super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
-                simulatedPlatformVersion);
+        super(context, LinearLayout.HORIZONTAL, "title_bar.xml", simulatedPlatformVersion);
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
         // We do know the order though.
-        mTextView = setText(0, label, true);
+        mTextView = setText(0, label);
 
         setStyle("windowTitleBackgroundStyle");
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 11da445..b71a0e2 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.impl;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.layoutlib.bridge.util.Debug;
 import com.android.layoutlib.bridge.util.SparseWeakArray;
 
@@ -25,9 +26,13 @@
 import java.io.PrintStream;
 import java.lang.ref.WeakReference;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.Set;
+import java.util.WeakHashMap;
 import java.util.concurrent.atomic.AtomicLong;
 
+import libcore.util.NativeAllocationRegistry_Delegate;
+
 /**
  * Manages native delegates.
  *
@@ -73,8 +78,6 @@
  * @param <T> the delegate class to manage
  */
 public final class DelegateManager<T> {
-    @SuppressWarnings("FieldCanBeLocal")
-    private final Class<T> mClass;
     private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>();
     /** Set used to store delegates when their main object holds a reference to them.
      * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
@@ -83,11 +86,46 @@
      */
     private static final Set<Object> sJavaReferences = new HashSet<>();
     private static final AtomicLong sDelegateCounter = new AtomicLong(1);
+    /**
+     * Tracks "native" allocations. This means that we know of the object in the Java side and we
+     * can attach the delegate lifecycle to the lifecycle of the Java object. If the Java object
+     * is disposed, it means we can get rid of the delegate allocation.
+     * Ideally, we would use a {@link WeakHashMap} but we do not control the equals() method of the
+     * referents so we can not safely rely on them.
+     */
+    private static final LinkedList<NativeAllocationHolder> sNativeAllocations = new LinkedList<>();
+    /**
+     * Map that allows to do a quick lookup of delegates that have been marked as native
+     * allocations.
+     * This allows us to quickly check if, when a manual dispose happens, there is work we have
+     * to do.
+     */
+    @GuardedBy("sNativeAllocations")
+    private static final WeakHashMap<Object, WeakReference<NativeAllocationHolder>>
+            sNativeAllocationsReferences = new WeakHashMap<>();
+    /**
+     * Counter of the number of native allocations. We use this counter to trigger the collection
+     * of unlinked references in the sNativeAllocations list. We do not need to do this process
+     * on every allocation so only run it every 50 allocations.
+     */
+    @GuardedBy("sNativeAllocations")
+    private static long sNativeAllocationsCount = 0;
+
+    @SuppressWarnings("FieldCanBeLocal")
+    private final Class<T> mClass;
 
     public DelegateManager(Class<T> theClass) {
         mClass = theClass;
     }
 
+    public synchronized static void dump(PrintStream out) {
+        for (Object reference : sJavaReferences) {
+            int idx = sDelegates.indexOfValue(reference);
+            out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
+        }
+        out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
+    }
+
     /**
      * Returns the delegate from the given native int.
      * <p>
@@ -156,15 +194,80 @@
                         " with int " + native_object);
             }
 
-            sJavaReferences.remove(delegate);
+            if (!sJavaReferences.remove(delegate)) {
+                // We didn't have any strong references to the delegate so it might be tracked by
+                // the native allocations tracker. If so, we want to remove that reference to
+                // make it available to collect ASAP.
+                synchronized (sNativeAllocations) {
+                    WeakReference<NativeAllocationHolder> holderRef = sNativeAllocationsReferences.get(delegate);
+                    NativeAllocationHolder holder = holderRef.get();
+                    if (holder != null) {
+                        // We only null the referred delegate. We do not spend the time in finding
+                        // the holder in the list and removing it since the "garbage collection" in
+                        // markAsNativeAllocation will do it for us.
+                        holder.mReferred = null;
+                    }
+                }
+            }
         }
     }
 
-    public synchronized static void dump(PrintStream out) {
-        for (Object reference : sJavaReferences) {
-            int idx = sDelegates.indexOfValue(reference);
-            out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
+    /**
+     * This method marks the given native_object as a native allocation of the passed referent.
+     * This means that the lifecycle of the native_object can now be attached to the referent and
+     * if the referent is disposed, we can safely dispose the delegate.
+     * This method is called by the {@link NativeAllocationRegistry_Delegate} and allows the
+     * DelegateManager to remove the strong reference to the delegate.
+     */
+    public void markAsNativeAllocation(Object referent, long native_object) {
+        NativeAllocationHolder holder;
+        synchronized (DelegateManager.class) {
+            T delegate = getDelegate(native_object);
+            if (Debug.DEBUG) {
+                if (delegate == null) {
+                    System.err.println("Unknown " + mClass.getSimpleName() + " with int " +
+                            native_object);
+                }
+                else {
+                    System.err.println("Marking element as native " + native_object);
+                }
+            }
+
+            assert delegate != null;
+            if (sJavaReferences.remove(delegate)) {
+                // We had a strong reference, move to the native allocation tracker.
+                holder = new NativeAllocationHolder(referent, delegate);
+            }
+            else {
+                holder = null;
+            }
         }
-        out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
+
+        if (holder != null) {
+            synchronized (sNativeAllocations) {
+                sNativeAllocations.add(holder);
+                // The value references the key in this case but we use a WeakReference value.
+                sNativeAllocationsReferences.put(holder.mReferred, new WeakReference<>(holder));
+
+                if (++sNativeAllocationsCount % 50 == 0) {
+                    // Do garbage collection
+                    boolean collected = sNativeAllocations.removeIf(e -> e.mReferent.get() == null);
+                    if (Debug.DEBUG && collected) {
+                        System.err.println("Elements collected");
+                    }
+                }
+            }
+        }
+    }
+
+    private static class NativeAllocationHolder {
+        private final WeakReference<Object> mReferent;
+        // The referred object is not null so we can null them on demand
+        private Object mReferred;
+
+        private NativeAllocationHolder(Object referent, Object referred) {
+            mReferent = new WeakReference<>(referent);
+            mReferred = referred;
+        }
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/DisplayCutoutView.java b/bridge/src/com/android/layoutlib/bridge/impl/DisplayCutoutView.java
new file mode 100644
index 0000000..48a8425
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/impl/DisplayCutoutView.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.View;
+
+class DisplayCutoutView extends View {
+
+    private final DisplayInfo mInfo = new DisplayInfo();
+    private final Paint mPaint = new Paint();
+    private final Region mBounds = new Region();
+    private final Rect mBoundingRect = new Rect();
+    private final Path mBoundingPath = new Path();
+    private final int[] mLocation = new int[2];
+    private final boolean mStart;
+
+    public DisplayCutoutView(Context context, boolean start) {
+        super(context);
+        mStart = start;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        update();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        getLocationOnScreen(mLocation);
+        canvas.translate(-mLocation[0], -mLocation[1]);
+        if (!mBoundingPath.isEmpty()) {
+            mPaint.setColor(Color.BLACK);
+            mPaint.setStyle(Paint.Style.FILL);
+            canvas.drawPath(mBoundingPath, mPaint);
+        }
+    }
+
+    private void update() {
+        requestLayout();
+        getDisplay().getDisplayInfo(mInfo);
+        mBounds.setEmpty();
+        mBoundingRect.setEmpty();
+        mBoundingPath.reset();
+        int newVisible;
+        if (hasCutout()) {
+            mBounds.set(mInfo.displayCutout.getBoundingRectTop());
+            localBounds(mBoundingRect);
+            mBounds.getBoundaryPath(mBoundingPath);
+            newVisible = VISIBLE;
+        } else {
+            newVisible = GONE;
+        }
+        if (newVisible != getVisibility()) {
+            setVisibility(newVisible);
+        }
+    }
+
+    private boolean hasCutout() {
+        final DisplayCutout displayCutout = mInfo.displayCutout;
+        if (displayCutout == null) {
+            return false;
+        }
+        if (mStart) {
+            return displayCutout.getSafeInsetLeft() > 0
+                    || displayCutout.getSafeInsetTop() > 0;
+        } else {
+            return displayCutout.getSafeInsetRight() > 0
+                    || displayCutout.getSafeInsetBottom() > 0;
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mBounds.isEmpty()) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            return;
+        }
+        setMeasuredDimension(
+                resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
+                resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
+    }
+
+    public static void boundsFromDirection(DisplayCutout displayCutout, int gravity, Rect out) {
+        Region bounds = new Region(displayCutout.getBoundingRectTop());
+        switch (gravity) {
+            case Gravity.TOP:
+                bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(),
+                        Region.Op.INTERSECT);
+                out.set(bounds.getBounds());
+                break;
+            case Gravity.LEFT:
+                bounds.op(0, 0, displayCutout.getSafeInsetLeft(), Integer.MAX_VALUE,
+                        Region.Op.INTERSECT);
+                out.set(bounds.getBounds());
+                break;
+            case Gravity.BOTTOM:
+                bounds.op(0, displayCutout.getSafeInsetTop() + 1, Integer.MAX_VALUE,
+                        Integer.MAX_VALUE, Region.Op.INTERSECT);
+                out.set(bounds.getBounds());
+                break;
+            case Gravity.RIGHT:
+                bounds.op(displayCutout.getSafeInsetLeft() + 1, 0, Integer.MAX_VALUE,
+                        Integer.MAX_VALUE, Region.Op.INTERSECT);
+                out.set(bounds.getBounds());
+                break;
+        }
+        bounds.recycle();
+    }
+
+    private void localBounds(Rect out) {
+        final DisplayCutout displayCutout = mInfo.displayCutout;
+
+        if (mStart) {
+            if (displayCutout.getSafeInsetLeft() > 0) {
+                boundsFromDirection(displayCutout, Gravity.LEFT, out);
+            } else if (displayCutout.getSafeInsetTop() > 0) {
+                boundsFromDirection(displayCutout, Gravity.TOP, out);
+            }
+        } else {
+            if (displayCutout.getSafeInsetRight() > 0) {
+                boundsFromDirection(displayCutout, Gravity.RIGHT, out);
+            } else if (displayCutout.getSafeInsetBottom() > 0) {
+                boundsFromDirection(displayCutout, Gravity.BOTTOM, out);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 7526e09..2ad7037 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -77,7 +77,6 @@
     /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
      * If this is null, this does not mean there's no layer, just that the snapshot is not the
      * one that created the layer.
-     * @see #getLayerSnapshot()
      */
     private final Layer mLocalLayer;
     private final Paint_Delegate mLocalLayerPaint;
@@ -252,7 +251,6 @@
 
     /**
      * Creates the root snapshot.
-     * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
      */
     private GcSnapshot() {
         mPrevious = null;
@@ -618,10 +616,6 @@
                 return;
             }
 
-            int x = 0;
-            int y = 0;
-            int width;
-            int height;
             Rectangle clipBounds = originalGraphics.getClip() != null ? originalGraphics
                     .getClipBounds() : null;
             if (clipBounds != null) {
@@ -629,17 +623,14 @@
                     // Clip is 0 so no need to paint anything.
                     return;
                 }
-                // If we have clipBounds available, use them as they will always be
-                // smaller than the full layer size.
-                x = clipBounds.x;
-                y = clipBounds.y;
-                width = clipBounds.width;
-                height = clipBounds.height;
-            } else {
-                width = layer.getImage().getWidth();
-                height = layer.getImage().getHeight();
             }
 
+            // b/63692596:
+            // Don't use the size of clip bound because it may be smaller than original size.
+            // Which makes Vector Drawable pixelized.
+            int width = layer.getImage().getWidth();
+            int height = layer.getImage().getHeight();
+
             // Create a temporary image to which the color filter will be applied.
             BufferedImage image = new BufferedImage(width, height,
                     BufferedImage.TYPE_INT_ARGB);
@@ -648,25 +639,27 @@
             Graphics2D imageGraphics = createCustomGraphics(
                     imageBaseGraphics, paint, compositeOnly,
                     AlphaComposite.SRC_OVER);
+
             // get a Graphics2D object configured with the drawing parameters, but no shader.
             Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
                     true /*compositeOnly*/, forceMode);
+            configuredGraphics.setTransform(new AffineTransform());
             try {
                 // The main draw operation.
                 // We translate the operation to take into account that the rendering does not
                 // know about the clipping area.
-                imageGraphics.translate(-x, -y);
+                imageGraphics.setTransform(originalGraphics.getTransform());
                 drawable.draw(imageGraphics, paint);
 
                 // Apply the color filter.
                 // Restore the original coordinates system and apply the filter only to the
                 // clipped area.
-                imageGraphics.translate(x, y);
+                imageGraphics.setTransform(new AffineTransform());
                 filter.applyFilter(imageGraphics, width, height);
 
                 // Draw the tinted image on the main layer using as start point the clipping
                 // upper left coordinates.
-                configuredGraphics.drawImage(image, x, y, null);
+                configuredGraphics.drawImage(image, 0, 0, null);
                 layer.change();
             } finally {
                 // dispose Graphics2D objects
@@ -774,6 +767,10 @@
         // make new one graphics
         Graphics2D g = (Graphics2D) original.create();
 
+        if (paint == null) {
+            return g;
+        }
+
         // configure it
 
         if (paint.isAntiAliased()) {
@@ -784,39 +781,33 @@
         }
 
         // set the shader first, as it'll replace the color if it can be used it.
-        boolean customShader = false;
         if (!compositeOnly) {
-            customShader = setShader(g, paint);
+            setShader(g, paint);
             // set the stroke
             g.setStroke(paint.getJavaStroke());
         }
         // set the composite.
-        setComposite(g, paint, compositeOnly || customShader, forceMode);
+        setComposite(g, paint, compositeOnly, forceMode);
 
         return g;
     }
 
-    private boolean setShader(Graphics2D g, Paint_Delegate paint) {
+    private void setShader(Graphics2D g, Paint_Delegate paint) {
         Shader_Delegate shaderDelegate = paint.getShader();
         if (shaderDelegate != null) {
             if (shaderDelegate.isSupported()) {
                 java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
                 assert shaderPaint != null;
-                if (shaderPaint != null) {
-                    g.setPaint(shaderPaint);
-                    return true;
-                }
+                g.setPaint(shaderPaint);
+                return;
             } else {
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
-                        shaderDelegate.getSupportMessage(),
-                        null /*throwable*/, null /*data*/);
+                        shaderDelegate.getSupportMessage(), null, null, null);
             }
         }
 
         // if no shader, use the paint color
         g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
-
-        return false;
     }
 
     private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
@@ -824,6 +815,10 @@
         // the alpha for the composite. Always opaque if the normal paint color is used since
         // it contains the alpha
         int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
+        Shader_Delegate shader = paint.getShader();
+        if (shader != null) {
+            alpha = (int)(alpha * shader.getAlpha());
+        }
         if (forceMode != 0) {
             g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
             return;
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index 8fad194..534bc5a 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -18,6 +18,8 @@
 
 import com.android.ide.common.rendering.api.HardwareConfig;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.layoutlib.bridge.Bridge;
@@ -35,6 +37,8 @@
 import com.android.resources.ScreenOrientation;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -49,6 +53,7 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.widget.LinearLayout.VERTICAL;
+import static com.android.layoutlib.bridge.impl.ResourceHelper.getBooleanThemeFrameworkAttrValue;
 import static com.android.layoutlib.bridge.impl.ResourceHelper.getBooleanThemeValue;
 
 /**
@@ -85,7 +90,7 @@
  *  +--------------------------------------+
  * </pre>
  */
-class Layout extends RelativeLayout {
+class Layout extends FrameLayout {
 
     // Theme attributes used for configuring appearance of the system decor.
     private static final String ATTR_WINDOW_FLOATING = "windowIsFloating";
@@ -122,6 +127,11 @@
     private Builder mBuilder;
 
     /**
+     * SysUI layout
+     */
+    private RelativeLayout mSysUiRoot;
+
+    /**
      * This holds user's layout.
      */
     private FrameLayout mContentRoot;
@@ -149,7 +159,7 @@
 
         if (mBuilder.hasNavBar()) {
             navBar = createNavBar(getContext(), density, isRtl, getParams().isRtlSupported(),
-                    simulatedPlatformVersion);
+                    simulatedPlatformVersion, false);
         }
 
         if (builder.hasStatusBar()) {
@@ -177,17 +187,28 @@
             frameworkActionBar = bar.getRootView();
         }
 
-        addViews(titleBar, mContentRoot == null ? (mContentRoot = createContentFrame()) : frameworkActionBar,
+        mSysUiRoot = new RelativeLayout(builder.mContext);
+        addSystemUiViews(titleBar, mContentRoot == null ? (mContentRoot = createContentFrame()) : frameworkActionBar,
                 statusBar, navBar, appCompatActionBar);
+        addView(mSysUiRoot);
+        //addView(createSysUiOverlay(mBuilder.mContext));
         // Done with the builder. Don't hold a reference to it.
         mBuilder = null;
     }
 
     @NonNull
+    private static View createSysUiOverlay(@NonNull BridgeContext context) {
+        SysUiOverlay overlay =  new SysUiOverlay(context, 20, 10, 50, 40, 60);
+        overlay.setNotchColor(Color.BLACK);
+        overlay.setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        return overlay;
+    }
+
+    @NonNull
     private FrameLayout createContentFrame() {
         FrameLayout contentRoot = new FrameLayout(getContext());
-        LayoutParams params = createLayoutParams(MATCH_PARENT, MATCH_PARENT);
-        int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
+        RelativeLayout.LayoutParams params = createSysUiLayoutParams(MATCH_PARENT, MATCH_PARENT);
+        int rule = mBuilder.isNavBarVertical() ? RelativeLayout.START_OF : RelativeLayout.ABOVE;
         if (mBuilder.hasSolidNavBar()) {
             params.addRule(rule, getId(ID_NAV_BAR));
         }
@@ -200,14 +221,14 @@
             below = getId(ID_STATUS_BAR);
         }
         if (below != -1) {
-            params.addRule(BELOW, below);
+            params.addRule(RelativeLayout.BELOW, below);
         }
         contentRoot.setLayoutParams(params);
         return contentRoot;
     }
 
     @NonNull
-    private LayoutParams createLayoutParams(int width, int height) {
+    private RelativeLayout.LayoutParams createSysUiLayoutParams(int width, int height) {
         DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
         if (width > 0) {
             width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics);
@@ -215,7 +236,7 @@
         if (height > 0) {
             height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics);
         }
-        return new LayoutParams(width, height);
+        return new RelativeLayout.LayoutParams(width, height);
     }
 
     @NonNull
@@ -244,9 +265,10 @@
             boolean isRtlSupported, int simulatedPlatformVersion) {
         StatusBar statusBar =
                 new StatusBar(context, density, isRtl, isRtlSupported, simulatedPlatformVersion);
-        LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mStatusBarSize);
+        RelativeLayout.LayoutParams params = createSysUiLayoutParams(MATCH_PARENT, mBuilder
+                .mStatusBarSize);
         if (mBuilder.isNavBarVertical()) {
-            params.addRule(START_OF, getId(ID_NAV_BAR));
+            params.addRule(RelativeLayout.START_OF, getId(ID_NAV_BAR));
         }
         statusBar.setLayoutParams(params);
         statusBar.setId(getId(ID_STATUS_BAR));
@@ -262,11 +284,11 @@
         // AppCompat ActionBar below it
         int heightRule = appCompatActionBar || !mBuilder.hasAppCompatActionBar() ? MATCH_PARENT :
           WRAP_CONTENT;
-        LayoutParams layoutParams = createLayoutParams(MATCH_PARENT, heightRule);
-        int rule = mBuilder.isNavBarVertical() ? START_OF : ABOVE;
+        RelativeLayout.LayoutParams layoutParams = createSysUiLayoutParams(MATCH_PARENT, heightRule);
+        int rule = mBuilder.isNavBarVertical() ? RelativeLayout.START_OF : RelativeLayout.ABOVE;
         if (mBuilder.hasSolidNavBar()) {
             // If there
-            if(rule == START_OF || appCompatActionBar || !mBuilder.hasAppCompatActionBar()) {
+            if(rule == RelativeLayout.START_OF || appCompatActionBar || !mBuilder.hasAppCompatActionBar()) {
                 layoutParams.addRule(rule, getId(ID_NAV_BAR));
             }
         }
@@ -278,15 +300,15 @@
             id = ID_APP_COMPAT_ACTION_BAR;
 
             if (mBuilder.hasTitleBar() || mBuilder.hasFrameworkActionBar()) {
-                layoutParams.addRule(BELOW, getId(ID_FRAMEWORK_BAR));
+                layoutParams.addRule(RelativeLayout.BELOW, getId(ID_FRAMEWORK_BAR));
             } else if (mBuilder.hasSolidStatusBar()) {
-                layoutParams.addRule(BELOW, getId(ID_STATUS_BAR));
+                layoutParams.addRule(RelativeLayout.BELOW, getId(ID_STATUS_BAR));
             }
         } else {
             actionBar = new FrameworkActionBar(context, params);
             id = ID_FRAMEWORK_BAR;
             if (mBuilder.hasSolidStatusBar()) {
-                layoutParams.addRule(BELOW, getId(ID_STATUS_BAR));
+                layoutParams.addRule(RelativeLayout.BELOW, getId(ID_STATUS_BAR));
             }
         }
 
@@ -300,12 +322,12 @@
     private TitleBar createTitleBar(BridgeContext context, String title,
             int simulatedPlatformVersion) {
         TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
-        LayoutParams params = createLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
+        RelativeLayout.LayoutParams params = createSysUiLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
         if (mBuilder.hasSolidStatusBar()) {
-            params.addRule(BELOW, getId(ID_STATUS_BAR));
+            params.addRule(RelativeLayout.BELOW, getId(ID_STATUS_BAR));
         }
         if (mBuilder.isNavBarVertical() && mBuilder.hasSolidNavBar()) {
-            params.addRule(START_OF, getId(ID_NAV_BAR));
+            params.addRule(RelativeLayout.START_OF, getId(ID_NAV_BAR));
         }
         titleBar.setLayoutParams(params);
         titleBar.setId(getId(ID_FRAMEWORK_BAR));
@@ -319,26 +341,29 @@
      */
     @NonNull
     private NavigationBar createNavBar(BridgeContext context, Density density, boolean isRtl,
-            boolean isRtlSupported, int simulatedPlatformVersion) {
+            boolean isRtlSupported, int simulatedPlatformVersion, boolean isQuickStepEnabled) {
         int orientation = mBuilder.mNavBarOrientation;
         int size = mBuilder.mNavBarSize;
+        // Only allow quickstep in the latest version or >= 28
+        isQuickStepEnabled = isQuickStepEnabled &&
+                (simulatedPlatformVersion == 0 || simulatedPlatformVersion >= 28);
         NavigationBar navBar =
                 new NavigationBar(context, density, orientation, isRtl, isRtlSupported,
-                        simulatedPlatformVersion);
+                        simulatedPlatformVersion, isQuickStepEnabled);
         boolean isVertical = mBuilder.isNavBarVertical();
         int w = isVertical ? size : MATCH_PARENT;
         int h = isVertical ? MATCH_PARENT : size;
-        LayoutParams params = createLayoutParams(w, h);
-        params.addRule(isVertical ? ALIGN_PARENT_END : ALIGN_PARENT_BOTTOM);
+        RelativeLayout.LayoutParams params = createSysUiLayoutParams(w, h);
+        params.addRule(isVertical ? RelativeLayout.ALIGN_PARENT_END : RelativeLayout.ALIGN_PARENT_BOTTOM);
         navBar.setLayoutParams(params);
         navBar.setId(getId(ID_NAV_BAR));
         return navBar;
     }
 
-    private void addViews(@NonNull View... views) {
+    private void addSystemUiViews(@NonNull View... views) {
         for (View view : views) {
             if (view != null) {
-                addView(view);
+                mSysUiRoot.addView(view);
             }
         }
     }
@@ -384,7 +409,8 @@
             mParams = params;
             mContext = context;
             mResources = mParams.getResources();
-            mWindowIsFloating = getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true);
+            mWindowIsFloating =
+                    getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_FLOATING, true);
 
             findBackground();
 
@@ -398,25 +424,26 @@
 
         private void findBackground() {
             if (!mParams.isBgColorOverridden()) {
-                mWindowBackground = mResources.findItemInTheme(ATTR_WINDOW_BACKGROUND, true);
+                mWindowBackground = mResources.findItemInTheme(
+                        BridgeContext.createFrameworkAttrReference(ATTR_WINDOW_BACKGROUND));
                 mWindowBackground = mResources.resolveResValue(mWindowBackground);
             }
         }
 
         private void findStatusBar() {
             boolean windowFullScreen =
-                    getBooleanThemeValue(mResources, ATTR_WINDOW_FULL_SCREEN, true, false);
+                    getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_FULL_SCREEN, false);
             if (!windowFullScreen && !mWindowIsFloating) {
                 mStatusBarSize =
-                        getDimension(ATTR_STATUS_BAR_HEIGHT, true, DEFAULT_STATUS_BAR_HEIGHT);
+                        getFrameworkAttrDimension(ATTR_STATUS_BAR_HEIGHT, DEFAULT_STATUS_BAR_HEIGHT);
                 mTranslucentStatus =
-                        getBooleanThemeValue(mResources, ATTR_WINDOW_TRANSLUCENT_STATUS, true,
-                                false);
+                        getBooleanThemeFrameworkAttrValue(
+                                mResources, ATTR_WINDOW_TRANSLUCENT_STATUS, false);
             }
         }
 
         /**
-         * The behavior is different wether the App is using AppCompat or not.
+         * The behavior is different whether the App is using AppCompat or not.
          * <h1>With App compat :</h1>
          * <li> framework ("android:") attributes have to effect
          * <li> windowNoTile=true hides the AppCompatActionBar
@@ -428,14 +455,17 @@
             }
 
             boolean windowNoTitle =
-                    getBooleanThemeValue(mResources, ATTR_WINDOW_NO_TITLE, false, false);
+                    getBooleanThemeValue(mResources,
+                            mContext.createAppCompatAttrReference(ATTR_WINDOW_NO_TITLE), false);
 
             boolean windowActionBar =
-                    getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR, false, true);
+                    getBooleanThemeValue(mResources,
+                            mContext.createAppCompatAttrReference(ATTR_WINDOW_ACTION_BAR), true);
 
             if (!windowNoTitle && windowActionBar) {
                 mAppCompatActionBarSize =
-                        getDimension(ATTR_ACTION_BAR_SIZE, false, DEFAULT_TITLE_BAR_HEIGHT);
+                        getDimension(mContext.createAppCompatAttrReference(ATTR_ACTION_BAR_SIZE),
+                                DEFAULT_TITLE_BAR_HEIGHT);
             }
         }
 
@@ -464,29 +494,28 @@
                 return;
             }
             boolean frameworkWindowNoTitle =
-                    getBooleanThemeValue(mResources, ATTR_WINDOW_NO_TITLE, true, false);
+                    getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_NO_TITLE, false);
 
             // Check if an actionbar is needed
             boolean isMenu = "menu".equals(mParams.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG));
 
-
             boolean windowActionBar =
-                    getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR, true, true);
+                    getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_ACTION_BAR, true);
 
             if (!frameworkWindowNoTitle || isMenu) {
                 if (isMenu || windowActionBar) {
                     mFrameworkActionBarSize =
-                            getDimension(ATTR_ACTION_BAR_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT);
+                            getFrameworkAttrDimension(ATTR_ACTION_BAR_SIZE, DEFAULT_TITLE_BAR_HEIGHT);
                 } else {
-                    mTitleBarSize =
-                            getDimension(ATTR_WINDOW_TITLE_SIZE, false, DEFAULT_TITLE_BAR_HEIGHT);
+                    mTitleBarSize = getDimension(
+                            mContext.createAppCompatAttrReference(ATTR_WINDOW_TITLE_SIZE),
+                            DEFAULT_TITLE_BAR_HEIGHT);
                 }
             }
         }
 
         private void findNavBar() {
             if (hasSoftwareButtons() && !mWindowIsFloating) {
-
                 // get orientation
                 HardwareConfig hwConfig = mParams.getHardwareConfig();
                 boolean barOnBottom = true;
@@ -503,19 +532,22 @@
 
                 mNavBarOrientation = barOnBottom ? LinearLayout.HORIZONTAL : VERTICAL;
                 mNavBarSize =
-                        getDimension(barOnBottom ? ATTR_NAV_BAR_HEIGHT : ATTR_NAV_BAR_WIDTH, true,
+                        getFrameworkAttrDimension(
+                                barOnBottom ? ATTR_NAV_BAR_HEIGHT : ATTR_NAV_BAR_WIDTH,
                                 DEFAULT_NAV_BAR_SIZE);
                 mTranslucentNav =
-                        getBooleanThemeValue(mResources, ATTR_WINDOW_TRANSLUCENT_NAV, true, false);
+                        getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_TRANSLUCENT_NAV,
+                                false);
             }
         }
 
         @SuppressWarnings("SameParameterValue")
-        private int getDimension(String attr, boolean isFramework, int defaultValue) {
-            ResourceValue value = mResources.findItemInTheme(attr, isFramework);
+        private int getDimension(@NonNull ResourceReference attrRef, int defaultValue) {
+            ResourceValue value = mResources.findItemInTheme(attrRef);
             value = mResources.resolveResValue(value);
             if (value != null) {
-                TypedValue typedValue = ResourceHelper.getValue(attr, value.getValue(), true);
+                TypedValue typedValue = ResourceHelper.getValue(attrRef.getName(), value.getValue(),
+                        true);
                 if (typedValue != null) {
                     return (int) typedValue.getDimension(mContext.getMetrics());
                 }
@@ -523,19 +555,24 @@
             return defaultValue;
         }
 
+        @SuppressWarnings("SameParameterValue")
+        private int getFrameworkAttrDimension(@NonNull String attr, int defaultValue) {
+            return getDimension(BridgeContext.createFrameworkAttrReference(attr), defaultValue);
+        }
+
         private boolean hasSoftwareButtons() {
             return mParams.getHardwareConfig().hasSoftwareButtons();
         }
 
         /**
-         * Return true if the nav bar is present and not translucent
+         * Returns true if the nav bar is present and not translucent.
          */
         private boolean hasSolidNavBar() {
             return hasNavBar() && !mTranslucentNav;
         }
 
         /**
-         * Return true if the status bar is present and not translucent
+         * Returns true if the status bar is present and not translucent.
          */
         private boolean hasSolidStatusBar() {
             return hasStatusBar() && !mTranslucentStatus;
@@ -568,5 +605,9 @@
         private boolean hasFrameworkActionBar() {
             return mFrameworkActionBarSize > 0;
         }
+
+        private boolean hasNotch() {
+            return !mParams.isForceNoDecor();
+        }
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
index 1ae9cb6..38b7aa5 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.impl;
 
+import com.android.ide.common.rendering.api.XmlParserFactory;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -25,55 +26,34 @@
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 
 /**
  * A factory for {@link XmlPullParser}.
- *
  */
 public class ParserFactory {
-
     public final static boolean LOG_PARSER = false;
 
     // Used to get a new XmlPullParser from the client.
     @Nullable
-    private static com.android.ide.common.rendering.api.ParserFactory sParserFactory;
+    private static XmlParserFactory sParserFactory;
 
-    public static void setParserFactory(
-            @Nullable com.android.ide.common.rendering.api.ParserFactory parserFactory) {
+    public static void setParserFactory(@Nullable XmlParserFactory parserFactory) {
         sParserFactory = parserFactory;
     }
 
-    @NonNull
-    public static XmlPullParser create(@NonNull File f)
-            throws XmlPullParserException, FileNotFoundException {
-        return create(f, false);
+    @Nullable
+    public static XmlPullParser create(@NonNull String filePath)
+            throws XmlPullParserException {
+        return create(filePath, false);
     }
 
-    public static XmlPullParser create(@NonNull File f, boolean isLayout)
-      throws XmlPullParserException, FileNotFoundException {
-        InputStream stream = new FileInputStream(f);
-        return create(stream, f.getName(), f.length(), isLayout);
-    }
-    @NonNull
-    public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name)
-        throws XmlPullParserException {
-        return create(stream, name, -1, false);
-    }
-
-    @NonNull
-    private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name,
-            long size, boolean isLayout) throws XmlPullParserException {
-        XmlPullParser parser = instantiateParser(name);
-
-        stream = readAndClose(stream, name, size);
-
-        parser.setInput(stream, null);
-        if (isLayout) {
+    @Nullable
+    public static XmlPullParser create(@NonNull String filePath, boolean isLayout)
+            throws XmlPullParserException {
+        XmlPullParser parser = sParserFactory.createXmlParserForFile(filePath);
+        if (parser != null && isLayout) {
             try {
                 return new LayoutParserWrapper(parser).peekTillLayoutStart();
             } catch (IOException e) {
@@ -84,46 +64,38 @@
     }
 
     @NonNull
-    public static XmlPullParser instantiateParser(@Nullable String name)
+    public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name)
             throws XmlPullParserException {
+        XmlPullParser parser = create();
+
+        stream = readAndClose(stream, name);
+
+        parser.setInput(stream, null);
+        return parser;
+    }
+
+    @NonNull
+    public static XmlPullParser create() throws XmlPullParserException {
         if (sParserFactory == null) {
             throw new XmlPullParserException("ParserFactory not initialized.");
         }
-        XmlPullParser parser = sParserFactory.createParser(name);
+        XmlPullParser parser = sParserFactory.createXmlParser();
         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
         return parser;
     }
 
     @NonNull
-    private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name,
-            long size) throws XmlPullParserException {
-        // just a sanity check. It's doubtful we'll have such big files!
-        if (size > Integer.MAX_VALUE) {
-            throw new XmlPullParserException("File " + name + " is too big to be parsed");
-        }
-        int intSize = (int) size;
+    private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name)
+            throws XmlPullParserException {
+        // Create a buffered stream to facilitate reading.
+        try (BufferedInputStream bufferedStream = new BufferedInputStream(stream)) {
+            int avail = bufferedStream.available();
 
-        // create a buffered reader to facilitate reading.
-        BufferedInputStream bufferedStream = new BufferedInputStream(stream);
-        try {
-            int avail;
-            if (intSize != -1) {
-                avail = intSize;
-            } else {
-                // get the size to read.
-                avail = bufferedStream.available();
-            }
-
-            // create the initial buffer and read it.
+            // Create the initial buffer and read it.
             byte[] buffer = new byte[avail];
             int read = stream.read(buffer);
 
-            // this is the easy case.
-            if (read == intSize) {
-                return new ByteArrayInputStream(buffer);
-            }
-
-            // check if there is more to read (read() does not necessarily read all that
+            // Check if there is more to read (read() does not necessarily read all that
             // available() returned!)
             while ((avail = bufferedStream.available()) > 0) {
                 if (read + avail > buffer.length) {
@@ -137,16 +109,10 @@
                 read += stream.read(buffer, read, avail);
             }
 
-            // return a new stream encapsulating this buffer.
+            // Return a new stream encapsulating this buffer.
             return new ByteArrayInputStream(buffer);
-
         } catch (IOException e) {
             throw new XmlPullParserException("Failed to read " + name, null, e);
-        } finally {
-            try {
-                bufferedStream.close();
-            } catch (IOException ignored) {
-            }
         }
     }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 5b42df1..51dcae9 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -20,17 +20,16 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderParams;
 import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider;
 import com.android.ide.common.rendering.api.Result;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.Density;
-import com.android.resources.ResourceType;
 import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenRound;
 import com.android.resources.ScreenSize;
 import com.android.tools.layoutlib.annotations.VisibleForTesting;
 
+import android.animation.PropertyValuesHolder_Accessor;
 import android.content.res.Configuration;
 import android.os.HandlerThread_Delegate;
 import android.util.DisplayMetrics;
@@ -39,7 +38,6 @@
 import android.view.Surface;
 import android.view.ViewConfiguration_Accessor;
 import android.view.WindowManagerGlobal_Delegate;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodManager_Accessor;
 
 import java.util.Locale;
@@ -62,7 +60,7 @@
  * @param <T> the {@link RenderParams} implementation
  *
  */
-public abstract class RenderAction<T extends RenderParams> extends FrameworkResourceIdProvider {
+public abstract class RenderAction<T extends RenderParams> {
 
     /**
      * The current context being rendered. This is set through {@link #acquire(long)} and
@@ -235,16 +233,13 @@
      */
     private void setUp() {
         // setup the ParserFactory
-        ParserFactory.setParserFactory(mParams.getLayoutlibCallback().getParserFactory());
+        ParserFactory.setParserFactory(mParams.getLayoutlibCallback());
 
         // make sure the Resources object references the context (and other objects) for this
         // scene
         mContext.initResources();
         sCurrentContext = mContext;
 
-        // create an InputMethodManager
-        InputMethodManager.getInstance();
-
         // Set-up WindowManager
         // FIXME: find those out, and possibly add them to the render params
         boolean hasNavigationBar = true;
@@ -255,7 +250,6 @@
 
         LayoutLog currentLog = mParams.getLog();
         Bridge.setLog(currentLog);
-        mContext.getRenderResources().setFrameworkResourceIdProvider(this);
         mContext.getRenderResources().setLogger(currentLog);
     }
 
@@ -280,16 +274,17 @@
         ViewConfiguration_Accessor.clearConfigurations();
 
         // remove the InputMethodManager
-        InputMethodManager_Accessor.resetInstance();
+        InputMethodManager_Accessor.tearDownEditMode();
 
         sCurrentContext = null;
 
         Bridge.setLog(null);
         if (mContext != null) {
-            mContext.getRenderResources().setFrameworkResourceIdProvider(null);
             mContext.getRenderResources().setLogger(null);
         }
         ParserFactory.setParserFactory(null);
+
+        PropertyValuesHolder_Accessor.clearClassCaches();
     }
 
     public static BridgeContext getCurrentContext() {
@@ -362,8 +357,8 @@
             density = Density.MEDIUM;
         }
 
-        config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue();
-        config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue();
+        config.screenWidthDp = hardwareConfig.getScreenWidth() * 160 / density.getDpiValue();
+        config.screenHeightDp = hardwareConfig.getScreenHeight() * 160 / density.getDpiValue();
         if (config.screenHeightDp < config.screenWidthDp) {
             //noinspection SuspiciousNameCombination
             config.smallestScreenWidthDp = config.screenHeightDp;
@@ -413,12 +408,4 @@
 
         return config;
     }
-
-
-    // --- FrameworkResourceIdProvider methods
-
-    @Override
-    public Integer getId(ResourceType resType, String resName) {
-        return Bridge.getResourceId(resType, resName);
-    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index d797eec..35ec77b 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -25,6 +25,7 @@
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.resources.ResourceType;
 
+import android.annotation.NonNull;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
@@ -43,28 +44,28 @@
 import java.util.List;
 
 /**
- * Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
+ * Action to render a given {@link Drawable} provided through {@link DrawableParams#getDrawable()}.
  *
  * The class only provides a simple {@link #render()} method, but the full life-cycle of the
  * action must be respected.
  *
  * @see RenderAction
- *
  */
 public class RenderDrawable extends RenderAction<DrawableParams> {
 
-    public RenderDrawable(DrawableParams params) {
+    public RenderDrawable(@NonNull DrawableParams params) {
         super(new DrawableParams(params));
     }
 
+    @NonNull
     public Result render() {
         checkLock();
-        // get the drawable resource value
+        // Get the drawable resource value.
         DrawableParams params = getParams();
         HardwareConfig hardwareConfig = params.getHardwareConfig();
         ResourceValue drawableResource = params.getDrawable();
 
-        // resolve it
+        // Resolve it.
         BridgeContext context = getContext();
         drawableResource = context.getRenderResources().resolveResValue(drawableResource);
 
@@ -78,17 +79,19 @@
         }
 
         Drawable d = ResourceHelper.getDrawable(drawableResource, context);
+        if (d == null) {
+            return Status.ERROR_NOT_A_DRAWABLE.createResult();
+        }
 
-        final Boolean allStates =
-                params.getFlag(RenderParamsFlags.FLAG_KEY_RENDER_ALL_DRAWABLE_STATES);
+        Boolean allStates = params.getFlag(RenderParamsFlags.FLAG_KEY_RENDER_ALL_DRAWABLE_STATES);
         if (allStates == Boolean.TRUE) {
-            final List<BufferedImage> result;
+            List<BufferedImage> result;
 
             if (d instanceof StateListDrawable) {
                 result = new ArrayList<BufferedImage>();
-                final StateListDrawable stateList = (StateListDrawable) d;
+                StateListDrawable stateList = (StateListDrawable) d;
                 for (int i = 0; i < stateList.getStateCount(); i++) {
-                    final Drawable stateDrawable = stateList.getStateDrawable(i);
+                    Drawable stateDrawable = stateList.getStateDrawable(i);
                     result.add(renderImage(hardwareConfig, stateDrawable, context));
                 }
             } else {
@@ -102,33 +105,33 @@
         }
     }
 
-    private BufferedImage renderImage(HardwareConfig hardwareConfig, Drawable d,
-            BridgeContext context) {
-        // create a simple FrameLayout
+    @NonNull
+    private BufferedImage renderImage(@NonNull HardwareConfig hardwareConfig, @NonNull Drawable d,
+            @NonNull BridgeContext context) {
+        // Create a simple FrameLayout.
         FrameLayout content = new FrameLayout(context);
 
-        // get the actual Drawable object to draw
+        // Get the actual Drawable object to draw.
         content.setBackground(d);
 
-        // set the AttachInfo on the root view.
+        // Set the AttachInfo on the root view.
         AttachInfo_Accessor.setAttachInfo(content);
 
-
-        // measure
+        // Measure.
         int w = d.getIntrinsicWidth();
         int h = d.getIntrinsicHeight();
 
-        final int screenWidth = hardwareConfig.getScreenWidth();
-        final int screenHeight = hardwareConfig.getScreenHeight();
+        int screenWidth = hardwareConfig.getScreenWidth();
+        int screenHeight = hardwareConfig.getScreenHeight();
 
         if (w == -1 || h == -1) {
-            // Use screen size when either intrinsic width or height isn't available
+            // Use screen size when either intrinsic width or height isn't available.
             w = screenWidth;
             h = screenHeight;
         } else if (w > screenWidth || h > screenHeight) {
             // If image wouldn't fit to the screen, resize it to avoid cropping.
 
-            // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight
+            // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight.
             double scale = Math.min((double) screenWidth / w, (double) screenHeight / h);
 
             // scale * w / scale * h = w / h, so, proportions are preserved.
@@ -140,28 +143,29 @@
         int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
         content.measure(w_spec, h_spec);
 
-        // now do the layout.
+        // Now do the layout.
         content.layout(0, 0, w, h);
 
-        // preDraw setup
+        // Pre-draw setup.
         AttachInfo_Accessor.dispatchOnPreDraw(content);
 
-        // draw into a new image
+        // Draw into a new image.
         BufferedImage image = getImage(w, h);
 
-        // create an Android bitmap around the BufferedImage
+        // Create an Android bitmap around the BufferedImage.
         Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
                 true /*isMutable*/, hardwareConfig.getDensity());
 
-        // create a Canvas around the Android bitmap
+        // Create a Canvas around the Android bitmap.
         Canvas canvas = new Canvas(bitmap);
         canvas.setDensity(hardwareConfig.getDensity().getDpiValue());
 
-        // and draw
+        // Draw.
         content.draw(canvas);
         return image;
     }
 
+    @NonNull
     protected BufferedImage getImage(int w, int h) {
         BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
         Graphics2D gc = image.createGraphics();
@@ -175,5 +179,4 @@
 
         return image;
     }
-
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 7168b54..9c314b8 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -18,9 +18,9 @@
 
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.HardwareConfig;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
@@ -45,11 +45,8 @@
 import com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
 import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
 import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
-import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.java.System_Delegate;
 import com.android.util.Pair;
-import com.android.util.PropertiesMap;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -57,6 +54,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
+import android.graphics.NinePatch_Delegate;
 import android.os.Looper;
 import android.preference.Preference_Delegate;
 import android.view.AttachInfo_Accessor;
@@ -86,6 +84,7 @@
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.util.ArrayList;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -171,8 +170,9 @@
         BridgeContext context = getContext();
 
         // use default of true in case it's not found to use alpha by default
-        mIsAlphaChannelImage = ResourceHelper.getBooleanThemeValue(params.getResources(),
-                "windowIsFloating", true, true);
+        mIsAlphaChannelImage =
+                ResourceHelper.getBooleanThemeFrameworkAttrValue(params.getResources(),
+                        "windowIsFloating", true);
 
         mLayoutBuilder = new Layout.Builder(params, context);
 
@@ -180,7 +180,8 @@
         mInflater = new BridgeInflater(context, params.getLayoutlibCallback());
         context.setBridgeInflater(mInflater);
 
-        mBlockParser = new BridgeXmlBlockParser(params.getLayoutDescription(), context, false);
+        ILayoutPullParser layoutParser = params.getLayoutDescription();
+        mBlockParser = new BridgeXmlBlockParser(layoutParser, context, layoutParser.getLayoutNamespace());
 
         return SUCCESS.createResult();
     }
@@ -296,7 +297,8 @@
                     Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_ENABLED,
                             "You are using a right-to-left " +
                                     "(RTL) locale but RTL is not enabled", null);
-                } else if (params.getSimulatedPlatformVersion() < 17) {
+                } else if (params.getSimulatedPlatformVersion() !=0 &&
+                        params.getSimulatedPlatformVersion() < 17) {
                     // This will render ok because we are using the latest layoutlib but at least
                     // warn the user that this might fail in a real device.
                     Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_SUPPORTED, "You are using a " +
@@ -310,7 +312,8 @@
             Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback());
 
             String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG);
-            boolean isPreference = "PreferenceScreen".equals(rootTag);
+            boolean isPreference = "PreferenceScreen".equals(rootTag) ||
+                    SupportPreferencesUtil.isSupportRootTag(rootTag);
             View view;
             if (isPreference) {
                 // First try to use the support library inflater. If something fails, fallback
@@ -484,6 +487,7 @@
                 // it doesn't get cached.
                 boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
                     RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
+
                 if (mNewRenderSize || mCanvas == null || disableBitmapCaching) {
                     mNewRenderSize = false;
                     if (params.getImageFactory() != null) {
@@ -518,6 +522,19 @@
                     } else {
                         mCanvas.setBitmap(bitmap);
                     }
+
+                    boolean enableImageResizing =
+                            mImage.getWidth() != mMeasuredScreenWidth &&
+                            mImage.getHeight() != mMeasuredScreenHeight &&
+                            Boolean.TRUE.equals(params.getFlag(
+                                    RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE));
+
+                    if (enableImageResizing) {
+                        float scaleX = (float)mImage.getWidth() / mMeasuredScreenWidth;
+                        float scaleY = (float)mImage.getHeight() / mMeasuredScreenHeight;
+                        mCanvas.scale(scaleX, scaleY);
+                    }
+
                     mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
                 }
 
@@ -715,12 +732,7 @@
         if (!hasToolbar(collapsingToolbar)) {
             return;
         }
-        RenderResources res = context.getRenderResources();
         String title = params.getAppLabel();
-        ResourceValue titleValue = res.findResValue(title, false);
-        if (titleValue != null && titleValue.getValue() != null) {
-            title = titleValue.getValue();
-        }
         DesignLibUtil.setTitle(collapsingToolbar, title);
     }
 
@@ -832,7 +844,7 @@
 
         // this must be called before addTab() so that the TabHost searches its TabWidget
         // and FrameLayout.
-        if (ReflectionUtils.isInstanceOf(tabHost, FragmentTabHostUtil.CN_FRAGMENT_TAB_HOST)) {
+        if (isInstanceOf(tabHost, FragmentTabHostUtil.CN_FRAGMENT_TAB_HOST)) {
             FragmentTabHostUtil.setup(tabHost, getContext());
         } else {
             tabHost.setup();
@@ -853,10 +865,10 @@
                 @SuppressWarnings("ConstantConditions")  // child cannot be null.
                 int id = child.getId();
                 @SuppressWarnings("deprecation")
-                Pair<ResourceType, String> resource = layoutlibCallback.resolveResourceId(id);
+                ResourceReference resource = layoutlibCallback.resolveResourceId(id);
                 String name;
                 if (resource != null) {
-                    name = resource.getSecond();
+                    name = resource.getName();
                 } else {
                     name = String.format("Tab %d", i+1); // default name if id is unresolved.
                 }
@@ -992,10 +1004,14 @@
 
             // The view is part of the layout added by the user. Hence,
             // the ViewCookie may be obtained only through the Context.
+            int shiftX = -scrollX + Math.round(view.getTranslationX()) + hOffset;
+            int shiftY = -scrollY + Math.round(view.getTranslationY()) + vOffset;
             result = new ViewInfo(view.getClass().getName(),
-                    getContext().getViewKey(view), -scrollX + view.getLeft() + hOffset,
-                    -scrollY + view.getTop() + vOffset, -scrollX + view.getRight() + hOffset,
-                    -scrollY + view.getBottom() + vOffset,
+                    getContext().getViewKey(view),
+                    shiftX + view.getLeft(),
+                    shiftY + view.getTop(),
+                    shiftX + view.getRight(),
+                    shiftY + view.getBottom(),
                     view, view.getLayoutParams());
         } else {
             // We are part of the system decor.
@@ -1098,10 +1114,24 @@
         return mSystemViewInfoList;
     }
 
-    public Map<Object, PropertiesMap> getDefaultProperties() {
+    public Map<Object, Map<ResourceReference, ResourceValue>> getDefaultNamespacedProperties() {
         return getContext().getDefaultProperties();
     }
 
+    public Map<Object, String> getDefaultStyles() {
+        Map<Object, String> defaultStyles = new IdentityHashMap<>();
+        Map<Object, ResourceReference> namespacedStyles = getDefaultNamespacedStyles();
+        for (Object key : namespacedStyles.keySet()) {
+            ResourceReference style = namespacedStyles.get(key);
+            defaultStyles.put(key, style.getQualifiedName());
+        }
+        return defaultStyles;
+    }
+
+    public Map<Object, ResourceReference> getDefaultNamespacedStyles() {
+        return getContext().getDefaultNamespacedStyles();
+    }
+
     public void setScene(RenderSession session) {
         mScene = session;
     }
@@ -1133,6 +1163,7 @@
         mImage = null;
         mViewRoot = null;
         mContentRoot = null;
+        NinePatch_Delegate.clearCache();
 
         if (createdLooper) {
             Choreographer_Delegate.dispose();
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 16f92f3..d3b934f 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -13,38 +13,42 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layoutlib.bridge.impl;
 
 import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.DensityBasedResourceValue;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.internal.util.XmlUtils;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeContext.Key;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.ninepatch.NinePatch;
 import com.android.ninepatch.NinePatchChunk;
 import com.android.resources.Density;
+import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.res.BridgeAssetManager;
 import android.content.res.ColorStateList;
 import android.content.res.ComplexColor;
 import android.content.res.ComplexColor_Accessor;
-import android.content.res.FontResourcesParser;
 import android.content.res.GradientColor;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
-import android.graphics.Color;
 import android.graphics.NinePatch_Delegate;
 import android.graphics.Rect;
 import android.graphics.Typeface;
@@ -54,30 +58,33 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
-import android.text.FontConfig;
 import android.util.TypedValue;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static android.content.res.AssetManager.ACCESS_STREAMING;
+
 /**
  * Helper class to provide various conversion method used in handling android resources.
  */
 public final class ResourceHelper {
+    private static final Key<Set<ResourceValue>> KEY_GET_DRAWABLE =
+            Key.create("ResourceHelper.getDrawable");
+    private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
+    private static final float[] sFloatOut = new float[1];
 
-    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
-    private final static float[] sFloatOut = new float[1];
-
-    private final static TypedValue mValue = new TypedValue();
+    private static final TypedValue mValue = new TypedValue();
 
     /**
-     * Returns the color value represented by the given string value
+     * Returns the color value represented by the given string value.
+     *
      * @param value the color value
      * @return the color as an int
      * @throws NumberFormatException if the conversion failed.
@@ -242,10 +249,12 @@
 
     /**
      * Returns a drawable from the given value.
+     *
      * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
-     * or an hexadecimal color
+     *     or an hexadecimal color
      * @param context the current context
      */
+    @Nullable
     public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
         return getDrawable(value, context, null);
     }
@@ -256,38 +265,43 @@
      */
     @Nullable
     public static BridgeXmlBlockParser getXmlBlockParser(@NonNull BridgeContext context,
-            @NonNull ResourceValue value)
-            throws FileNotFoundException, XmlPullParserException {
+            @NonNull ResourceValue value) throws XmlPullParserException {
         String stringValue = value.getValue();
         if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
             return null;
         }
 
         XmlPullParser parser = null;
+        ResourceNamespace namespace;
 
+        LayoutlibCallback layoutlibCallback = context.getLayoutlibCallback();
         // Framework values never need a PSI parser. They do not change and the do not contain
         // aapt:attr attributes.
         if (!value.isFramework()) {
-            parser = context.getLayoutlibCallback().getParser(value);
+            parser = layoutlibCallback.getParser(value);
         }
 
-        if (parser == null) {
-            File xmlFile = new File(stringValue);
-            if (xmlFile.isFile()) {
-                parser = ParserFactory.create(xmlFile);
-            }
+        if (parser != null) {
+            namespace = ((ILayoutPullParser) parser).getLayoutNamespace();
+        } else {
+            parser = ParserFactory.create(stringValue);
+            namespace = value.getNamespace();
         }
 
-        return new BridgeXmlBlockParser(parser, context, value.isFramework());
+        return parser == null
+                ? null
+                : new BridgeXmlBlockParser(parser, context, namespace);
     }
 
     /**
      * Returns a drawable from the given value.
+     *
      * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
-     * or an hexadecimal color
+     *     or an hexadecimal color
      * @param context the current context
      * @param theme the theme to be used to inflate the drawable.
      */
+    @Nullable
     public static Drawable getDrawable(ResourceValue value, BridgeContext context, Theme theme) {
         if (value == null) {
             return null;
@@ -314,27 +328,35 @@
         }
 
         if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
-            File file = new File(stringValue);
-            if (file.isFile()) {
-                try {
-                    return getNinePatchDrawable(new FileInputStream(file), density,
-                            value.isFramework(), stringValue, context);
-                } catch (IOException e) {
-                    // failed to read the file, we'll return null below.
-                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                            "Failed lot load " + file.getAbsolutePath(), e, null /*data*/);
-                }
+            try {
+                return getNinePatchDrawable(density, value.isFramework(), stringValue, context);
+            } catch (IOException e) {
+                // failed to read the file, we'll return null below.
+                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                        "Failed to load " + stringValue, e, null /*data*/);
             }
 
             return null;
-        } else if (lowerCaseValue.endsWith(".xml") || stringValue.startsWith("@aapt:_aapt/")) {
+        } else if (lowerCaseValue.endsWith(".xml") ||
+                value.getResourceType() == ResourceType.AAPT) {
             // create a block parser for the file
             try {
                 BridgeXmlBlockParser blockParser = getXmlBlockParser(context, value);
                 if (blockParser != null) {
+                    Set<ResourceValue> visitedValues = context.getUserData(KEY_GET_DRAWABLE);
+                    if (visitedValues == null) {
+                        visitedValues = new HashSet<>();
+                        context.putUserData(KEY_GET_DRAWABLE, visitedValues);
+                    }
+                    if (!visitedValues.add(value)) {
+                        Bridge.getLog().error(null, "Cyclic dependency in " + stringValue, null);
+                        return null;
+                    }
+
                     try {
                         return Drawable.createFromXml(context.getResources(), blockParser, theme);
                     } finally {
+                        visitedValues.remove(value);
                         blockParser.ensurePopped();
                     }
                 }
@@ -347,15 +369,22 @@
 
             return null;
         } else {
-            File bmpFile = new File(stringValue);
-            if (bmpFile.isFile()) {
+            AssetRepository repository = getAssetRepository(context);
+            if (repository.isFileResource(stringValue)) {
                 try {
                     Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
                             value.isFramework() ? null : context.getProjectKey());
 
                     if (bitmap == null) {
+                        InputStream stream;
+                        try {
+                            stream = repository.openNonAsset(0, stringValue, ACCESS_STREAMING);
+
+                        } catch (FileNotFoundException e) {
+                            stream = null;
+                        }
                         bitmap =
-                                Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/, density);
+                                Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
                         Bridge.setCachedBitmap(stringValue, bitmap,
                                 value.isFramework() ? null : context.getProjectKey());
                     }
@@ -364,7 +393,7 @@
                 } catch (IOException e) {
                     // we'll return null below
                     Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                            "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/);
+                            "Failed to load " + stringValue, e, null /*data*/);
                 }
             }
         }
@@ -372,6 +401,11 @@
         return null;
     }
 
+    private static AssetRepository getAssetRepository(@NonNull BridgeContext context) {
+        BridgeAssetManager assetManager = context.getAssets();
+        return assetManager.getAssetRepository();
+    }
+
     /**
      * Returns a {@link Typeface} given a font name. The font name, can be a system font family
      * (like sans-serif) or a full path if the font is to be loaded from resources.
@@ -404,24 +438,29 @@
         return getFont(value.getValue(), context, theme, value.isFramework());
     }
 
-    private static Drawable getNinePatchDrawable(InputStream inputStream, Density density,
-            boolean isFramework, String cacheKey, BridgeContext context) throws IOException {
+    private static Drawable getNinePatchDrawable(Density density, boolean isFramework,
+            String path, BridgeContext context) throws IOException {
         // see if we still have both the chunk and the bitmap in the caches
-        NinePatchChunk chunk = Bridge.getCached9Patch(cacheKey,
+        NinePatchChunk chunk = Bridge.getCached9Patch(path,
                 isFramework ? null : context.getProjectKey());
-        Bitmap bitmap = Bridge.getCachedBitmap(cacheKey,
+        Bitmap bitmap = Bridge.getCachedBitmap(path,
                 isFramework ? null : context.getProjectKey());
 
         // if either chunk or bitmap is null, then we reload the 9-patch file.
         if (chunk == null || bitmap == null) {
             try {
-                NinePatch ninePatch = NinePatch.load(inputStream, true /*is9Patch*/,
+                AssetRepository repository = getAssetRepository(context);
+                if (!repository.isFileResource(path)) {
+                    return null;
+                }
+                InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING);
+                NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/,
                         false /* convert */);
                 if (ninePatch != null) {
                     if (chunk == null) {
                         chunk = ninePatch.getChunk();
 
-                        Bridge.setCached9Patch(cacheKey, chunk,
+                        Bridge.setCached9Patch(path, chunk,
                                 isFramework ? null : context.getProjectKey());
                     }
 
@@ -430,7 +469,7 @@
                                 false /*isMutable*/,
                                 density);
 
-                        Bridge.setCachedBitmap(cacheKey, bitmap,
+                        Bridge.setCachedBitmap(path, bitmap,
                                 isFramework ? null : context.getProjectKey());
                     }
                 }
@@ -455,14 +494,13 @@
      * Looks for an attribute in the current theme.
      *
      * @param resources the render resources
-     * @param name the name of the attribute
+     * @param attr the attribute reference
      * @param defaultValue the default value.
-     * @param isFrameworkAttr if the attribute is in android namespace
      * @return the value of the attribute or the default one if not found.
      */
-    public static boolean getBooleanThemeValue(@NonNull RenderResources resources, String name,
-            boolean isFrameworkAttr, boolean defaultValue) {
-        ResourceValue value = resources.findItemInTheme(name, isFrameworkAttr);
+    public static boolean getBooleanThemeValue(@NonNull RenderResources resources,
+            @NonNull ResourceReference attr, boolean defaultValue) {
+        ResourceValue value = resources.findItemInTheme(attr);
         value = resources.resolveResValue(value);
         if (value == null) {
             return defaultValue;
@@ -470,6 +508,20 @@
         return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
     }
 
+    /**
+     * Looks for a framework attribute in the current theme.
+     *
+     * @param resources the render resources
+     * @param name the name of the attribute
+     * @param defaultValue the default value.
+     * @return the value of the attribute or the default one if not found.
+     */
+    public static boolean getBooleanThemeFrameworkAttrValue(@NonNull RenderResources resources,
+            @NonNull String name, boolean defaultValue) {
+        ResourceReference attrRef = BridgeContext.createFrameworkAttrReference(name);
+        return getBooleanThemeValue(resources, attrRef, defaultValue);
+    }
+
     // ------- TypedValue stuff
     // This is taken from //device/libs/utils/ResourceTypes.cpp
 
@@ -487,7 +539,7 @@
         }
     }
 
-    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
+    private static final UnitEntry[] sUnitNames = new UnitEntry[] {
         new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
         new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
         new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/SysUiOverlay.java b/bridge/src/com/android/layoutlib/bridge/impl/SysUiOverlay.java
new file mode 100644
index 0000000..8900c06
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/impl/SysUiOverlay.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+
+class SysUiOverlay extends View {
+    private final Path mCornerPath;
+    private final Path mNotchPath;
+    private final Paint mCornerPaint;
+    private final Paint mNotchPaint;
+    private final int mNotchTopWidth;
+
+
+    private static Path createCornerPath(float width, float height, float r) {
+        Path corner = new Path();
+        corner.moveTo(0, 0);
+        corner.lineTo(width, 0);
+        corner.cubicTo(width, 0, width /2 - r, height /2 - r, 0, height);
+        corner.close();
+
+        return corner;
+    }
+
+    private static Path createNotchPath(float topWidth, float bottomWidth, float height, float r) {
+        Path corner = new Path();
+        corner.moveTo(0, 0);
+        float widthDiff = topWidth - bottomWidth;
+        corner.lineTo(topWidth, 0);
+        corner.lineTo(topWidth - widthDiff/2, height);
+        corner.lineTo(widthDiff/2, height);
+        corner.close();
+
+        return corner;
+    }
+
+
+    private static float dpToPx(DisplayMetrics metrics, float value) {
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, value, metrics);
+    }
+
+
+    public SysUiOverlay(Context context, int cornerSizeDp, int radiusDp, int topNotchWidthDp, int bottomNotchWidthDp, int notchHeightDp) {
+        super(context);
+
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+
+        float cornerSizePx = dpToPx(metrics, cornerSizeDp);
+        float radiusPx = dpToPx(metrics, radiusDp);
+
+        float topNotchWidthPx = dpToPx(metrics, topNotchWidthDp);
+        float bottomNotchWidthPx = dpToPx(metrics, bottomNotchWidthDp);
+        float notchHeightPx = dpToPx(metrics, notchHeightDp);
+
+        mCornerPath = createCornerPath(cornerSizePx, cornerSizePx, radiusPx);
+        mNotchPath = createNotchPath(topNotchWidthPx, bottomNotchWidthPx, notchHeightPx, 0);
+        mNotchTopWidth = topNotchWidthDp;
+
+        mCornerPaint = new Paint();
+        mCornerPaint.setColor(Color.BLACK);
+        mCornerPaint.setStrokeWidth(0);
+        mCornerPaint.setAntiAlias(true);
+
+        mNotchPaint = new Paint();
+        mNotchPaint.setColor(Color.BLACK);
+        mNotchPaint.setStrokeWidth(0);
+        mNotchPaint.setAntiAlias(true);
+    }
+
+    public void setCornerColor(int color) {
+        mCornerPaint.setColor(color);
+    }
+
+    public void setNotchColor(int color) {
+        mNotchPaint.setColor(color);
+    }
+
+    private void paintRoundedBorders(Canvas canvas) {
+        // Top left
+        canvas.drawPath(mCornerPath, mCornerPaint);
+        // Top right
+        canvas.save();
+        canvas.translate(getWidth(), 0);
+        canvas.rotate(90);
+        canvas.drawPath(mCornerPath, mCornerPaint);
+        canvas.restore();
+        // Bottom right
+        canvas.save();
+        canvas.translate( getWidth(), getHeight());
+        canvas.rotate(180);
+        canvas.drawPath(mCornerPath, mCornerPaint);
+        canvas.restore();
+        // Bottom left
+        canvas.save();
+        canvas.translate( 0, getHeight());
+        canvas.rotate(270);
+        canvas.drawPath(mCornerPath, mCornerPaint);
+        canvas.restore();
+    }
+
+    private void paintNotch(Canvas canvas) {
+        canvas.translate(getWidth() / 2 - mNotchTopWidth / 2, 0);
+        canvas.drawPath(mNotchPath, mNotchPaint);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        paintRoundedBorders(canvas);
+        paintNotch(canvas);
+
+    }
+}
\ No newline at end of file
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
index c0ca1d5..f830b07 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -19,8 +19,8 @@
 import com.android.ide.common.rendering.api.DataBindingItem;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback.ViewAttribute;
 import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.impl.RenderAction;
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java b/bridge/src/com/android/layoutlib/bridge/resources/IconLoader.java
similarity index 93%
rename from bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java
rename to bridge/src/com/android/layoutlib/bridge/resources/IconLoader.java
index 9ab2e82..df4f252 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java
+++ b/bridge/src/com/android/layoutlib/bridge/resources/IconLoader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.layoutlib.bridge.bars;
+package com.android.layoutlib.bridge.resources;
 
+import com.android.layoutlib.bridge.bars.Config;
 import com.android.resources.Density;
 import com.android.resources.LayoutDirection;
 
@@ -31,7 +32,8 @@
     private Density mCurrentDensity;
     private StringBuilder mCurrentPath;
 
-    IconLoader(String iconName, Density density, int platformVersion, LayoutDirection direction) {
+    public IconLoader(String iconName, Density density, int platformVersion, LayoutDirection
+            direction) {
         mIconName = iconName;
         mDesiredDensity = density;
         mPlatformVersion = platformVersion;
diff --git a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
new file mode 100644
index 0000000..96a6e34
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.resources;
+
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.bars.Config;
+import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.resources.Density;
+import com.android.resources.LayoutDirection;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.drawable.BitmapDrawable;
+import android.widget.ImageView;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class SysUiResources {
+    private static final ResourceNamespace PRIVATE_LAYOUTLIB_NAMESPACE =
+            ResourceNamespace.fromPackageName("com.android.layoutlib");
+
+    @NotNull
+    public static BridgeXmlBlockParser loadXml(BridgeContext context, int apiLevel, String
+            layoutName) {
+        for (String resourceRepository : Config.getResourceDirs(apiLevel)) {
+            String path = resourceRepository + layoutName;
+            InputStream stream = SysUiResources.class.getResourceAsStream(path);
+            if (stream != null) {
+                try {
+                    XmlPullParser parser = ParserFactory.create(stream, layoutName);
+
+                    // TODO(namespaces): does the namespace matter here?
+                    return new BridgeXmlBlockParser(parser, context, PRIVATE_LAYOUTLIB_NAMESPACE);
+                } catch (XmlPullParserException e) {
+                    // Should not happen as the resource is bundled with the jar, and  ParserFactory should
+                    // have been initialized.
+                    assert false;
+                }
+            }
+        }
+
+        assert false;
+        return null;
+    }
+
+    public static ImageView loadIcon(Context context, int api, ImageView imageView, String
+            iconName,
+            Density density, boolean
+            isRtl) {
+        LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
+        IconLoader iconLoader = new IconLoader(iconName, density, api,
+                dir);
+        InputStream stream = iconLoader.getIcon();
+
+        if (stream != null) {
+            density = iconLoader.getDensity();
+            String path = iconLoader.getPath();
+            // look for a cached bitmap
+            Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
+            if (bitmap == null) {
+                try {
+                    bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
+                    Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
+                } catch (IOException e) {
+                    return imageView;
+                }
+            }
+
+            if (bitmap != null) {
+                BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);
+                imageView.setImageDrawable(drawable);
+            }
+        }
+
+        return imageView;
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
index f149b6c..75e4a2b 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -16,9 +16,13 @@
 
 package com.android.layoutlib.bridge.util;
 
+import com.android.tools.layoutlib.annotations.NotNull;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 
 /**
  * Simpler wrapper around FileInputStream. This is used when the input stream represent
@@ -26,17 +30,22 @@
  * This is useful when the InputStream is created in a method but used in another that needs
  * to know whether this is 9-patch or not, such as BitmapFactory.
  */
-public class NinePatchInputStream extends FileInputStream {
+public class NinePatchInputStream extends InputStream {
+    private final InputStream mDelegate;
     private boolean mFakeMarkSupport = true;
+
     public NinePatchInputStream(File file) throws FileNotFoundException {
-        super(file);
+        mDelegate = new FileInputStream(file);
+    }
+
+    public NinePatchInputStream(@NotNull InputStream stream) {
+        mDelegate = stream;
     }
 
     @Override
     public boolean markSupported() {
         // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
-        return mFakeMarkSupport || super.markSupported();
-
+        return mFakeMarkSupport || mDelegate.markSupported();
     }
 
     public void disableFakeMarkSupport() {
@@ -44,4 +53,44 @@
         // we don't lie to them.
         mFakeMarkSupport = false;
     }
+
+    @Override
+    public int read() throws IOException {
+        return mDelegate.read();
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return mDelegate.read(b);
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        return mDelegate.read(b, off, len);
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        return mDelegate.skip(n);
+    }
+
+    @Override
+    public int available() throws IOException {
+        return mDelegate.available();
+    }
+
+    @Override
+    public void close() throws IOException {
+        mDelegate.close();
+    }
+
+    @Override
+    public void mark(int readlimit) {
+        mDelegate.mark(readlimit);
+    }
+
+    @Override
+    public void reset() throws IOException {
+        mDelegate.reset();
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
index 64d100a..00348ea 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -77,21 +77,30 @@
     }
 
     /**
-     * Check if the object is an instance of any of the class named in {@code className}. This
-     * doesn't work for interfaces.
+     * Check if the object is an instance of a class named {@code className}. This doesn't work
+     * for interfaces.
      */
     public static boolean isInstanceOf(Object object, String[] classNames) {
+        return getParentClass(object, classNames) != null;
+    }
+
+    /**
+     * Check if the object is an instance of any of the class named in {@code className} and
+     * returns the name of the parent class that matched. This doesn't work for interfaces.
+     */
+    @Nullable
+    public static String getParentClass(Object object, String[] classNames) {
         Class superClass = object.getClass();
         while (superClass != null) {
             String name = superClass.getName();
             for (String className : classNames) {
                 if (name.equals(className)) {
-                    return true;
+                    return className;
                 }
             }
             superClass = superClass.getSuperclass();
         }
-        return false;
+        return null;
     }
 
     @NonNull
diff --git a/bridge/src/dalvik/system/VMRuntime_Delegate.java b/bridge/src/dalvik/system/VMRuntime_Delegate.java
index 36efc3a..648d2be 100644
--- a/bridge/src/dalvik/system/VMRuntime_Delegate.java
+++ b/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -75,4 +75,10 @@
         }
     }
 
+    @LayoutlibDelegate
+    /*package*/ static int getNotifyNativeInterval() {
+        // This cannot return 0, otherwise it is responsible for triggering an exception
+        // whenever trying to use a NativeAllocationRegistry with size 0
+        return 1;
+    }
 }
diff --git a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index 04fabc2..c739c65 100644
--- a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -19,6 +19,10 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import libcore.util.NativeAllocationRegistry.CleanerRunner;
+import libcore.util.NativeAllocationRegistry.CleanerThunk;
+import sun.misc.Cleaner;
+
 /**
  * Delegate implementing the native methods of {@link NativeAllocationRegistry}
  *
@@ -51,6 +55,44 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static void registerNativeAllocation(long size) {
+        NativeAllocationRegistry.registerNativeAllocation_Original(size);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Runnable registerNativeAllocation(NativeAllocationRegistry registry,
+            Object referent,
+            long nativePtr) {
+        // Mark the object as already "natively" tracked.
+        // This allows the DelegateManager to dispose objects without waiting
+        // for an explicit call when the referent does not exist anymore.
+        sManager.markAsNativeAllocation(referent, nativePtr);
+        if (referent == null) {
+            throw new IllegalArgumentException("referent is null");
+        }
+        if (nativePtr == 0) {
+            throw new IllegalArgumentException("nativePtr is null");
+        }
+
+        CleanerThunk thunk;
+        CleanerRunner result;
+        try {
+            thunk = registry.new CleanerThunk();
+            Cleaner cleaner = Cleaner.create(referent, thunk);
+            result = new CleanerRunner(cleaner);
+            registerNativeAllocation(registry.size);
+        } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
+            applyFreeFunction(registry.freeFunction, nativePtr);
+            throw vme;
+        } // Other exceptions are impossible.
+        // Enable the cleaner only after we can no longer throw anything, including OOME.
+        thunk.setNativePtr(nativePtr);
+        // Needs to call Reference.reachabilityFence(referent) to ensure that cleaner doesn't
+        // get invoked before we enable it. Unfortunately impossible in OpenJDK 8.
+        return result;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) {
         // This method MIGHT run in the context of the finalizer thread. If the delegate method
         // crashes, it could bring down the VM. That's why we catch all the exceptions and ignore
diff --git a/bridge/tests/.classpath b/bridge/tests/.classpath
deleted file mode 100644
index 2b32e09..0000000
--- a/bridge/tests/.classpath
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="res"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/layoutlib_bridge"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/bridge/tests/.project b/bridge/tests/.project
deleted file mode 100644
index 2325eed..0000000
--- a/bridge/tests/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>layoutlib_bridge-tests</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/bridge/tests/res/testApp/MyApplication/.gitignore b/bridge/tests/res/testApp/MyApplication/.gitignore
index a2ce0dc..135b1bc 100644
--- a/bridge/tests/res/testApp/MyApplication/.gitignore
+++ b/bridge/tests/res/testApp/MyApplication/.gitignore
@@ -11,4 +11,4 @@
 !/build/intermediates/
 
 /build/intermediates/*
-!/build/intermediates/classes/
+!/build/intermediates/javac/
diff --git a/bridge/tests/res/testApp/MyApplication/build.gradle b/bridge/tests/res/testApp/MyApplication/build.gradle
index c9058b5..0c6deb4 100644
--- a/bridge/tests/res/testApp/MyApplication/build.gradle
+++ b/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -1,9 +1,10 @@
 buildscript {
     repositories {
         jcenter()
+        google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.0.0-beta5'
+        classpath 'com.android.tools.build:gradle:3.2.0'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -13,18 +14,18 @@
 allprojects {
     repositories {
         jcenter()
+        google()
     }
 }
 
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 26
-    buildToolsVersion '26.0.2'
+    compileSdkVersion 28
     defaultConfig {
         applicationId 'com.android.layoutlib.test.myapplication'
-        minSdkVersion 21
-        targetSdkVersion 26
+        minSdkVersion 24
+        targetSdkVersion 28
         versionCode 1
         versionName '1.0'
     }
@@ -44,5 +45,5 @@
 }
 
 dependencies {
-    compile fileTree(dir: 'libs', include: ['*.jar'])
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
 }
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class
deleted file mode 100644
index 1ca7e01..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/R.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/R.class
deleted file mode 100644
index a734a21..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/R.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$array.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$array.class
deleted file mode 100644
index d279a3e..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$array.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$color.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$color.class
deleted file mode 100644
index a73fcca..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$color.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
deleted file mode 100644
index 93c5977..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
deleted file mode 100644
index e580ef5..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
deleted file mode 100644
index 9bdc44f..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
deleted file mode 100644
index 11e0686..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
deleted file mode 100644
index e91c774..0000000
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
similarity index 98%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
index ea2e30d..af7d768 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/BuildConfig.class
similarity index 100%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/BuildConfig.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/DrawableArrayWidget.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/DrawableArrayWidget.class
new file mode 100644
index 0000000..db12785
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/DrawableArrayWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/MyActivity.class
similarity index 96%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/MyActivity.class
index be984f0..3c4d1b5 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$anim.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$anim.class
new file mode 100644
index 0000000..8cf720c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$anim.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$array.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$array.class
new file mode 100644
index 0000000..a4660d3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$array.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$attr.class
similarity index 71%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$attr.class
index 2b98809..4710082 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$attr.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$color.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$color.class
new file mode 100644
index 0000000..4e9a632
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$color.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$dimen.class
similarity index 63%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$dimen.class
index 6f6bd54..f354028 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$dimen.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
new file mode 100644
index 0000000..5ac9e72
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$font.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
similarity index 63%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$font.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
index a3e9926..6fe46c5 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$font.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
new file mode 100644
index 0000000..15e3e52
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
similarity index 73%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
index 4f1a852..64df0c3 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
new file mode 100644
index 0000000..c0bb96d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
new file mode 100644
index 0000000..afeb357
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
similarity index 64%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
index 331ea4a..c8d5e0b 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
similarity index 61%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
index 3b47ac9..ccfecc8 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R.class
new file mode 100644
index 0000000..4538161
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ThemableWidget.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
similarity index 98%
rename from bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ThemableWidget.class
rename to bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
index 8d96ac1..5171dad 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ThemableWidget.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/activity.png b/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 773c5be..1750fa8 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index 4391f47..0638f32 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
index bca3347..8869159 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
index 0835d51..28000f1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/array_check.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
new file mode 100644
index 0000000..a053e8e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/canvas.png b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
new file mode 100644
index 0000000..e3098ab
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png b/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
new file mode 100644
index 0000000..16a457d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/font_test.png b/bridge/tests/res/testApp/MyApplication/golden/font_test.png
index 7e0c29a..a8a4c90 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/font_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/font_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
index ffba2b5..2be5248 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
index b73b359..f9e728a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
index 09fd279..4d5a38c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
new file mode 100644
index 0000000..794b546
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png b/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
new file mode 100644
index 0000000..de3abe6
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
new file mode 100644
index 0000000..0e2848e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png b/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
new file mode 100644
index 0000000..5800b24
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
index 62fb869..b200928 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png
new file mode 100644
index 0000000..d219a1b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
index 8212d04..6331256 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
new file mode 100644
index 0000000..1639a1a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png
new file mode 100644
index 0000000..c5ba2c9
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
new file mode 100644
index 0000000..c21852c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
index b6f3737..8ef9d55 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
index fd6e6b6..0ff09d7 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
index ef20727..614c000 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/translate_test.png b/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
new file mode 100644
index 0000000..413fd73
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
new file mode 100644
index 0000000..372eccd
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vecitor_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden/vecitor_drawable_with_tint_in_image_view.png
new file mode 100644
index 0000000..a67ef73
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/vecitor_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
index eddb5e6..a81fa50 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
index e9dca69..56c35f9 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
index 8bb1677..ea3d2d9 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
index 2a3f3dd..ad4fed8 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
new file mode 100644
index 0000000..7e6814c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
new file mode 100644
index 0000000..8866a3d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
index bf358fa..dbfd73a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/view_stub.png b/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
new file mode 100644
index 0000000..00f0246
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties b/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
index 565238f..5048215 100644
--- a/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
+++ b/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Mar 17 15:13:06 PDT 2015
+#Fri Sep 28 14:21:00 BST 2018
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/DrawableArrayWidget.java b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/DrawableArrayWidget.java
new file mode 100644
index 0000000..c87f605
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/DrawableArrayWidget.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.test.myapplication;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+public class DrawableArrayWidget extends LinearLayout {
+    public DrawableArrayWidget(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DrawableArrayWidget(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DrawableArrayWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        TypedArray drawableArray = context.getResources().obtainTypedArray(R.array.drawable_array);
+        for (int i = 0; i < drawableArray.length(); i++) {
+            addImageView(context, drawableArray.getDrawable(i));
+        }
+        drawableArray.recycle();
+    }
+
+    private void addImageView(Context context, Drawable drawable) {
+        ImageView imageView = new ImageView(context);
+        imageView.setImageDrawable(drawable);
+        addView(imageView);
+    }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CanvasTestView.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CanvasTestView.java
new file mode 100644
index 0000000..8cf166d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/CanvasTestView.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.test.myapplication.widgets;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class CanvasTestView extends ImageView {
+    public CanvasTestView(Context context) {
+        super(context);
+    }
+
+    public CanvasTestView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CanvasTestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int savedLayer = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null);
+        super.onDraw(canvas);
+        canvas.restoreToCount(savedLayer);
+    }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/anim/color_interpolation.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/anim/color_interpolation.xml
new file mode 100644
index 0000000..4d008e6
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/anim/color_interpolation.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<objectAnimator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="450"
+    android:interpolator="@android:anim/linear_interpolator"
+    android:propertyName="strokeColor"
+    android:startOffset="50"
+    android:valueFrom="#121f21"
+    android:valueTo="#dadce0"
+    android:valueType="colorType" />
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/color/multiple_alpha_items_gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/color/multiple_alpha_items_gradient.xml
new file mode 100644
index 0000000..e0f1d35
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/color/multiple_alpha_items_gradient.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<gradient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:startX="0"
+    android:endX="117"
+    android:type="linear">
+    <item
+        android:color="#33000000"
+        android:offset="0.3" />
+    <item
+        android:color="#ddff00ff"
+        android:offset="0.6" />
+    <item
+        android:color="#55ff0000"
+        android:offset="0.1" />
+    <item
+        android:color="#ee00ffff"
+        android:offset="0.8" />
+</gradient>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/color/multiple_offset_items_gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/color/multiple_offset_items_gradient.xml
new file mode 100644
index 0000000..c6ec363
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/color/multiple_offset_items_gradient.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<gradient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:startX="117"
+    android:endX="234"
+    android:type="linear">
+    <item
+        android:color="#000000"
+        android:offset="-0.3" />
+    <item
+        android:color="#ff00ff"
+        android:offset="0.6" />
+    <item
+        android:color="#ee00ffff"
+        android:offset="1.8" />
+</gradient>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/avd_color_interpolator.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/avd_color_interpolator.xml
new file mode 100644
index 0000000..81b3601
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/avd_color_interpolator.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/headset">
+
+    <target
+        android:name="outline"
+        android:animation="@anim/color_interpolation" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/clipped_even_odd.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/clipped_even_odd.xml
new file mode 100644
index 0000000..2e4a87b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/clipped_even_odd.xml
@@ -0,0 +1,37 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="150dp"
+        android:height="150dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <clip-path
+        android:pathData="M 4.8 12 C 4.8 8.024 8.024 4.8 12 4.8 C 15.976 4.8 19.2 8.024 19.2 12
+            C 19.2 15.976 15.976 19.2 12 19.2 C 8.024 19.2 4.8 15.976 4.8 12 M 9.6 12
+            C 9.6 10.675 10.675 9.6 12 9.6 C 13.325 9.6 14.4 10.675 14.4 12
+            C 14.4 13.325 13.325 14.4 12 14.4 C 10.675 14.4 9.6 13.325 9.6 12"
+    />
+
+    <path
+        android:pathData="M 4.8 12 C 4.8 8.024 8.024 4.8 12 4.8 C 15.976 4.8 19.2 8.024 19.2 12
+            C 19.2 15.976 15.976 19.2 12 19.2 C 8.024 19.2 4.8 15.976 4.8 12"
+        android:fillColor="#FFFF0000"
+        android:fillType="evenOdd"
+    />
+
+</vector>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/corner.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/corner.xml
new file mode 100644
index 0000000..d74a750
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/corner.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="#f0f" />
+    <corners android:topLeftRadius="4dp" />
+</shape>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/gradient.xml
new file mode 100644
index 0000000..40511c4
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/gradient.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="234.0dp"
+        android:height="210.0dp"
+        android:viewportWidth="234.0"
+        android:viewportHeight="210.0">
+    <path
+        android:pathData="M117,183 C114.95,183 112.91,182.22 111.34,180.66 L38.48,107.53 C30.15,99.21 25,87.7 25,75 C25,49.59 45.59,29 71,29 C83.7,29 95.2,34.15 103.53,42.47 L117,55.83 C117,55.83 117,183 117,183 "
+        android:fillColor="@color/multiple_alpha_items_gradient">
+    </path>
+    <path
+        android:pathData="M117,183 C119.05,183 121.09,182.22 122.66,180.66 L195.52,107.53 C203.85,99.21 209,87.7 209,75 C209,49.59 188.41,29 163,29 C150.3,29 138.8,34.15 130.47,42.47 L117,55.83 C117,55.83 117,183 117,183 "
+        android:fillColor="@color/multiple_offset_items_gradient">
+    </path>
+</vector>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml
index 897c411..fc6692e 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml
@@ -20,6 +20,7 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
+        android:name="outline"
         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>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/rounded_edge_rect.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/rounded_edge_rect.xml
new file mode 100644
index 0000000..57f8062
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/rounded_edge_rect.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<!--<?xml version="1.0" encoding="UTF-8"?>-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#B1BCBE"/>
+    <stroke android:width="3dp" android:color="#B1BCBE" />
+    <corners android:radius="20dp"/>
+    <padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
+</shape>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_drawable_with_tint.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_drawable_with_tint.xml
new file mode 100644
index 0000000..3bdd704
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_drawable_with_tint.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector android:height="100dp" android:tint="#6A8BD2"
+        android:viewportHeight="24.0" android:viewportWidth="24.0"
+        android:width="100dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <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/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_drawable_without_tint.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_drawable_without_tint.xml
new file mode 100644
index 0000000..9efda52
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_drawable_without_tint.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector android:height="24dp" android:viewportHeight="300.0"
+        android:viewportWidth="300.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:fillType="evenOdd" android:pathData="M150.5,162.9c-5.4,0 -9.9,4.4 -9.9,9.8c0,5.4 4.4,9.8 9.9,9.8c5.5,0 9.9,-4.4 9.9,-9.8C160.3,167.3 155.9,162.9 150.5,162.9zM150.5,176c-1.8,0 -3.3,-1.5 -3.3,-3.3c0,-1.8 1.5,-3.3 3.3,-3.3s3.3,1.5 3.3,3.3C153.8,174.5 152.3,176 150.5,176zM136.6,103.3c-3.4,-3.5 -8.7,-5.3 -15.8,-5.3c-18.2,0 -23.1,17.2 -23.2,17.4v0l-13.3,48.4c1.5,-1.2 3.2,-2.2 4.9,-3.1l12.9,-47c0.5,-1.8 2.3,-2.8 4,-2.3c1.8,0.5 2.8,2.3 2.3,4l-11.6,42.5c2.4,-0.6 4.9,-0.8 7.5,-0.8c12.1,0 22.8,6.6 28.5,16.4l7.9,-59.7C140.8,112.2 140.3,107.2 136.6,103.3zM104.4,173.5c-9.1,0 -16.5,7.4 -16.5,16.4c0,9.1 7.4,16.4 16.5,16.4c9.1,0 16.5,-7.4 16.5,-16.4C120.8,180.8 113.5,173.5 104.4,173.5zM180.1,91.4c2.7,0 5.1,0.3 7.3,0.8l-0.8,-3.2c-0.3,-0.6 -2.2,-4.2 -9.9,-4.2c-3.3,0 -5.7,0.9 -7.2,2.8c-0.7,0.9 -1,1.8 -1.1,2.2l0.3,3.3C172.1,92 175.9,91.4 180.1,91.4zM159.6,159.1l-5.3,-40.6c-0.3,-0.3 -1.4,-0.8 -3.8,-0.8s-3.5,0.5 -3.8,0.8l-5.3,40.6c2.6,-1.7 5.7,-2.7 9.1,-2.7C153.8,156.3 157,157.3 159.6,159.1zM132.2,93.1l0.3,-3.3c-0.1,-0.5 -0.5,-1.5 -1.3,-2.4c-1.5,-1.7 -3.8,-2.6 -7,-2.6c-7.7,0 -9.6,3.6 -9.9,4.2l-0.8,3.2c2.2,-0.5 4.7,-0.8 7.3,-0.8C125.1,91.4 128.9,92 132.2,93.1zM104.4,163.6c-14.5,0 -26.4,11.8 -26.4,26.3c0,14.5 11.8,26.3 26.4,26.3c14.5,0 26.3,-11.8 26.3,-26.3C130.7,175.4 118.9,163.6 104.4,163.6zM104.4,212.9c-12.7,0 -23.1,-10.3 -23.1,-23s10.3,-23 23.1,-23c12.7,0 23.1,10.3 23.1,23S117.1,212.9 104.4,212.9zM196.6,173.5c-9.1,0 -16.5,7.4 -16.5,16.4c0,9.1 7.4,16.4 16.5,16.4c9.1,0 16.5,-7.4 16.5,-16.4C213.1,180.8 205.7,173.5 196.6,173.5zM196.6,163.6c-14.5,0 -26.4,11.8 -26.4,26.3c0,14.5 11.8,26.3 26.4,26.3c14.5,0 26.4,-11.8 26.4,-26.3C223,175.4 211.1,163.6 196.6,163.6zM196.6,212.9c-12.7,0 -23.1,-10.3 -23.1,-23s10.3,-23 23.1,-23c12.7,0 23.1,10.3 23.1,23S209.3,212.9 196.6,212.9zM150,4.5C69.6,4.5 4.5,69.6 4.5,150S69.6,295.5 150,295.5S295.5,230.4 295.5,150S230.4,4.5 150,4.5zM196.6,222.8c-18.1,0 -32.8,-14.6 -32.9,-32.6l-0.9,-6.6c-3,3.4 -7.4,5.6 -12.3,5.6c-4.9,0 -9.3,-2.1 -12.3,-5.5l-0.9,6.5c-0.1,18 -14.9,32.6 -32.9,32.6c-18.2,0 -32.9,-14.7 -32.9,-32.8c0,-4.3 0.8,-8.3 2.3,-12.1l17.6,-64.2c0.1,-0.2 1.6,-5.7 5.9,-11.1c2,-2.5 4.9,-5.3 8.8,-7.5l1.9,-8c0.1,-0.4 2.9,-8.9 16.2,-8.9c6.8,0 10.5,2.9 12.4,5.3c2.1,2.6 2.5,5.3 2.5,5.6l0,0.7l-0.5,6.5c1.1,0.8 2.1,1.6 3,2.6c4,4.2 5.3,9.3 5.7,12.5c1,-0.2 2,-0.3 3.3,-0.3s2.3,0.1 3.3,0.3c0.4,-3.2 1.7,-8.2 5.7,-12.5c0.9,-1 1.9,-1.8 3,-2.6l-0.5,-6.5c0,-0.2 0,-0.5 0,-0.7c0,-0.3 0.4,-3 2.5,-5.6c1.9,-2.4 5.6,-5.3 12.4,-5.3c13.3,0 16.1,8.5 16.2,8.9l0,0.2l1.9,7.8c3.9,2.2 6.8,4.9 8.8,7.5c4.3,5.4 5.9,10.9 5.9,11.1l17.6,64.2c1.5,3.7 2.3,7.8 2.3,12.1C229.5,208 214.8,222.8 196.6,222.8zM196.6,157.1c2.6,0 5.1,0.3 7.5,0.8l-11.6,-42.5c-0.5,-1.8 0.6,-3.6 2.3,-4c1.8,-0.5 3.6,0.6 4,2.3l12.9,47c1.7,0.9 3.4,1.9 4.9,3.1l-13.3,-48.4c0,0 -1.3,-4.5 -4.9,-8.9c-4.6,-5.6 -10.8,-8.5 -18.3,-8.5c-7.1,0 -12.4,1.8 -15.8,5.3c-3.7,3.8 -4.1,8.8 -4.2,10.4l7.9,59.7C173.8,163.6 184.5,157.1 196.6,157.1z"/>
+</vector>
+
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_gradient_alpha.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_gradient_alpha.xml
new file mode 100644
index 0000000..a4394ad
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/vector_gradient_alpha.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="108dp"
+        android:height="108dp"
+        android:viewportWidth="108"
+        android:viewportHeight="108">
+    <path
+        android:pathData="M0,0h108v108h-108z"
+        android:fillColor="#FFFFFF"/>
+    <path
+        android:pathData="M70.87,73.52c4.76,-4.44 7.79,-11.05 7.79,-19.52c0,-1.54 -0.14,-3.3 -0.49,-4.88H54v10.05h13.89c-0.7,3.51 -2.6,6.18 -5.32,7.91v1.29l6.78,5.14L70.87,73.52L70.87,73.52z"
+        android:fillColor="#4285F4"/>
+    <path
+        android:pathData="M39.37,58.81c2.01,6.14 7.77,10.56 14.59,10.56c3.33,0 6.27,-0.79 8.61,-2.28l8.3,6.44C66.34,77.75 60.29,80 53.96,80c-10.27,0 -19.09,-5.89 -23.31,-14.5l0.29,-1.76l7.04,-4.93H39.37z"
+        android:fillColor="#34A853"/>
+    <path
+        android:pathData="M39.38,49.16c-0.5,1.52 -0.78,3.14 -0.78,4.84c0,1.69 0.27,3.32 0.78,4.84l-8.72,6.68C28.95,62.05 28,58.14 28,54s0.96,-8.05 2.66,-11.52l1.73,-0.37l6.6,5.16L39.38,49.16z"
+        android:fillColor="#FBBC05"/>
+    <path
+        android:pathData="M53.96,38.64c3.66,0 6.97,1.3 9.56,3.43l7.56,-7.57C66.47,30.48 60.56,28 53.95,28c-10.27,0 -19.09,5.89 -23.3,14.49l8.73,6.68C41.39,43.04 47.14,38.64 53.96,38.64z"
+        android:fillColor="#EA4335"/>
+    <path
+        android:pathData="M0,0h108v108h-108z"
+        android:fillAlpha="0.3"
+        android:fillColor="@color/gradient"/>
+</vector>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/large_view_shadows_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/large_view_shadows_test.xml
new file mode 100644
index 0000000..2209e50
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/large_view_shadows_test.xml
@@ -0,0 +1,46 @@
+
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+        <Button
+            android:layout_width="100dp"
+            android:layout_height="200dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="28dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="5.5dp"
+            android:stateListAnimator="@null"
+            android:text="5.5"/>
+        <Button
+            android:layout_width="100dp"
+            android:layout_height="200dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginRight="61dp"
+            android:layout_alignParentRight="true"
+            android:elevation="36dp"
+            android:stateListAnimator="@null"
+            android:text="36"/>
+    </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadow_sizes_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadow_sizes_test.xml
new file mode 100644
index 0000000..5c53ca0
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadow_sizes_test.xml
@@ -0,0 +1,165 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+        <Button
+            android:layout_width="100dp"
+            android:layout_height="200dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="28dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="5.5dp"
+            android:stateListAnimator="@null"
+            android:text="5.5"/>
+        <Button
+            android:layout_width="100dp"
+            android:layout_height="200dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginRight="61dp"
+            android:layout_alignParentRight="true"
+            android:elevation="36dp"
+            android:stateListAnimator="@null"
+            android:text="36"/>
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+        <Button
+            android:layout_width="46dp"
+            android:layout_height="22dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="103dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="15dp"
+            android:stateListAnimator="@null"
+            android:text="15"/>
+        <Button
+            android:id="@+id/button14"
+            android:layout_width="23dp"
+            android:layout_height="21dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="54dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="15dp"
+            android:stateListAnimator="@null"
+            android:text="15"/>
+        <Button
+            android:id="@+id/button12"
+            android:layout_width="76dp"
+            android:layout_height="20dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="176dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="15dp"
+            android:stateListAnimator="@null"
+            android:text="15"/>
+        <Button
+            android:id="@+id/button11"
+            android:layout_width="15dp"
+            android:layout_height="23dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="28dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="15dp"
+            android:stateListAnimator="@null"
+            android:text="15"/>
+        <Button
+            android:layout_width="39dp"
+            android:layout_height="21dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginStart="278dp"
+            android:layout_marginLeft="278dp"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
+            android:elevation="5dp"
+            android:stateListAnimator="@null"
+            android:text="5"/>
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+        <Button
+            android:layout_width="20dp"
+            android:layout_height="36dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="136dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="5.5dp"
+            android:stateListAnimator="@null"
+            android:text="5.5"/>
+        <Button
+            android:id="@+id/button17"
+            android:layout_width="22dp"
+            android:layout_height="85dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="77dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="5.5dp"
+            android:stateListAnimator="@null"
+            android:text="5.5"/>
+        <Button
+            android:id="@+id/button16"
+            android:layout_width="23dp"
+            android:layout_height="131dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginLeft="28dp"
+            android:layout_alignParentLeft="true"
+            android:elevation="5.5dp"
+            android:stateListAnimator="@null"
+            android:text="5.5"/>
+        <Button
+            android:layout_width="33dp"
+            android:layout_height="115dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginRight="155dp"
+            android:layout_alignParentRight="true"
+            android:elevation="36dp"
+            android:stateListAnimator="@null"
+            android:text="36"/>
+        <Button
+            android:id="@+id/button20"
+            android:layout_width="29dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="28dp"
+            android:layout_marginRight="92dp"
+            android:layout_alignParentRight="true"
+            android:elevation="36dp"
+            android:stateListAnimator="@null"
+            android:text="36"/>
+        <Button
+            android:id="@+id/button18"
+            android:layout_width="14dp"
+            android:layout_height="23dp"
+            android:layout_marginTop="28dp"
+            android:layout_marginRight="50dp"
+            android:layout_alignParentRight="true"
+            android:elevation="36dp"
+            android:stateListAnimator="@null"
+            android:text="36"/>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_rounded_edge_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_rounded_edge_test.xml
new file mode 100644
index 0000000..943e103
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_rounded_edge_test.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="match_parent"
+              android:layout_height="match_parent" >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="48dp"
+            android:stateListAnimator="@null"
+            android:background="@drawable/rounded_edge_rect"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="48dp"
+            android:background="@drawable/rounded_edge_rect"
+            android:stateListAnimator="@null"/>
+
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="0dp"
+            android:background="@drawable/rounded_edge_rect"
+            android:stateListAnimator="@null"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="100dp"
+            android:background="@drawable/rounded_edge_rect"
+            android:stateListAnimator="@null"/>
+
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="40dp"
+            android:layout_height="48dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="12dp"
+            android:background="@drawable/rounded_edge_rect"
+            android:stateListAnimator="@null"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="40dp"
+            android:layout_height="48dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="36dp"
+            android:background="@drawable/rounded_edge_rect"
+            android:stateListAnimator="@null"/>
+
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="12dp"
+            android:stateListAnimator="@null"
+            android:background="@drawable/rounded_edge_rect"
+            android:alpha="0.1"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="12dp"
+            android:stateListAnimator="@null"
+            android:background="@drawable/rounded_edge_rect"
+            android:alpha="0.5"/>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
index c083a6f..498a63f 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
@@ -32,6 +32,7 @@
             android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
             android:elevation="48dp"
+            android:text="48"
             android:stateListAnimator="@null"/>
 
         <Button
@@ -41,6 +42,7 @@
             android:layout_alignParentRight="true"
             android:layout_centerVertical="true"
             android:elevation="48dp"
+            android:text="48"
             android:stateListAnimator="@null"/>
 
     </RelativeLayout>
@@ -57,6 +59,7 @@
             android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
             android:elevation="0dp"
+            android:text="0"
             android:stateListAnimator="@null"/>
 
         <Button
@@ -66,6 +69,7 @@
             android:layout_alignParentRight="true"
             android:layout_centerVertical="true"
             android:elevation="100dp"
+            android:text="100"
             android:stateListAnimator="@null"/>
 
     </RelativeLayout>
@@ -82,6 +86,7 @@
             android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
             android:elevation="12dp"
+            android:text="12"
             android:stateListAnimator="@null"/>
 
         <Button
@@ -91,6 +96,7 @@
             android:layout_alignParentRight="true"
             android:layout_centerVertical="true"
             android:elevation="36dp"
+            android:text="36"
             android:stateListAnimator="@null"/>
 
     </RelativeLayout>
@@ -107,6 +113,7 @@
             android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
             android:elevation="12dp"
+            android:text="12"
             android:stateListAnimator="@null"
             android:alpha="0.1"/>
 
@@ -117,6 +124,7 @@
             android:layout_alignParentRight="true"
             android:layout_centerVertical="true"
             android:elevation="12dp"
+            android:text="12"
             android:stateListAnimator="@null"
             android:alpha="0.5"/>
 
@@ -137,7 +145,22 @@
             android:layout_alignParentLeft="true"
             android:layout_centerVertical="true"
             android:elevation="12dp"
+            android:text="12"
+            android:alpha="5"
             android:stateListAnimator="@null" />
 
+        <Button
+            android:translationX="5dp"
+            android:translationY="20dp"
+            android:layout_marginRight="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="12dp"
+            android:text="12"
+            android:stateListAnimator="@null"
+            android:background="@drawable/corner" />
+
     </RelativeLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/translate_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/translate_test.xml
new file mode 100644
index 0000000..f42da40
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/translate_test.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/button2"
+        android:layout_width="100dp"
+        android:layout_height="50dp"
+        android:translationX="100px"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/typed_array.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/typed_array.xml
new file mode 100644
index 0000000..b0ca4c3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/typed_array.xml
@@ -0,0 +1,34 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/text1"
+        android:text="@string/hello_world"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:autoSizeTextType="uniform"
+        android:autoSizePresetSizes="@array/autosize_text_sizes" />
+
+    <TextView
+        android:id="@+id/text2"
+        android:text="@string/hello_world"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:autoSizeTextType="uniform"
+        android:autoSizePresetSizes="@array/autosize_text_sizes" />
+
+    <TextView
+        android:id="@+id/text3"
+        android:text="@string/hello_world"
+        android:layout_width="match_parent"
+        android:layout_height="20dp"
+        android:autoSizeTextType="uniform"
+        android:autoSizePresetSizes="@array/autosize_text_sizes" />
+
+    <com.android.layoutlib.test.myapplication.DrawableArrayWidget
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
index 1059d2e..3cf19da 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
@@ -19,8 +19,18 @@
         <item>@android:string/unknownName</item>  <!-- value = Unknown -->
         <item>\?EC</item>
     </string-array>
-
+    <array name="autosize_text_sizes">
+        <item>10sp</item>
+        <item>12sp</item>
+        <item>20sp</item>
+        <item>40sp</item>
+        <item>100sp</item>
+    </array>
+    <array name="drawable_array">
+	<item>@drawable/gradient</item>
+	<item>@drawable/headset</item>
+    </array>
     <!-- resources that the above array can refer to -->
     <integer name="ten">10</integer>
     <integer name="twelve">12</integer>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/bridge/tests/run_tests.sh b/bridge/tests/run_tests.sh
new file mode 100755
index 0000000..4d9cde2
--- /dev/null
+++ b/bridge/tests/run_tests.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+SCRIPT_DIR="$(dirname $0)"
+DIST_DIR="$1"
+
+STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/studio/jdk/linux"
+MISC_COMMON=${SCRIPT_DIR}"/../../../../prebuilts/misc/common"
+M2_REPO=${SCRIPT_DIR}"/../../../../prebuilts/tools/common/m2/repository"
+JAVA_LIBRARIES=${SCRIPT_DIR}"/../../../../out/host/common/obj/JAVA_LIBRARIES"
+
+${STUDIO_JDK}/bin/java -ea \
+    -Dtest_res.dir=${SCRIPT_DIR}/res \
+    -Dtest_failure.dir=${DIST_DIR}/layoutlib_failures \
+    -cp ${M2_REPO}/junit/junit/4.12/junit-4.12.jar:${M2_REPO}/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:${MISC_COMMON}/tools-common/tools-common-prebuilt.jar:${MISC_COMMON}/sdk-common/sdk-common.jar:${MISC_COMMON}/layoutlib_api/layoutlib_api-prebuilt.jar:${MISC_COMMON}/kxml2/kxml2-2.3.0.jar:${M2_REPO}/com/google/guava/guava/22.0/guava-22.0.jar:${JAVA_LIBRARIES}/layoutlib-tests_intermediates/javalib.jar:${JAVA_LIBRARIES}/layoutlib_intermediates/javalib.jar:${JAVA_LIBRARIES}/mockito-host_intermediates/javalib.jar:${JAVA_LIBRARIES}/objenesis-host_intermediates/javalib.jar \
+    org.junit.runner.JUnitCore \
+    com.android.layoutlib.bridge.intensive.Main
+
diff --git a/bridge/tests/src/android/graphics/Color_DelegateTest.java b/bridge/tests/src/android/graphics/Color_DelegateTest.java
new file mode 100644
index 0000000..1b9c13e
--- /dev/null
+++ b/bridge/tests/src/android/graphics/Color_DelegateTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import junit.framework.TestCase;
+
+public class Color_DelegateTest extends TestCase {
+
+    public void testRGBToHSV() {
+        float[] hsv = new float[3];
+        Color_Delegate.nativeRGBToHSV(14, 203, 49, hsv);
+        assertTrue(131.1111 - hsv[0] < 0.001);
+        assertTrue(0.9310 - hsv[1] < 0.001);
+        assertTrue(0.7961 - hsv[2] < 0.001);
+    }
+
+    public void testHSVToColor() {
+        assertEquals(2077003642,
+                Color_Delegate.nativeHSVToColor(123, new float[]{15.0f, 0.4f, 0.8f}));
+        assertEquals(603979776,
+                Color_Delegate.nativeHSVToColor(36, new float[]{15.0f, 25.0f, -17.0f}));
+    }
+}
diff --git a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java b/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
index aa51773..0886132 100644
--- a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
+++ b/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
@@ -52,13 +52,15 @@
         Matrix m1 = new Matrix();
         Matrix inverse = new Matrix();
         m1.setValues(new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
-        m1.invert(inverse);
+        assertFalse(m1.invert(inverse));
 
+        m1.setValues(new float[]{3, 5, 6, 2, 5, 7, 4, 8, 2});
+        m1.invert(inverse);
         float[] values = new float[9];
         inverse.getValues(values);
 
         assertTrue(Arrays.equals(values,
-                new float[]{-1.6666666f, 0.6666667f, 1.0f, 1.3333334f, -0.33333334f, -2.0f, 0.0f,
-                        0.0f, 1.0f}));
+                new float[]{1.0952381f, -0.9047619f, -0.11904762f, -0.5714286f, 0.42857143f,
+                        0.21428572f, 0.0952381f, 0.0952381f, -0.11904762f}));
     }
 }
diff --git a/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java b/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
index 2fcec8e..f995d1a 100644
--- a/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
+++ b/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
@@ -16,7 +16,10 @@
 
 package android.util;
 
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
 import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.BridgeContext;
 
@@ -31,6 +34,7 @@
 import static org.mockito.Mockito.when;
 
 public class BridgeXmlPullAttributesTest {
+
     @Test
     public void testGetAttributeIntValueForEnums() {
         RenderResources renderResources = new RenderResources();
@@ -41,18 +45,22 @@
         when(parser.getAttributeName(0)).thenReturn("layout_width");
         when(parser.getAttributeNamespace(0)).thenReturn(BridgeConstants.NS_RESOURCES);
         // Return every value twice since there is one test using name and other using index
-        when(parser.getAttributeValue("http://custom", "my_custom_attr"))
+        when(parser.getAttributeValue(BridgeConstants.NS_APP_RES_AUTO, "my_custom_attr"))
                 .thenReturn("a", "a", "b", "b", "invalid", "invalid");
         when(parser.getAttributeName(1)).thenReturn("my_custom_attr");
-        when(parser.getAttributeNamespace(1)).thenReturn("http://custom");
+        when(parser.getAttributeNamespace(1)).thenReturn(BridgeConstants.NS_APP_RES_AUTO);
 
         BridgeContext context = mock(BridgeContext.class);
         when(context.getRenderResources()).thenReturn(renderResources);
 
+        LayoutlibCallback callback = mock(LayoutlibCallback.class);
+        when(callback.getImplicitNamespaces()).thenReturn(Resolver.EMPTY_RESOLVER);
+        when(context.getLayoutlibCallback()).thenReturn(callback);
+
         BridgeXmlPullAttributes attributes = new BridgeXmlPullAttributes(
                 parser,
                 context,
-                false,
+                ResourceNamespace.RES_AUTO,
                 attrName -> {
                     if ("layout_width".equals(attrName)) {
                         return ImmutableMap.of(
@@ -60,7 +68,7 @@
                     }
                     return ImmutableMap.of();
                 },
-                attrName -> {
+                (ns, attrName) -> {
                     if ("my_custom_attr".equals(attrName)) {
                         return ImmutableMap.of(
                                 "a", 1,
@@ -80,16 +88,16 @@
         assertEquals(500, attributes.getAttributeIntValue(2, 500));
 
         // Test project defined enum attribute
-        assertEquals(1, attributes.getAttributeIntValue("http://custom",
+        assertEquals(1, attributes.getAttributeIntValue(BridgeConstants.NS_APP_RES_AUTO,
                 "my_custom_attr", 500));
         assertEquals(1, attributes.getAttributeIntValue(1, 500));
-        assertEquals(2, attributes.getAttributeIntValue("http://custom",
+        assertEquals(2, attributes.getAttributeIntValue(BridgeConstants.NS_APP_RES_AUTO,
                 "my_custom_attr", 500));
         assertEquals(2, attributes.getAttributeIntValue(1, 500));
         // Test an invalid enum
         boolean exception = false;
         try {
-            attributes.getAttributeIntValue("http://custom", "my_custom_attr", 500);
+            attributes.getAttributeIntValue(BridgeConstants.NS_APP_RES_AUTO, "my_custom_attr", 500);
         } catch(NumberFormatException e) {
             exception = true;
         }
@@ -103,8 +111,8 @@
         assertTrue(exception);
 
         // Test non existing project attribute
-        assertEquals(500, attributes.getAttributeIntValue("http://custom",
+        assertEquals(500, attributes.getAttributeIntValue(BridgeConstants.NS_APP_RES_AUTO,
                 "my_other_attr", 500));
     }
 
-}
\ No newline at end of file
+}
diff --git a/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java b/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java
new file mode 100644
index 0000000..25aa466
--- /dev/null
+++ b/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import org.junit.Test;
+
+import android.util.imagepool.Bucket.BucketCreationMetaData;
+import android.util.imagepool.ImagePool.Image.Orientation;
+
+import java.awt.image.BufferedImage;
+import java.lang.ref.SoftReference;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class ImagePoolHelperTest {
+
+    @Test
+    public void testGetBufferedImage() {
+        int width = 10;
+        int height = 10;
+        int numberOfCopiesInBucket = 10;
+        int maxCacheSize = width * height * 4 * 5; // can fit 5 width | height buffer
+        Bucket bucket = new Bucket();
+        BucketCreationMetaData metaData = new BucketCreationMetaData(
+                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesInBucket, Orientation
+                .NONE, maxCacheSize);
+        ImagePoolStats stats = new ImagePoolStatsProdImpl();
+
+        assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
+    }
+
+    @Test
+    public void testGetBufferedImageRecurse() {
+        int width = 10;
+        int height = 10;
+        int numberOfCopiesToRequestInBucket = 1;
+        int numberOfCopiesInBucket = 10;
+        int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket;
+
+        Bucket bucket = new Bucket();
+        for (int i = 0; i < numberOfCopiesInBucket; i++) {
+            bucket.mBufferedImageRef.add(new SoftReference<>(null));
+        }
+        BucketCreationMetaData metaData = new BucketCreationMetaData(
+                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
+                .NONE, maxCacheSize);
+        ImagePoolStats stats = new ImagePoolStatsProdImpl();
+
+        assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
+    }
+
+    @Test
+    public void testRecurseThenHitCacheLimit() {
+        int width = 10;
+        int height = 10;
+        int numberOfCopiesToRequestInBucket = 1;
+        int numberOfCopiesInBucket = 10;
+        int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket / 2;
+
+        Bucket bucket = new Bucket();
+        for (int i = 0; i < numberOfCopiesInBucket; i++) {
+            bucket.mBufferedImageRef.add(new SoftReference<>(null));
+        }
+        BucketCreationMetaData metaData = new BucketCreationMetaData(
+                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
+                .NONE, maxCacheSize);
+        ImagePoolStats stats = new ImagePoolStatsProdImpl();
+
+        assertNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
+    }
+
+    @Test
+    public void testBucketHasImageToReturn() {
+        int width = 10;
+        int height = 10;
+        int numberOfCopiesToRequestInBucket = 1;
+        int numberOfCopiesInBucket = 10;
+        int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket / 2;
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
+
+        Bucket bucket = new Bucket();
+        for (int i = 0; i < numberOfCopiesInBucket; i++) {
+            bucket.mBufferedImageRef.add(new SoftReference<>(null));
+        }
+        bucket.mBufferedImageRef.add(new SoftReference<>(image));
+        BucketCreationMetaData metaData = new BucketCreationMetaData(
+                width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
+                .NONE, maxCacheSize);
+        ImagePoolStats stats = new ImagePoolStatsProdImpl();
+
+        assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
+    }
+}
\ No newline at end of file
diff --git a/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java b/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
new file mode 100644
index 0000000..e8f0f17
--- /dev/null
+++ b/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imagepool;
+
+import org.junit.Test;
+
+import android.util.imagepool.ImagePool.Image;
+import android.util.imagepool.ImagePool.ImagePoolPolicy;
+
+import java.awt.image.BufferedImage;
+import java.lang.ref.SoftReference;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class ImagePoolImplTest {
+
+    private static final long TIMEOUT_SEC = 3;
+
+    @Test
+    public void testImagePoolInstance() {
+        ImagePool pool1 = ImagePoolProvider.get();
+        ImagePool pool2 = ImagePoolProvider.get();
+        assertNotNull(pool1);
+        assertNotNull(pool2);
+        assertEquals(pool1, pool2);
+    }
+
+
+    @Test
+    public void testImageDispose() throws InterruptedException {
+        int width = 700;
+        int height = 800;
+        int type = BufferedImage.TYPE_INT_ARGB;
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        ImagePoolImpl pool = getSimpleSingleBucketPool(width, height);
+        Image img1 = pool.acquire(width, height, type,
+                bufferedImage -> countDownLatch.countDown());
+        BufferedImage img = getImg(img1);
+        assertNotNull(img);
+        img1 = null;
+
+        // ensure dispose actually loses buffered image link so it can be gc'd
+        gc();
+        assertTrue(countDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
+    }
+    @Test
+    public void testImageDisposeFromFunction() throws InterruptedException {
+        int width = 700;
+        int height = 800;
+        int type = BufferedImage.TYPE_INT_ARGB;
+        CountDownLatch cd = new CountDownLatch(1);
+        ImagePoolImpl pool = getSimpleSingleBucketPool(width, height);
+
+        BufferedImage img = createImageAndReturnBufferedImage(pool, width, height, type, cd);
+        assertNotNull(img);
+
+        // ensure dispose actually loses buffered image link so it can be gc'd
+        gc();
+        assertTrue(cd.await(TIMEOUT_SEC, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testImageDisposedAndRecycled() throws InterruptedException {
+        int width = 700;
+        int height = 800;
+        int bucketWidth = 800;
+        int bucketHeight = 800;
+        int variant = 1;
+        int type = BufferedImage.TYPE_INT_ARGB;
+        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
+                new int[]{bucketWidth, bucketHeight},
+                new int[]{1, 1},
+                bucketHeight * bucketWidth * 4 * 3));
+
+        // acquire first image and draw something.
+        BufferedImage bufferedImageForImg1;
+        final CountDownLatch countDownLatch1 = new CountDownLatch(1);
+        {
+            Image img1 = pool.acquire(width, height, type,
+                    bufferedImage -> countDownLatch1.countDown());
+            bufferedImageForImg1 = getImg(img1);
+            img1 = null; // this is still needed.
+        }
+        // dispose
+        gc();
+        assertTrue(countDownLatch1.await(TIMEOUT_SEC, TimeUnit.SECONDS));
+
+        // ensure dispose actually loses buffered image link so it can be gc'd
+        assertNotNull(bufferedImageForImg1);
+        assertEquals(bufferedImageForImg1.getWidth(), bucketWidth);
+        assertEquals(bufferedImageForImg1.getHeight(), bucketHeight);
+
+        // get 2nd image with the same spec
+        final CountDownLatch countDownLatch2 = new CountDownLatch(1);
+        BufferedImage bufferedImageForImg2;
+        {
+            Image img2 = pool.acquire(width - variant, height - variant, type,
+                    bufferedImage -> countDownLatch2.countDown());
+            bufferedImageForImg2 = getImg(img2);
+            assertEquals(bufferedImageForImg1, bufferedImageForImg2);
+            img2 = null;
+        }
+        // dispose
+        gc();
+        assertTrue(countDownLatch2.await(TIMEOUT_SEC, TimeUnit.SECONDS));
+
+        // ensure that we're recycling previously created buffered image.
+        assertNotNull(bufferedImageForImg1);
+        assertNotNull(bufferedImageForImg2);
+    }
+
+
+    @Test
+    public void testBufferedImageReleased() throws InterruptedException {
+        int width = 700;
+        int height = 800;
+        int bucketWidth = 800;
+        int bucketHeight = 800;
+        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
+                new int[]{bucketWidth, bucketHeight},
+                new int[]{1, 1},
+                bucketWidth * bucketWidth * 4 * 2));
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        Image image1 = pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB,
+                bufferedImage -> countDownLatch.countDown());
+        BufferedImage internalPtr = getImg(image1);
+        // dispose
+        image1 = null;
+        gc();
+        assertTrue(countDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
+
+        // Simulate BufferedBitmaps being gc'd. Bucket filled with null soft refs.
+        for (Bucket bucket : ((ImagePoolImpl) pool).mPool.values()) {
+            bucket.mBufferedImageRef.clear();
+            bucket.mBufferedImageRef.add(new SoftReference<>(null));
+            bucket.mBufferedImageRef.add(new SoftReference<>(null));
+        }
+
+        assertNotEquals(internalPtr,
+                getImg(pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB)));
+    }
+
+    @Test
+    public void testPoolWidthHeightNotBigEnough() {
+        int width = 1000;
+        int height = 1000;
+        int bucketWidth = 999;
+        int bucketHeight = 800;
+        ImagePool pool = new ImagePoolImpl(
+                new ImagePoolPolicy(new int[]{bucketWidth, bucketHeight}, new int[]{1, 1},
+                        bucketWidth * bucketWidth * 4 * 2));
+        ImageImpl image = (ImageImpl) pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB);
+
+        assertEquals(getTooBigForPoolCount(pool), 1);
+    }
+
+    @Test
+    public void testSizeNotBigEnough() {
+        int width = 500;
+        int height = 500;
+        int bucketWidth = 800;
+        int bucketHeight = 800;
+        ImagePoolImpl pool = new ImagePoolImpl(
+                new ImagePoolPolicy(new int[]{bucketWidth, bucketHeight}, new int[]{1, 1},
+                        bucketWidth * bucketWidth)); // cache not big enough.
+        ImageImpl image = (ImageImpl) pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB);
+
+        assertEquals(getTooBigForPoolCount(pool), 1);
+        assertEquals(image.getWidth(), width);
+        assertEquals(image.getHeight(), height);
+    }
+
+    @Test
+    public void testImageMultipleCopies() throws InterruptedException {
+        int width = 700;
+        int height = 800;
+        int bucketWidth = 800;
+        int bucketHeight = 800;
+        int type = BufferedImage.TYPE_INT_ARGB;
+        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
+                new int[]{bucketWidth, bucketHeight},
+                new int[]{2, 2},
+                bucketHeight * bucketWidth * 4 * 4));
+
+        // create 1, and 2 different instances.
+        final CountDownLatch cd1 = new CountDownLatch(1);
+        Image img1 = pool.acquire(width, height, type, bufferedImage -> cd1.countDown());
+        BufferedImage bufferedImg1 = getImg(img1);
+
+        Image img2 = pool.acquire(width, height, type);
+        BufferedImage bufferedImg2 = getImg(img2);
+
+        assertNotEquals(bufferedImg1, bufferedImg2);
+
+        // disposing img1. Since # of copies == 2, this buffer should be recycled.
+        img1 = null;
+        gc();
+        cd1.await(TIMEOUT_SEC, TimeUnit.SECONDS);
+
+        // Ensure bufferedImg1 is recycled in newly acquired img3.
+        Image img3 = pool.acquire(width, height, type);
+        BufferedImage bufferedImage3 = getImg(img3);
+        assertNotEquals(bufferedImg2, bufferedImage3);
+        assertEquals(bufferedImg1, bufferedImage3);
+    }
+
+    @Test
+    public void testPoolDispose() throws InterruptedException {
+        int width = 700;
+        int height = 800;
+        int bucketWidth = 800;
+        int bucketHeight = 800;
+        int type = BufferedImage.TYPE_INT_ARGB;
+
+        // Pool barely enough for 1 image.
+        ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
+                new int[]{bucketWidth, bucketHeight},
+                new int[]{2, 2},
+                bucketHeight * bucketWidth * 4));
+
+        // create 1, and 2 different instances.
+        final CountDownLatch cd1 = new CountDownLatch(1);
+        Image img1 = pool.acquire(width, height, type, bufferedImage -> cd1.countDown());
+        BufferedImage bufferedImg1 = getImg(img1);
+        assertEquals(getAllocatedTotalBytes(pool), bucketWidth * bucketHeight * 4);
+        assertEquals(getTooBigForPoolCount(pool), 0);
+
+        // Release the img1.
+        img1 = null;
+        gc();
+        cd1.await(TIMEOUT_SEC, TimeUnit.SECONDS);
+
+        // Dispose pool.
+        pool.dispose();
+        assertEquals(getAllocatedTotalBytes(pool), 0);
+
+        // Request the same sized image as previous.
+        // If the pool was not disposed, this would return the image with bufferedImg1.
+        Image img2 = pool.acquire(width, height, type);
+        BufferedImage bufferedImg2 = getImg(img2);
+        assertEquals(getAllocatedTotalBytes(pool), bucketWidth * bucketHeight * 4);
+        assertEquals(getTooBigForPoolCount(pool), 0);
+
+        // Pool disposed before. No buffered image should be recycled.
+        assertNotEquals(img1, img2);
+        assertNotEquals(bufferedImg1, bufferedImg2);
+    }
+
+    private static BufferedImage createImageAndReturnBufferedImage(ImagePoolImpl pool, int width,
+            int height
+            , int type, CountDownLatch cd) {
+        Image img1 = pool.acquire(width, height, type, bufferedImage -> cd.countDown());
+        return getImg(img1);
+        // At this point img1 should have no reference, causing finalizable to trigger
+    }
+
+    private static ImagePoolImpl getSimpleSingleBucketPool(int width, int height) {
+
+        int bucketWidth = Math.max(width, height);
+        int bucketHeight = Math.max(width, height);
+        return new ImagePoolImpl(new ImagePoolPolicy(
+                new int[]{bucketWidth, bucketHeight},
+                new int[]{1, 1},
+                bucketHeight * bucketWidth * 4 * 3));
+    }
+
+    // Try to force a gc round
+    private static void gc() {
+        System.gc();
+        System.gc();
+        System.gc();
+    }
+
+    private static int getTooBigForPoolCount(ImagePool pool) {
+        return ((ImagePoolStatsProdImpl) ((ImagePoolImpl) pool).mImagePoolStats).mTooBigForPoolCount;
+    }
+
+    private static long getAllocatedTotalBytes(ImagePool pool) {
+        return ((ImagePoolStatsProdImpl) ((ImagePoolImpl) pool).mImagePoolStats).mAllocateTotalBytes;
+    }
+
+    private static BufferedImage getImg(Image image) {
+        return ((ImageImpl) image).mImg;
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/BridgeRenderSessionTest.java b/bridge/tests/src/com/android/layoutlib/bridge/BridgeRenderSessionTest.java
index 63b9b43..723311d 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/BridgeRenderSessionTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/BridgeRenderSessionTest.java
@@ -16,7 +16,6 @@
 
 package com.android.layoutlib.bridge;
 
-import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.Result.Status;
 
 import org.junit.Test;
@@ -32,6 +31,7 @@
         assertNotNull(renderSession.getImage());
         assertNotNull(renderSession.getRootViews());
         assertNotNull(renderSession.getSystemRootViews());
-        assertNotNull(renderSession.getDefaultProperties());
+        assertNotNull(renderSession.getDefaultNamespacedProperties());
+        assertNotNull(renderSession.getDefaultNamespacedStyles());
     }
 }
\ No newline at end of file
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index 46e3778..8c69e3e 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -48,7 +48,8 @@
         final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
         mErrors.clear();
         for (String clazz : classes) {
-            loadAndCompareClasses(clazz, clazz + "_Delegate");
+            String targetClassName = clazz.replace('$', '_') + "_Delegate";
+            loadAndCompareClasses(clazz, targetClassName);
         }
         assertTrue(getErrors(), mErrors.isEmpty());
     }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 77c997b..1a66c94 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -16,6 +16,8 @@
 
 package com.android.layoutlib.bridge.android;
 
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.XmlParserFactory;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 
 import org.junit.AfterClass;
@@ -27,6 +29,12 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
 
 import static org.junit.Assert.assertEquals;
 
@@ -39,12 +47,11 @@
 
     @Test
     public void testXmlBlockParser() throws Exception {
-
         XmlPullParser parser = ParserFactory.create(
                 getClass().getResourceAsStream("/com/android/layoutlib/testdata/layout1.xml"),
                         "layout1.xml");
 
-        parser = new BridgeXmlBlockParser(parser, null, false /* platformResourceFlag */);
+        parser = new BridgeXmlBlockParser(parser, null, ResourceNamespace.RES_AUTO);
 
         assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
 
@@ -125,12 +132,29 @@
         ParserFactory.setParserFactory(null);
     }
 
-    private static class ParserFactoryImpl
-            extends com.android.ide.common.rendering.api.ParserFactory {
-
-        @NonNull
+    private static class ParserFactoryImpl implements XmlParserFactory {
         @Override
-        public XmlPullParser createParser(String displayName) throws XmlPullParserException {
+        @Nullable
+        public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
+            return createXmlParserForFile(fileName);
+        }
+
+        @Override
+        @Nullable
+        public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
+            try {
+                InputStream stream = new BufferedInputStream(new FileInputStream(fileName));
+                XmlPullParser parser = new KXmlParser();
+                parser.setInput(stream, null);
+                return parser;
+            } catch (FileNotFoundException | XmlPullParserException e) {
+                return null;
+            }
+        }
+
+        @Override
+        @NonNull
+        public XmlPullParser createXmlParser() {
             return new KXmlParser();
         }
     }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 1360334..e2d5e6a 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -27,6 +27,9 @@
 import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
 
+import android.app.SystemServiceRegistry_AccessorTest;
+import android.content.res.Resources_DelegateTest;
+import android.graphics.Color_DelegateTest;
 import android.graphics.Matrix_DelegateTest;
 import android.util.BridgeXmlPullAttributesTest;
 
@@ -38,7 +41,9 @@
         RenderTests.class, LayoutParserWrapperTest.class,
         BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
         Matrix_DelegateTest.class, TestDelegates.class,
-        BridgeRenderSessionTest.class, ResourceHelperTest.class, BridgeContextTest.class
+        BridgeRenderSessionTest.class, ResourceHelperTest.class, BridgeContextTest.class,
+        SystemServiceRegistry_AccessorTest.class, Resources_DelegateTest.class,
+        Color_DelegateTest.class,
 })
 public class Main {
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
index 56d1d65..36c09ad 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -101,7 +101,7 @@
     /** Location of the app's asset dir inside {@link #TEST_RES_DIR} */
     private static final String APP_TEST_ASSET = APP_TEST_DIR + "/src/main/assets/";
     private static final String APP_CLASSES_LOCATION =
-            APP_TEST_DIR + "/build/intermediates/classes/debug/";
+            APP_TEST_DIR + "/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/";
     protected static Bridge sBridge;
     /** List of log messages generated by a render call. It can be used to find specific errors */
     protected static ArrayList<String> sRenderMessages = Lists.newArrayList();
@@ -312,7 +312,7 @@
         File buildProp = new File(PLATFORM_DIR, "build.prop");
         File attrs = new File(res, "values" + File.separator + "attrs.xml");
         sBridge = new Bridge();
-        sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
+        sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null,
                 ConfigGenerator.getEnumMap(attrs), getLayoutLog());
         Bridge.getLock().lock();
         try {
@@ -419,8 +419,9 @@
                 }
 
                 @Override
-                public void fidelityWarning(String tag, String message, Throwable throwable,
-                        Object viewCookie, Object data) {
+                public void fidelityWarning(@Nullable String tag, String message,
+                        Throwable throwable, Object cookie, Object data) {
+
                     System.out.println("FidelityWarning " + tag + ": " + message);
                     if (throwable != null) {
                         throwable.printStackTrace();
@@ -520,9 +521,10 @@
      * doesn't throw any exceptions and matches the provided image.
      */
     @Nullable
-    protected RenderResult renderAndVerify(String layoutFileName, String goldenFileName)
+    protected RenderResult renderAndVerify(String layoutFileName, String goldenFileName,
+            boolean decoration)
             throws ClassNotFoundException, FileNotFoundException {
-        return renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5);
+        return renderAndVerify(layoutFileName, goldenFileName, ConfigGenerator.NEXUS_5, decoration);
     }
 
     /**
@@ -531,8 +533,12 @@
      */
     @Nullable
     protected RenderResult renderAndVerify(String layoutFileName, String goldenFileName,
-            ConfigGenerator deviceConfig) throws ClassNotFoundException, FileNotFoundException {
+            ConfigGenerator deviceConfig, boolean decoration) throws ClassNotFoundException,
+            FileNotFoundException {
         SessionParams params = createSessionParams(layoutFileName, deviceConfig);
+        if (!decoration) {
+            params.setForceNoDecor();
+        }
         return renderAndVerify(params, goldenFileName);
     }
 
@@ -566,7 +572,7 @@
                 .setProjectResources(sProjectResources)
                 .setTheme("AppTheme", true)
                 .setRenderingMode(RenderingMode.NORMAL)
-                .setTargetSdk(22)
+                .setTargetSdk(28)
                 .setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true)
                 .setAssetRepository(new TestAssetRepository(TEST_RES_DIR + "/" + APP_TEST_ASSET));
     }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 3b4851f..2c91ade 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -18,10 +18,12 @@
 
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ResourceNamespace;
-import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValueImpl;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
 import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.rendering.api.XmlParserFactory;
 import com.android.internal.R;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
@@ -35,15 +37,14 @@
 import com.android.resources.Density;
 import com.android.resources.Navigation;
 import com.android.resources.ResourceType;
-import com.android.resources.ResourceUrl;
 
 import org.junit.After;
 import org.junit.Test;
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.AssetManager;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -83,7 +84,7 @@
 
     @Test
     public void testActivity() throws ClassNotFoundException, FileNotFoundException {
-        renderAndVerify("activity.xml", "activity.png");
+        renderAndVerify("activity.xml", "activity.png", true);
     }
 
     @Test
@@ -151,7 +152,18 @@
 
     @Test
     public void testAllWidgets() throws ClassNotFoundException, FileNotFoundException {
-        renderAndVerify("allwidgets.xml", "allwidgets.png");
+        LayoutPullParser parser = createParserFromPath("allwidgets.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setUseShadow(false);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .build();
+
+        renderAndVerify(params, "allwidgets.png");
 
         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
@@ -159,15 +171,30 @@
 
     @Test
     public void testArrayCheck() throws ClassNotFoundException, FileNotFoundException {
-        renderAndVerify("array_check.xml", "array_check.png");
+        renderAndVerify("array_check.xml", "array_check.png", false);
+
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(
+                message -> message.equals("Font$Builder.nAddAxis is not supported."));
     }
 
     @Test
     public void testAllWidgetsTablet() throws ClassNotFoundException, FileNotFoundException {
-        renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
+        LayoutPullParser parser = createParserFromPath("allwidgets.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setUseShadow(false);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_7_2012)
+                .setCallback(layoutLibCallback)
+                .build();
+        renderAndVerify(params, "allwidgets_tab.png");
 
         // We expect fidelity warnings for Path.isConvex. Fail for anything else.
         sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+        sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
     }
 
     @Test
@@ -370,6 +397,7 @@
                 .setParser(parser)
                 .setCallback(layoutLibCallback)
                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .disableDecoration()
                 .setRenderingMode(RenderingMode.V_SCROLL)
                 .build();
 
@@ -396,6 +424,10 @@
                         "        android:layout_height=\"wrap_content\"\n" +
                         "        android:layout_width=\"wrap_content\"\n" +
                         "        android:src=\"@drawable/headset\"/>\n" +
+                        "    <ImageView\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:src=\"@drawable/clipped_even_odd\"/>\n" +
                         "</LinearLayout>");
         // Create LayoutLibCallback.
         LayoutLibTestCallback layoutLibCallback =
@@ -413,6 +445,61 @@
     }
 
     /**
+     * Test a ImageView which has a vector drawable as its src and tint attribute.
+     */
+    @Test
+    public void testVectorDrawableWithTintInImageView() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "    android:layout_height=\"match_parent\"\n" +
+                        "    android:layout_width=\"match_parent\"\n" +
+                        "    android:src=\"@drawable/vector_drawable_without_tint\"\n" +
+                        "    android:tint=\"#FF00FF00\" />");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
+
+        renderAndVerify(params, "vector_drawable_with_tint_in_image_view.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    /**
+     * Test a vector drawable which has tint attribute.
+     */
+    @Test
+    public void testVectorDrawableWithTintInItself() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "    android:layout_height=\"match_parent\"\n" +
+                        "    android:layout_width=\"match_parent\"\n" +
+                        "    android:src=\"@drawable/vector_drawable_with_tint\" />");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
+
+        renderAndVerify(params, "vector_drawable_with_tint_itself.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    /**
      * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
      * for vector drawables (lines, moves and cubic and quadratic curves).
      */
@@ -520,6 +607,79 @@
                 TimeUnit.SECONDS.toNanos(2));
     }
 
+    /**
+     * Tests that the gradients are correctly displayed when using transparent colors
+     * and a wide range of offset values.
+     * <p/>
+     * http://b/112759140
+     */
+    @Test
+    public void testGradientColors() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" +
+                        "    <ImageView\n" +
+                        "             android:layout_height=\"match_parent\"\n" +
+                        "             android:layout_width=\"match_parent\"\n" +
+                        "             android:src=\"@drawable/gradient\" />\n\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "gradient_colors.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    /**
+     * Tests that the gradients are correctly combined with alpha values.
+     * <p/>
+     * http://b/122260583
+     */
+    @Test
+    public void testGradientAlphaDrawable() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" +
+                        "    <ImageView\n" +
+                        "             android:layout_height=\"match_parent\"\n" +
+                        "             android:layout_width=\"match_parent\"\n" +
+                        "             android:src=\"@drawable/vector_gradient_alpha\" />\n\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "gradient_alpha_drawable.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
     /** Test activity.xml */
     @Test
     public void testScrollingAndMeasure() throws ClassNotFoundException, FileNotFoundException {
@@ -535,8 +695,8 @@
                 .setCallback(layoutLibCallback)
                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
                 .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
                 .build();
-        params.setForceNoDecor();
         params.setExtendedViewInfoMode(true);
 
         // Do an only-measure pass
@@ -568,8 +728,8 @@
                     .setCallback(layoutLibCallback)
                     .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
                     .setRenderingMode(RenderingMode.V_SCROLL)
+                    .disableDecoration()
                     .build();
-        params.setForceNoDecor();
         params.setExtendedViewInfoMode(true);
 
         result = renderAndVerify(params, "scrolled.png");
@@ -607,9 +767,9 @@
         assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar));
         assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar));
         assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar));
-        int id = Resources_Delegate.getLayoutlibCallback(resources).getResourceId(
-                ResourceType.STRING,
-                "app_name");
+        Integer id = Resources_Delegate.getLayoutlibCallback(resources).getOrGenerateResourceId(
+                new ResourceReference(ResourceNamespace.RES_AUTO, ResourceType.STRING, "app_name"));
+        assertNotNull(id);
         assertEquals("com.android.layoutlib.test.myapplication:string/app_name",
                 resources.getResourceName(id));
         assertEquals("com.android.layoutlib.test.myapplication",
@@ -644,9 +804,14 @@
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
 
-        int id = Resources_Delegate.getLayoutlibCallback(resources).getResourceId(
-                ResourceType.ARRAY,
-                "string_array");
+        Integer id =
+                Resources_Delegate.getLayoutlibCallback(resources)
+                        .getOrGenerateResourceId(
+                                new ResourceReference(
+                                        ResourceNamespace.RES_AUTO,
+                                        ResourceType.ARRAY,
+                                        "string_array"));
+        assertNotNull(id);
         String[] strings = resources.getStringArray(id);
         assertArrayEquals(
                 new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"},
@@ -659,7 +824,9 @@
     @Test
     public void testFonts() throws ClassNotFoundException, FileNotFoundException {
         // TODO: styles seem to be broken in TextView
-        renderAndVerify("fonts_test.xml", "font_test.png");
+        renderAndVerify("fonts_test.xml", "font_test.png", false);
+        sRenderMessages.removeIf(
+                message -> message.equals("Font$Builder.nAddAxis is not supported."));
     }
 
     @Test
@@ -771,9 +938,22 @@
         // Setup
         // Create the layout pull parser for our resources (empty.xml can not be part of the test
         // app as it won't compile).
-        ParserFactory.setParserFactory(new com.android.ide.common.rendering.api.ParserFactory() {
+        ParserFactory.setParserFactory(new XmlParserFactory() {
             @Override
-            public XmlPullParser createParser(String debugName) throws XmlPullParserException {
+            @Nullable
+            public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
+                return null;
+            }
+
+            @Override
+            @Nullable
+            public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
+                return null;
+            }
+
+            @Override
+            @NonNull
+            public XmlPullParser createXmlParser() {
                 return new KXmlParser();
             }
         });
@@ -801,7 +981,7 @@
 
         try {
             ColorStateList stateList = ResourceHelper.getColorStateList(
-                    new ResourceValue(
+                    new ResourceValueImpl(
                             ResourceNamespace.RES_AUTO,
                             ResourceType.COLOR,
                             "test_list",
@@ -823,7 +1003,7 @@
             Resources.Theme theme = mContext.getResources().newTheme();
             theme.applyStyle(R.style.ThemeOverlay_Material_Light, true);
             stateList = ResourceHelper.getColorStateList(
-                    new ResourceValue(
+                    new ResourceValueImpl(
                             ResourceNamespace.RES_AUTO,
                             ResourceType.COLOR,
                             "test_list",
@@ -847,8 +1027,115 @@
     }
 
     @Test
+    public void testShadowFlagsNoShadows() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadows_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setUseShadow(false);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "shadows_test_no_shadow.png");
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+    }
+
+    @Test
     public void testRectangleShadow() throws Exception {
-        renderAndVerify("shadows_test.xml", "shadows_test.png");
+        LayoutPullParser parser = createParserFromPath("shadows_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setShadowQuality(false);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "shadows_test.png");
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+    }
+
+    @Test
+    public void testHighQualityRectangleShadow() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadows_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setShadowQuality(true);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "shadows_test_high_quality.png");
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+    }
+
+    @Test
+    public void testHighQualityRoundedEdgeRectangleShadow() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadows_rounded_edge_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setShadowQuality(true);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .build();
+
+        renderAndVerify(params, "shadows_test_high_quality_rounded_edge.png");
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+    }
+
+    @Test
+    public void testHighQualityLargeViewShadow() throws Exception {
+        LayoutPullParser parser = createParserFromPath("large_view_shadows_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setShadowQuality(true);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "large_shadows_test_high_quality.png");
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+    }
+
+    @Test
+    public void testHighQualityShadowSizes() throws Exception {
+        LayoutPullParser parser = createParserFromPath("shadow_sizes_test.xml");
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setShadowQuality(true);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setCallback(layoutLibCallback)
+                .build();
+
+        renderAndVerify(params, "shadow_sizes_test_high_quality.png");
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
     }
 
     @Test
@@ -874,12 +1161,23 @@
                 params.getTargetSdkVersion(), params.isRtlSupported());
         Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
                 configuration, params.getLayoutlibCallback());
-        int id = Resources_Delegate.getLayoutlibCallback(resources).getResourceId(
-                ResourceType.STRING,
-                "app_name");
-        assertEquals(id, resources.getIdentifier("string/app_name", null, null));
-        assertEquals(id, resources.getIdentifier("app_name", "string", null));
-        assertEquals(0, resources.getIdentifier("string/does_not_exist", null, null));
+        Integer id =
+                Resources_Delegate.getLayoutlibCallback(resources)
+                        .getOrGenerateResourceId(
+                                new ResourceReference(
+                                        ResourceNamespace.RES_AUTO,
+                                        ResourceType.STRING,
+                                        "app_name"));
+        assertNotNull(id);
+        assertEquals(id.intValue(),
+                resources.getIdentifier("com.android.layoutlib.test.myapplication:string/app_name",
+                        null, null));
+        assertEquals(id.intValue(), resources.getIdentifier("app_name", "string",
+                "com.android.layoutlib.test.myapplication"));
+        assertEquals(0, resources.getIdentifier("string/app_name", null, null));
+        assertEquals(0, resources.getIdentifier("string/app_name", null, "com.foo.bar"));
+        assertEquals(0, resources.getIdentifier("string/does_not_exist", null,
+                "com.android.layoutlib.test.myapplication"));
         assertEquals(R.string.accept, resources.getIdentifier("android:string/accept", null,
                 null));
         assertEquals(R.string.accept, resources.getIdentifier("string/accept", null,
@@ -1131,8 +1429,8 @@
                 .setCallback(layoutLibCallback)
                 .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
                 .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
                 .build();
-        params.setForceNoDecor();
 
         RenderResult result = RenderTestBase.render(sBridge, params, -1);
         BufferedImage image = result.getImage();
@@ -1145,4 +1443,254 @@
 
         RenderTestBase.verify("view_boundaries.png", image);
     }
+
+    /**
+     * Test rendering of strings that have mixed RTL and LTR scripts.
+     * <p>
+     * http://b/37510906
+     */
+    @Test
+    public void testMixedRtlLtrRendering() throws Exception {
+        //
+        final String layout =
+                "<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" + "\n" +
+                        "    <TextView\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:textSize=\"30sp\"\n" +
+                        "        android:background=\"#55FF0000\"\n" +
+                        "        android:text=\"این یک رشته ایرانی است\"/>\n" +
+                        "    <TextView\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:textSize=\"30sp\"\n" +
+                        "        android:background=\"#55FF00FF\"\n" +
+                        "        android:text=\"این یک رشته ایرانی است(\"/>\n" +
+                        "    <TextView\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:textSize=\"30sp\"\n" +
+                        "        android:background=\"#55FAF012\"\n" +
+                        "        android:text=\")(این یک رشته ایرانی است(\"/>\n" +
+                        "</LinearLayout>";
+
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "rtl_ltr.png", -1);
+    }
+
+    @Test
+    public void testViewStub() throws Exception {
+        //
+        final String layout =
+                "<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" + "\n" +
+                        "      <ViewStub\n" +
+                        "        xmlns:tools=\"http://schemas.android.com/tools\"\n" +
+                        "        android:layout_width=\"match_parent\"\n" +
+                        "        android:layout_height=\"match_parent\"\n" +
+                        "        android:layout=\"@layout/four_corners\"\n" +
+                        "        tools:visibility=\"visible\" />" +
+                        "</LinearLayout>";
+
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "view_stub.png", -1);
+    }
+
+    @Test
+    public void testImageResize() throws ClassNotFoundException {
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        layoutLibCallback.setUseShadow(false);
+
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "    android:layout_width=\"match_parent\"\n" +
+                        "    android:layout_height=\"match_parent\"\n" +
+                        "    android:background=\"@drawable/ninepatch\"\n" +
+                        "    android:layout_margin=\"20dp\"\n" +
+                        "    android:orientation=\"vertical\">\n\n" +
+                        "    <Button\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:text=\"Button\" />\n\n" +
+                        "    <Button\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:text=\"Button\" />\n"
+                        + "</LinearLayout>");
+
+        // Ask for an image that it's 1/10th the size of the actual device image
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setImageFactory((width, height) ->
+                        new BufferedImage(width / 10, height / 10,
+                        BufferedImage.TYPE_INT_ARGB))
+                .setFlag(RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE, true)
+                .build();
+
+        renderAndVerify(params, "auto-scale-image.png");
+    }
+
+    @Test
+    public void testTranslation() throws ClassNotFoundException, FileNotFoundException {
+        RenderResult res = renderAndVerify("translate_test.xml", "translate_test.png", false);
+        ViewInfo rootInfo = res.getRootViews().get(0);
+        ViewInfo buttonInfo = rootInfo.getChildren().get(0);
+        assertEquals(100, buttonInfo.getLeft());
+    }
+
+    /**
+     * Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
+     * for vector drawables (lines, moves and cubic and quadratic curves).
+     */
+    @Test
+    public void testCanvas() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"fill_parent\"\n" +
+                        "              android:layout_height=\"fill_parent\">\n" +
+                        "    <com.android.layoutlib.test.myapplication.widgets.CanvasTestView\n" +
+                        "             android:layout_height=\"fill_parent\"\n" +
+                        "             android:layout_width=\"fill_parent\"\n" +
+                        "             android:src=\"@drawable/android\" />\n" + "\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "canvas.png", TimeUnit.SECONDS.toNanos(2));
+    }
+
+    @Test
+    public void testTypedArrays() throws ClassNotFoundException, FileNotFoundException {
+        renderAndVerify("typed_array.xml", "typed_arrays.png", false);
+    }
+
+    /**
+     * Tests that the gradients are correctly displayed when using transparent colors
+     * and a wide range of offset values.
+     * <p/>
+     * http://b/112759140
+     */
+    @Test
+    public void testAnimatedVectorDrawableWithColorInterpolator() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:padding=\"16dp\"\n" +
+                        "              android:orientation=\"horizontal\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" +
+                        "    <ImageView\n" +
+                        "             android:layout_height=\"match_parent\"\n" +
+                        "             android:layout_width=\"match_parent\"\n" +
+                        "             android:src=\"@drawable/avd_color_interpolator\" />\n\n" +
+                        "</LinearLayout>");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "color_interpolation.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    @Test
+    public void testManyLineBreaks() throws Exception {
+        String layout =
+                "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:layout_width=\"match_parent\"\n" +
+                        "              android:layout_height=\"match_parent\">\n" + "\n" +
+                        "    <EditText\n" +
+                        "        android:layout_width=\"match_parent\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:fallbackLineSpacing=\"true\"\n" +
+                        "        android:text=\"A very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very very very very very very very very " +
+                        "very very very very very very very long text\"/>\n" +
+                        "</FrameLayout>";
+
+        LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .disableDecoration()
+                .build();
+
+        renderAndVerify(params, "many_line_breaks.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 184538e..f2d62d4 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -21,14 +21,11 @@
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.ide.common.rendering.api.ParserFactory;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams.Key;
-import com.android.ide.common.resources.IntArrayWrapper;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
 import com.android.resources.ResourceType;
-import com.android.util.Pair;
 import com.android.utils.ILogger;
 
 import org.kxml2.io.KXmlParser;
@@ -38,30 +35,33 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.util.HashMap;
 import java.util.Map;
 
-import com.google.android.collect.Maps;
+import com.google.common.io.ByteStreams;
 
-import static org.junit.Assert.fail;
+import static com.android.ide.common.rendering.api.ResourceNamespace.RES_AUTO;
 
-@SuppressWarnings("deprecation") // For Pair
 public class LayoutLibTestCallback extends LayoutlibCallback {
-
-    private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
     private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
 
-    private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
-    private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
-    private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
+    private final Map<Integer, ResourceReference> mProjectResources = new HashMap<>();
+    private final Map<ResourceReference, Integer> mResources = new HashMap<>();
     private final ILogger mLog;
     private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
     private final ClassLoader mModuleClassLoader;
     private String mAdaptiveIconMaskPath;
+    private boolean mSetUseShadow = true;
+    private boolean mHighShadowQuality = true;
 
     public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
         mLog = logger;
@@ -75,27 +75,22 @@
             final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
 
             if (resType != null) {
-                final Map<String, Integer> resName2Id = Maps.newHashMap();
-                mResources.put(resType, resName2Id);
-
                 for (Field field : resClass.getDeclaredFields()) {
                     final int modifiers = field.getModifiers();
                     if (Modifier.isStatic(modifiers)) { // May not be final in library projects
                         final Class<?> type = field.getType();
                         try {
-                            if (type.isArray() && type.getComponentType() == int.class) {
-                                mStyleableValueToNameMap.put(
-                                        new IntArrayWrapper((int[]) field.get(null)),
-                                        field.getName());
-                            } else if (type == int.class) {
+                            if (type == int.class) {
                                 final Integer value = (Integer) field.get(null);
-                                mProjectResources.put(value, Pair.of(resType, field.getName()));
-                                resName2Id.put(field.getName(), value);
-                            } else {
+                                ResourceReference reference =
+                                        new ResourceReference(RES_AUTO, resType, field.getName());
+                                mProjectResources.put(value, reference);
+                                mResources.put(reference, value);
+                            } else if (!(type.isArray() && type.getComponentType() == int.class)) {
                                 mLog.error(null, "Unknown field type in R class: %1$s", type);
                             }
-                        } catch (IllegalAccessException ignored) {
-                            mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
+                        } catch (IllegalAccessException e) {
+                            mLog.error(e, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
                         }
                     }
                 }
@@ -105,7 +100,7 @@
 
 
     @Override
-    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+    public Object loadView(@NonNull String name, @NonNull Class[] constructorSignature, Object[] constructorArgs)
             throws Exception {
         Class<?> viewClass = mModuleClassLoader.loadClass(name);
         Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
@@ -113,6 +108,7 @@
         return viewConstructor.newInstance(constructorArgs);
     }
 
+    @NonNull
     @Override
     public String getNamespace() {
         return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
@@ -120,32 +116,18 @@
     }
 
     @Override
-    public Pair<ResourceType, String> resolveResourceId(int id) {
+    public ResourceReference resolveResourceId(int id) {
         return mProjectResources.get(id);
     }
 
     @Override
-    public String resolveResourceId(int[] id) {
-        return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
+    public int getOrGenerateResourceId(@NonNull ResourceReference resource) {
+        Integer id = mResources.get(resource);
+        return id != null ? id : 0;
     }
 
     @Override
-    public Integer getResourceId(ResourceType type, String name) {
-        Map<String, Integer> resName2Id = mResources.get(type);
-        if (resName2Id == null) {
-            return null;
-        }
-        return resName2Id.get(name);
-    }
-
-    @Override
-    public ILayoutPullParser getParser(String layoutName) {
-        fail("This method shouldn't be called by this version of LayoutLib.");
-        return null;
-    }
-
-    @Override
-    public ILayoutPullParser getParser(ResourceValue layoutResource) {
+    public ILayoutPullParser getParser(@NonNull ResourceValue layoutResource) {
         try {
             return LayoutPullParser.createFromFile(new File(layoutResource.getValue()));
         } catch (FileNotFoundException e) {
@@ -177,20 +159,36 @@
         return false;
     }
 
-    @NonNull
     @Override
-    public ParserFactory getParserFactory() {
-        return new ParserFactory() {
-            @NonNull
-            @Override
-            public XmlPullParser createParser(@Nullable String debugName)
-                    throws XmlPullParserException {
-                return new KXmlParser();
-            }
-        };
+    @Nullable
+    public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
+        return createXmlParserForFile(fileName);
     }
 
     @Override
+    @Nullable
+    public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
+        try (FileInputStream fileStream = new FileInputStream(fileName)) {
+            // Read data fully to memory to be able to close the file stream.
+            ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
+            ByteStreams.copy(fileStream, byteOutputStream);
+            KXmlParser parser = new KXmlParser();
+            parser.setInput(new ByteArrayInputStream(byteOutputStream.toByteArray()), null);
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            return parser;
+        } catch (IOException | XmlPullParserException e) {
+            return null;
+        }
+    }
+
+    @Override
+    @NonNull
+    public XmlPullParser createXmlParser() {
+        return new KXmlParser();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked") // The Key<T> API is based on unchecked casts.
     public <T> T getFlag(Key<T> key) {
         if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
             return (T) PACKAGE_NAME;
@@ -198,10 +196,32 @@
         if (key.equals(RenderParamsFlags.FLAG_KEY_ADAPTIVE_ICON_MASK_PATH)) {
             return (T) mAdaptiveIconMaskPath;
         }
+        if (key.equals(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW)) {
+            return (T) new Boolean(mHighShadowQuality);
+        }
+        if (key.equals(RenderParamsFlags.FLAG_ENABLE_SHADOW)) {
+            return (T) new Boolean(mSetUseShadow);
+        }
         return null;
     }
 
     public void setAdaptiveIconMaskPath(String adaptiveIconMaskPath) {
         mAdaptiveIconMaskPath = adaptiveIconMaskPath;
     }
+
+    /**
+     * Enables shadow from rendering. Shadow rendering is enabled by default.
+     * @param useShadow true to enable shadow. False to disable.
+     */
+    public void setUseShadow(boolean useShadow) {
+        mSetUseShadow = useShadow;
+    }
+
+    /**
+     * Sets high quality shadow rendering. Turned off by default.
+     * @param useHighQuality true to use high quality shadow. False otherwise.
+     */
+    public void setShadowQuality(boolean useHighQuality) {
+        mHighShadowQuality = useHighQuality;
+    }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
index 526613f..af0c9e5 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.intensive.setup;
 
 import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 
 import org.kxml2.io.KXmlParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -29,7 +30,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOError;
 import java.io.InputStream;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -41,6 +42,9 @@
 import static com.android.SdkConstants.TOOLS_URI;
 
 public class LayoutPullParser extends KXmlParser implements ILayoutPullParser{
+
+    private ResourceNamespace mLayoutNamespace = ResourceNamespace.RES_AUTO;
+
     @NonNull
     public static LayoutPullParser createFromFile(@NonNull File layoutFile)
             throws FileNotFoundException {
@@ -63,7 +67,7 @@
     @NonNull
     public static LayoutPullParser createFromString(@NonNull String contents) {
         return new LayoutPullParser(new ByteArrayInputStream(
-                contents.getBytes(Charset.forName("UTF-8"))));
+                contents.getBytes(StandardCharsets.UTF_8)));
     }
 
     private LayoutPullParser(@NonNull InputStream inputStream) {
@@ -96,7 +100,7 @@
                         continue;
                     }
                     if (map == null) {
-                        map = new HashMap<String, String>(4);
+                        map = new HashMap<>(4);
                     }
                     map.put(attribute, getAttributeValue(i));
                 }
@@ -109,10 +113,12 @@
     }
 
     @Override
-    @Deprecated
-    public ILayoutPullParser getParser(String layoutName) {
-        // Studio returns null.
-        return null;
+    @NonNull
+    public ResourceNamespace getLayoutNamespace() {
+        return mLayoutNamespace;
     }
 
+    public void setLayoutNamespace(@NonNull ResourceNamespace layoutNamespace) {
+        mLayoutNamespace = layoutNamespace;
+    }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
index 4866051..b1801c6 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
@@ -182,7 +182,7 @@
                 assertTrue(deleted);
             }
             ImageIO.write(deltaImage, "PNG", output);
-            error += " - see details in " + output.getPath() + "\n";
+            error += " - see details in file://" + output.getPath() + "\n";
             error = saveImageAndAppendMessage(image, error, relativePath);
             System.out.println(error);
             fail(error);
@@ -303,8 +303,14 @@
      */
     @NonNull
     private static File getFailureDir() {
-        String workingDirString = System.getProperty("user.dir");
-        File failureDir = new File(workingDirString, "out/failures");
+        File failureDir;
+        String failureDirString = System.getProperty("test_failure.dir");
+        if (failureDirString != null) {
+            failureDir = new File(failureDirString);
+        } else {
+            String workingDirString = System.getProperty("user.dir");
+            failureDir = new File(workingDirString, "out/failures");
+        }
 
         //noinspection ResultOfMethodCallIgnored
         failureDir.mkdirs();
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
index 1c7857c..321bb77 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
@@ -18,6 +18,7 @@
 
 import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.AssetRepository;
+import com.android.ide.common.rendering.api.IImageFactory;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.ResourceNamespace;
@@ -57,6 +58,8 @@
     private LayoutLog mLayoutLog;
     private Map<SessionParams.Key, Object> mFlags = new HashMap<>();
     private AssetRepository mAssetRepository = null;
+    private boolean mDecor = true;
+    private IImageFactory mImageFactory = null;
 
     @NonNull
     public SessionParamsBuilder setParser(@NonNull LayoutPullParser layoutParser) {
@@ -146,6 +149,18 @@
     }
 
     @NonNull
+    public SessionParamsBuilder disableDecoration() {
+        mDecor = false;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setImageFactory(@NonNull IImageFactory imageFactory) {
+        mImageFactory = imageFactory;
+        return this;
+    }
+
+    @NonNull
     public SessionParams build() {
         assert mFrameworkResources != null;
         assert mProjectResources != null;
@@ -157,7 +172,7 @@
         ResourceResolver resourceResolver = ResourceResolver.create(
                 ImmutableMap.of(
                         ResourceNamespace.ANDROID, mFrameworkResources.getConfiguredResources(config),
-                        ResourceNamespace.TODO, mProjectResources.getConfiguredResources(config)),
+                        ResourceNamespace.TODO(), mProjectResources.getConfiguredResources(config)),
                 new ResourceReference(
                         ResourceNamespace.fromBoolean(!isProjectTheme),
                         ResourceType.STYLE,
@@ -166,10 +181,17 @@
         SessionParams params = new SessionParams(mLayoutParser, mRenderingMode, mProjectKey /* for
         caching */, mConfigGenerator.getHardwareConfig(), resourceResolver, mLayoutlibCallback,
                 mMinSdk, mTargetSdk, mLayoutLog);
+        if (mImageFactory != null) {
+            params.setImageFactory(mImageFactory);
+        }
 
         mFlags.forEach(params::setFlag);
         params.setAssetRepository(mAssetRepository);
 
+        if (!mDecor) {
+            params.setForceNoDecor();
+        }
+
         return params;
     }
 }
diff --git a/create/.classpath b/create/.classpath
deleted file mode 100644
index 25c3b3e..0000000
--- a/create/.classpath
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry excluding="mock_data/" kind="src" path="tests"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
-	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/asm/asm-4.0.jar" sourcepath="/ANDROID_PLAT_SRC/prebuilts/misc/common/asm/src.zip"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/create/.project b/create/.project
deleted file mode 100644
index e100d17..0000000
--- a/create/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>layoutlib_create</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/create/Android.bp b/create/Android.bp
index 3ac790e..961456a 100644
--- a/create/Android.bp
+++ b/create/Android.bp
@@ -22,6 +22,7 @@
     main_class: "com.android.tools.layoutlib.create.Main",
     static_libs: [
         "asm-6.0",
+        "guava",
         "layoutlib-common",
     ],
 }
diff --git a/create/create.iml b/create/create.iml
index 8c05400..8814d91 100644
--- a/create/create.iml
+++ b/create/create.iml
@@ -24,5 +24,16 @@
     </orderEntry>
     <orderEntry type="library" scope="TEST" name="junit" level="project" />
     <orderEntry type="module" module-name="common" />
+    <orderEntry type="module-library">
+      <library name="guava">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/22.0/guava-22.0-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
   </component>
-</module>
\ No newline at end of file
+</module>
diff --git a/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index 919ef9f..b77708b 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -16,6 +16,9 @@
 
 package com.android.tools.layoutlib.create;
 
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.Attribute;
 import org.objectweb.asm.ClassReader;
@@ -27,16 +30,24 @@
 import org.objectweb.asm.signature.SignatureReader;
 import org.objectweb.asm.signature.SignatureVisitor;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -47,20 +58,49 @@
  */
 public class AsmAnalyzer {
 
+    public static class Result {
+        private final Map<String, ClassReader> mFound;
+        private final Map<String, ClassReader> mDeps;
+        private final Map<String, InputStream> mFilesFound;
+        private final Set<String> mReplaceMethodCallClasses;
+
+        private Result(Map<String, ClassReader> found, Map<String, ClassReader> deps,
+                Map<String, InputStream> filesFound, Set<String> replaceMethodCallClasses) {
+            mFound = found;
+            mDeps = deps;
+            mFilesFound = filesFound;
+            mReplaceMethodCallClasses = replaceMethodCallClasses;
+        }
+
+        public Map<String, ClassReader> getFound() {
+            return mFound;
+        }
+
+        public Map<String, ClassReader> getDeps() {
+            return mDeps;
+        }
+
+        public Map<String, InputStream> getFilesFound() {
+            return mFilesFound;
+        }
+
+        public Set<String> getReplaceMethodCallClasses() {
+            return mReplaceMethodCallClasses;
+        }
+    }
+
     // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
 
     /** Output logger. */
     private final Log mLog;
     /** The input source JAR to parse. */
     private final List<String> mOsSourceJar;
-    /** The generator to fill with the class list and dependency list. */
-    private final AsmGenerator mGen;
     /** Keep all classes that derive from these one (these included). */
     private final String[] mDeriveFrom;
     /** Glob patterns of classes to keep, e.g. "com.foo.*" */
     private final String[] mIncludeGlobs;
-    /** The set of classes to exclude.*/
-    private final Set<String> mExcludedClasses;
+    /** Glob patterns of classes to exclude.*/
+    private final String[] mExcludedGlobs;
     /** Glob patterns of files to keep as is. */
     private final String[] mIncludeFileGlobs;
     /** Internal names of classes that contain method calls that need to be rewritten. */
@@ -71,47 +111,65 @@
      *
      * @param log The log output.
      * @param osJarPath The input source JARs to parse.
-     * @param gen The generator to fill with the class list and dependency list.
      * @param deriveFrom Keep all classes that derive from these one (these included).
      * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
      *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
      * @param includeFileGlobs Glob patterns of files which are kept as is. This is only for files
      *        not ending in .class.
      */
-    public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
-            String[] deriveFrom, String[] includeGlobs, Set<String> excludeClasses,
+    public AsmAnalyzer(Log log, List<String> osJarPath,
+            String[] deriveFrom, String[] includeGlobs, String[] excludedGlobs,
             String[] includeFileGlobs) {
         mLog = log;
-        mGen = gen;
-        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
+        mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<>();
         mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
         mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
-        mExcludedClasses = excludeClasses;
+        mExcludedGlobs = excludedGlobs != null ? excludedGlobs : new String[0];
         mIncludeFileGlobs = includeFileGlobs != null ? includeFileGlobs : new String[0];
     }
 
     /**
-     * Starts the analysis using parameters from the constructor.
-     * Fills the generator with classes & dependencies found.
+     * Starts the analysis using parameters from the constructor and returns the result.
      */
-    public void analyze() throws IOException, LogAbortException {
-
-        TreeMap<String, ClassReader> zipClasses = new TreeMap<>();
+    @NotNull
+    public Result analyze() throws IOException {
+        Map<String, ClassReader> zipClasses = new TreeMap<>();
         Map<String, InputStream> filesFound = new TreeMap<>();
 
         parseZip(mOsSourceJar, zipClasses, filesFound);
         mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
                 mOsSourceJar.size() > 1 ? "s" : "");
 
-        Map<String, ClassReader> found = findIncludes(zipClasses);
-        Map<String, ClassReader> deps = findDeps(zipClasses, found);
+        Pattern[] includePatterns = Arrays.stream(mIncludeGlobs).parallel()
+                .map(AsmAnalyzer::getPatternFromGlob)
+                .toArray(Pattern[]::new);
+        Pattern[] excludePatterns = Arrays.stream(mExcludedGlobs).parallel()
+                .map(AsmAnalyzer::getPatternFromGlob)
+                .toArray(Pattern[]::new);
 
-        if (mGen != null) {
-            mGen.setKeep(found);
-            mGen.setDeps(deps);
-            mGen.setCopyFiles(filesFound);
-            mGen.setRewriteMethodCallClasses(mReplaceMethodCallClasses);
-        }
+
+        Map<String, ClassReader> found = new HashMap<>();
+        findIncludes(mLog, includePatterns, mDeriveFrom, zipClasses, entry -> {
+            if (!matchesAny(entry.getKey(), excludePatterns)) {
+                found.put(entry.getKey(), entry.getValue());
+            }
+        });
+
+        Map<String, ClassReader> deps = new HashMap<>();
+        findDeps(mLog, zipClasses, found, keepEntry -> {
+            if (!matchesAny(keepEntry.getKey(), excludePatterns)) {
+                found.put(keepEntry.getKey(), keepEntry.getValue());
+            }
+        }, depEntry -> {
+            if (!matchesAny(depEntry.getKey(), excludePatterns)) {
+                deps.put(depEntry.getKey(), depEntry.getValue());
+            }
+        });
+
+        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
+                found.size(), deps.size());
+
+        return new Result(found, deps, filesFound, mReplaceMethodCallClasses);
     }
 
     /**
@@ -134,27 +192,39 @@
             includeFilePatterns[i] = getPatternFromGlob(mIncludeFileGlobs[i]);
         }
 
+        List<ForkJoinTask<?>> futures = new ArrayList<>();
         for (String jarPath : jarPathList) {
-            ZipFile zip = new ZipFile(jarPath);
-            Enumeration<? extends ZipEntry> entries = zip.entries();
-            ZipEntry entry;
-            while (entries.hasMoreElements()) {
-                entry = entries.nextElement();
-                if (entry.getName().endsWith(".class")) {
-                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
-                    String className = classReaderToClassName(cr);
-                    classes.put(className, cr);
-                } else {
-                    for (Pattern includeFilePattern : includeFilePatterns) {
-                        if (includeFilePattern.matcher(entry.getName()).matches()) {
-                            filesFound.put(entry.getName(), zip.getInputStream(entry));
-                            break;
+            futures.add(ForkJoinPool.commonPool().submit(() -> {
+                try {
+                    ZipFile zip = new ZipFile(jarPath);
+                    Enumeration<? extends ZipEntry> entries = zip.entries();
+                    ZipEntry entry;
+                    while (entries.hasMoreElements()) {
+                        entry = entries.nextElement();
+                        if (entry.getName().endsWith(".class")) {
+                            ClassReader cr = new ClassReader(zip.getInputStream(entry));
+                            String className = classReaderToClassName(cr);
+                            synchronized (classes) {
+                                classes.put(className, cr);
+                            }
+                        } else {
+                            for (Pattern includeFilePattern : includeFilePatterns) {
+                                if (includeFilePattern.matcher(entry.getName()).matches()) {
+                                    synchronized (filesFound) {
+                                        filesFound.put(entry.getName(), zip.getInputStream(entry));
+                                    }
+                                    break;
+                                }
+                            }
                         }
                     }
+                } catch (IOException e) {
+                    e.printStackTrace();
                 }
-            }
+            }));
         }
 
+        futures.forEach(ForkJoinTask::join);
     }
 
     /**
@@ -173,7 +243,7 @@
      * Utility that returns the fully qualified binary class name from a path-like FQCN.
      * E.g. it returns android.view.View from android/view/View.
      */
-    static String internalToBinaryClassName(String className) {
+    private static String internalToBinaryClassName(String className) {
         if (className == null) {
             return null;
         } else {
@@ -181,25 +251,43 @@
         }
     }
 
+    private static boolean matchesAny(@Nullable String className, @NotNull Pattern[] patterns) {
+        for (int i = 0; i < patterns.length; i++) {
+            if (patterns[i].matcher(className).matches()) {
+                return true;
+            }
+        }
+
+        int dollarIdx = className.indexOf('$');
+        if (dollarIdx != -1) {
+            // This is an inner class, if the outer class matches, we also consider this a match
+            return matchesAny(className.substring(0, dollarIdx), patterns);
+        }
+
+        return false;
+    }
+
     /**
      * Process the "includes" arrays.
      * <p/>
      * This updates the in_out_found map.
      */
-    Map<String, ClassReader> findIncludes(Map<String, ClassReader> zipClasses)
-            throws LogAbortException {
+    private static void findIncludes(@NotNull Log log, @NotNull Pattern[] includePatterns,
+            @NotNull String[] deriveFrom, @NotNull Map<String, ClassReader> zipClasses,
+            @NotNull Consumer<Entry<String, ClassReader>> newInclude) throws FileNotFoundException {
         TreeMap<String, ClassReader> found = new TreeMap<>();
 
-        mLog.debug("Find classes to include.");
+        log.debug("Find classes to include.");
 
-        for (String s : mIncludeGlobs) {
-            findGlobs(s, zipClasses, found);
-        }
-        for (String s : mDeriveFrom) {
-            findClassesDerivingFrom(s, zipClasses, found);
+        zipClasses.entrySet().stream()
+                .filter(entry -> matchesAny(entry.getKey(), includePatterns))
+                .forEach(entry -> found.put(entry.getKey(), entry.getValue()));
+
+        for (String entry : deriveFrom) {
+            findClassesDerivingFrom(entry, zipClasses, found);
         }
 
-        return found;
+        found.entrySet().forEach(newInclude);
     }
 
 
@@ -208,47 +296,19 @@
      * If found, insert it in the in_out_found map.
      * Returns the class reader object.
      */
-    ClassReader findClass(String className, Map<String, ClassReader> zipClasses,
-            Map<String, ClassReader> inOutFound) throws LogAbortException {
+    static ClassReader findClass(String className, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws FileNotFoundException {
         ClassReader classReader = zipClasses.get(className);
         if (classReader == null) {
-            throw new LogAbortException("Class %s not found by ASM in %s",
-                    className, mOsSourceJar);
+            throw new FileNotFoundException(String.format("Class %s not found by ASM", className));
         }
 
         inOutFound.put(className, classReader);
         return classReader;
     }
 
-    /**
-     * Insert in the inOutFound map all classes found in zipClasses that match the
-     * given glob pattern.
-     * <p/>
-     * The glob pattern is not a regexp. It only accepts the "*" keyword to mean
-     * "anything but a period". The "." and "$" characters match themselves.
-     * The "**" keyword means everything including ".".
-     * <p/>
-     * Examples:
-     * <ul>
-     * <li>com.foo.* matches all classes in the package com.foo but NOT sub-packages.
-     * <li>com.foo*.*$Event matches all internal Event classes in a com.foo*.* class.
-     * </ul>
-     */
-    void findGlobs(String globPattern, Map<String, ClassReader> zipClasses,
-            Map<String, ClassReader> inOutFound) throws LogAbortException {
 
-        Pattern regexp = getPatternFromGlob(globPattern);
-
-        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
-            String class_name = entry.getKey();
-            if (regexp.matcher(class_name).matches() &&
-                    !mExcludedClasses.contains(getOuterClassName(class_name))) {
-                findClass(class_name, zipClasses, inOutFound);
-            }
-        }
-    }
-
-    Pattern getPatternFromGlob(String globPattern) {
+    static Pattern getPatternFromGlob(String globPattern) {
      // transforms the glob pattern in a regexp:
         // - escape "." with "\."
         // - replace "*" by "[^.]*"
@@ -271,11 +331,8 @@
      * determine if they are derived from the given FQCN super class name.
      * Inserts the super class and all the class objects found in the map.
      */
-    void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
-            Map<String, ClassReader> inOutFound) throws LogAbortException {
-        if (mExcludedClasses.contains(getOuterClassName(super_name))) {
-            return;
-        }
+    static void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutFound) throws FileNotFoundException {
         findClass(super_name, zipClasses, inOutFound);
 
         for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
@@ -314,16 +371,20 @@
      * Finds all dependencies for all classes in keepClasses which are also
      * listed in zipClasses. Returns a map of all the dependencies found.
      */
-    Map<String, ClassReader> findDeps(Map<String, ClassReader> zipClasses,
-            Map<String, ClassReader> inOutKeepClasses) {
+    void findDeps(Log log,
+            Map<String, ClassReader> zipClasses,
+            Map<String, ClassReader> inOutKeepClasses,
+            Consumer<Entry<String, ClassReader>> newKeep,
+            Consumer<Entry<String, ClassReader>> newDep) {
 
+        TreeMap<String, ClassReader> keep = new TreeMap<>(inOutKeepClasses);
         TreeMap<String, ClassReader> deps = new TreeMap<>();
         TreeMap<String, ClassReader> new_deps = new TreeMap<>();
         TreeMap<String, ClassReader> new_keep = new TreeMap<>();
         TreeMap<String, ClassReader> temp = new TreeMap<>();
 
         DependencyVisitor visitor = getVisitor(zipClasses,
-                inOutKeepClasses, new_keep,
+                keep, new_keep,
                 deps, new_deps);
 
         for (ClassReader cr : inOutKeepClasses.values()) {
@@ -332,15 +393,17 @@
         }
 
         while (new_deps.size() > 0 || new_keep.size() > 0) {
+            new_deps.entrySet().forEach(newDep);
+            new_keep.entrySet().forEach(newKeep);
+            keep.putAll(new_keep);
             deps.putAll(new_deps);
-            inOutKeepClasses.putAll(new_keep);
 
             temp.clear();
             temp.putAll(new_deps);
             temp.putAll(new_keep);
             new_deps.clear();
             new_keep.clear();
-            mLog.debug("Found %1$d to keep, %2$d dependencies.",
+            log.debug("Found %1$d to keep, %2$d dependencies.",
                     inOutKeepClasses.size(), deps.size());
 
             for (ClassReader cr : temp.values()) {
@@ -348,19 +411,6 @@
                 cr.accept(visitor, 0 /* flags */);
             }
         }
-
-        mLog.info("Found %1$d classes to keep, %2$d class dependencies.",
-                inOutKeepClasses.size(), deps.size());
-
-        return deps;
-    }
-
-    private String getOuterClassName(String className) {
-        int pos = className.indexOf('$');
-        if (pos > 0) {
-            return className.substring(0, pos);
-        }
-        return className;
     }
 
     // ----------------------------------
@@ -425,8 +475,7 @@
             if (mInKeep.containsKey(className) ||
                     mOutKeep.containsKey(className) ||
                     mInDeps.containsKey(className) ||
-                    mOutDeps.containsKey(className) ||
-                    mExcludedClasses.contains(getOuterClassName(className))) {
+                    mOutDeps.containsKey(className)) {
                 return;
             }
 
@@ -483,7 +532,8 @@
 
         /**
          * Considers this {@link Type}. For arrays, the element type is considered.
-         * If the type is an object, it's internal name is considered.
+         * If the type is an object, its internal name is considered. If it is a method type,
+         * iterate through the argument and return types.
          */
         public void considerType(Type t) {
             if (t != null) {
@@ -493,6 +543,12 @@
                 if (t.getSort() == Type.OBJECT) {
                     considerName(t.getInternalName());
                 }
+                if (t.getSort() == Type.METHOD) {
+                    for (Type type : t.getArgumentTypes()) {
+                        considerType(type);
+                    }
+                    considerType(t.getReturnType());
+                }
             }
         }
 
diff --git a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 395bfb8..35fc584 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -16,17 +16,18 @@
 
 package com.android.tools.layoutlib.create;
 
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.create.AsmAnalyzer.Result;
+
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
 
 import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.ListIterator;
@@ -34,8 +35,6 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -47,8 +46,6 @@
 
     /** Output logger. */
     private final Log mLog;
-    /** The path of the destination JAR to create. */
-    private final String mOsDestJar;
     /** List of classes to inject in the final JAR from _this_ archive. */
     private final Class<?>[] mInjectClasses;
     /** All classes to output as-is, except if they have native methods. */
@@ -87,12 +84,10 @@
      * Creates a new generator that can generate the output JAR with the stubbed classes.
      *
      * @param log Output logger.
-     * @param osDestJar The path of the destination JAR to create.
      * @param createInfo Creation parameters. Must not be null.
      */
-    public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) {
+    public AsmGenerator(Log log, ICreateInfo createInfo) {
         mLog = log;
-        mOsDestJar = osDestJar;
         ArrayList<Class<?>> injectedClasses =
                 new ArrayList<>(Arrays.asList(createInfo.getInjectedClasses()));
         // Search for and add anonymous inner classes also.
@@ -229,7 +224,7 @@
      * Utility that returns the internal ASM class name from a fully qualified binary class
      * name. E.g. it returns android/view/View from android.view.View.
      */
-    String binaryToInternalClassName(String className) {
+    private String binaryToInternalClassName(String className) {
         if (className == null) {
             return null;
         } else {
@@ -237,27 +232,8 @@
         }
     }
 
-    /** Sets the map of classes to output as-is, except if they have native methods */
-    public void setKeep(Map<String, ClassReader> keep) {
-        mKeep = keep;
-    }
-
-    /** Sets the map of dependencies that must be completely stubbed */
-    public void setDeps(Map<String, ClassReader> deps) {
-        mDeps = deps;
-    }
-
-    /** Sets the map of files to output as-is. */
-    public void setCopyFiles(Map<String, InputStream> copyFiles) {
-        mCopyFiles = copyFiles;
-    }
-
-    public void setRewriteMethodCallClasses(Set<String> rewriteMethodCallClasses) {
-        mReplaceMethodCallsClasses = rewriteMethodCallClasses;
-    }
-
     /** Generates the final JAR */
-    public void generate() throws IOException {
+    public Map<String, byte[]> generate() throws IOException {
         TreeMap<String, byte[]> all = new TreeMap<>();
 
         for (Class<?> clazz : mInjectClasses) {
@@ -296,28 +272,7 @@
         mLog.info("# keep classes: %d", mKeep.size());
         mLog.info("# renamed     : %d", mRenameCount);
 
-        createJar(new FileOutputStream(mOsDestJar), all);
-        mLog.info("Created JAR file %s", mOsDestJar);
-    }
-
-    /**
-     * Writes the JAR file.
-     *
-     * @param outStream The file output stream were to write the JAR.
-     * @param all The map of all classes to output.
-     * @throws IOException if an I/O error has occurred
-     */
-    void createJar(FileOutputStream outStream, Map<String,byte[]> all) throws IOException {
-        JarOutputStream jar = new JarOutputStream(outStream);
-        for (Entry<String, byte[]> entry : all.entrySet()) {
-            String name = entry.getKey();
-            JarEntry jar_entry = new JarEntry(name);
-            jar.putNextEntry(jar_entry);
-            jar.write(entry.getValue());
-            jar.closeEntry();
-        }
-        jar.flush();
-        jar.close();
+        return all;
     }
 
     /**
@@ -391,8 +346,11 @@
         if (mInjectedMethodsMap.keySet().contains(binaryNewName)) {
             cv = new InjectMethodsAdapter(cv, mInjectedMethodsMap.get(binaryNewName));
         }
-        cv = new TransformClassAdapter(mLog, Collections.emptySet(), mDeleteReturns.get(className),
-                newName, cv, stubNativesOnly);
+        cv = StubClassAdapter.builder(mLog, cv)
+                .withDeleteReturns(mDeleteReturns.get(className))
+                .withNewClassName(newName)
+                .useOnlyStubNative(stubNativesOnly)
+                .build();
 
         Set<String> delegateMethods = mDelegateMethods.get(className);
         if (delegateMethods != null && !delegateMethods.isEmpty()) {
@@ -412,6 +370,11 @@
         if (!mPromotedClasses.isEmpty()) {
             cv = new PromoteClassClassAdapter(cv, mPromotedClasses);
         }
+
+        // Make sure no class file has a version above 52 (corresponding to Java 8),
+        // so that layoutlib can be run with JDK 8.
+        cv = new ChangeFileVersionAdapter(mLog, 52, cv);
+
         cr.accept(cv, 0);
 
         return cw.toByteArray();
@@ -461,4 +424,16 @@
         return buffer.toByteArray();
     }
 
+    /**
+     * Sets the inputs for the generator phase.
+     */
+    public void setAnalysisResult(@NotNull Result analysisResult) {
+        // Map of classes to output as-is, except if they have native methods
+        mKeep = analysisResult.getFound();
+        // Map of dependencies that must be completely stubbed
+        mDeps = analysisResult.getDeps();
+        // Map of files to output as-is.
+        mCopyFiles = analysisResult.getFilesFound();
+        mReplaceMethodCallsClasses = analysisResult.getReplaceMethodCallClasses();
+    }
 }
diff --git a/create/src/com/android/tools/layoutlib/create/ChangeFileVersionAdapter.java b/create/src/com/android/tools/layoutlib/create/ChangeFileVersionAdapter.java
new file mode 100644
index 0000000..3b66367
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/ChangeFileVersionAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+
+/**
+ * Class adapter that modifies the file version of classes to be
+ * below a certain maximum version.
+ */
+public class ChangeFileVersionAdapter extends ClassVisitor {
+    private final Log mLog;
+    private final int mMaxVersion;
+
+    public ChangeFileVersionAdapter(Log logger, int maxVersion, ClassVisitor cv) {
+        super(Main.ASM_VERSION, cv);
+        mLog = logger;
+        mMaxVersion = maxVersion;
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName,
+            String[] interfaces) {
+        int classFileVersion = version;
+        if (classFileVersion > mMaxVersion) {
+            classFileVersion = mMaxVersion;
+            mLog.debug("Class %s has had its file version changed from %d to %d", name, version,
+                    classFileVersion);
+        }
+        super.visit(classFileVersion, access, name, signature, superName, interfaces);
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index e46e67f..02766e2 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -67,7 +67,7 @@
     }
 
     @Override
-    public Set<String> getExcludedClasses() {
+    public String[] getExcludedClasses() {
         String[] refactoredClasses = getJavaPkgClasses();
         int count = refactoredClasses.length / 2 + EXCLUDED_CLASSES.length;
         Set<String> excludedClasses = new HashSet<>(count);
@@ -75,7 +75,7 @@
             excludedClasses.add(refactoredClasses[i]);
         }
         excludedClasses.addAll(Arrays.asList(EXCLUDED_CLASSES));
-        return excludedClasses;
+        return excludedClasses.toArray(new String[0]);
     }
 
     @Override
@@ -118,6 +118,7 @@
     public final static String[] DELEGATE_METHODS = new String[] {
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
         "android.content.res.Resources#getAnimation",
+        "android.content.res.Resources#getAttributeSetSourceResId",
         "android.content.res.Resources#getBoolean",
         "android.content.res.Resources#getColor",
         "android.content.res.Resources#getColorStateList",
@@ -153,9 +154,12 @@
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.content.res.Resources$Theme#resolveAttributes",
         "android.content.res.AssetManager#open",
-        "android.content.res.AssetManager#newTheme",
-        "android.content.res.AssetManager#deleteTheme",
+        "android.content.res.AssetManager#nativeCreate",
+        "android.content.res.AssetManager#nativeDestroy",
+        "android.content.res.AssetManager#nativeThemeCreate",
+        "android.content.res.AssetManager#nativeThemeDestroy",
         "android.content.res.AssetManager#getAssignedPackageIdentifiers",
+        "android.content.res.AssetManager#nativeCreateIdmapsForStaticOverlaysTargetingAndroid",
         "android.content.res.TypedArray#getValueAt",
         "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
@@ -165,9 +169,9 @@
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.drawable.AdaptiveIconDrawable#<init>",
         "android.graphics.FontFamily#addFont",
-        "android.graphics.Typeface#buildSystemFallback",
         "android.graphics.Typeface#create",
-        "android.graphics.Typeface#createFontFamily",
+        "android.graphics.Typeface$Builder#createAssetUid",
+        "android.graphics.fonts.SystemFonts#buildSystemFallback",
         "android.os.Binder#getNativeBBinderHolder",
         "android.os.Binder#getNativeFinalizer",
         "android.os.Handler#sendMessageAtTime",
@@ -181,6 +185,7 @@
         "android.view.Display#updateDisplayInfoLocked",
         "android.view.Display#getWindowManager",
         "android.view.HandlerActionQueue#postDelayed",
+        "android.view.LayoutInflater#initPrecompiledViews",
         "android.view.LayoutInflater#rInflate",
         "android.view.LayoutInflater#parseInclude",
         "android.view.View#draw",
@@ -190,44 +195,54 @@
         "android.view.View#isInEditMode",
         "android.view.ViewRootImpl#isInTouchMode",
         "android.view.WindowManagerGlobal#getWindowManagerService",
-        "android.view.inputmethod.InputMethodManager#getInstance",
+        "android.view.inputmethod.InputMethodManager#isInEditMode",
         "android.view.MenuInflater#registerMenu",
-        "android.view.RenderNode#getMatrix",
-        "android.view.RenderNode#nCreate",
-        "android.view.RenderNode#nGetNativeFinalizer",
-        "android.view.RenderNode#nSetElevation",
-        "android.view.RenderNode#nGetElevation",
-        "android.view.RenderNode#nSetTranslationX",
-        "android.view.RenderNode#nGetTranslationX",
-        "android.view.RenderNode#nSetTranslationY",
-        "android.view.RenderNode#nGetTranslationY",
-        "android.view.RenderNode#nSetTranslationZ",
-        "android.view.RenderNode#nGetTranslationZ",
-        "android.view.RenderNode#nSetRotation",
-        "android.view.RenderNode#nGetRotation",
-        "android.view.RenderNode#nSetLeft",
-        "android.view.RenderNode#nSetTop",
-        "android.view.RenderNode#nSetRight",
-        "android.view.RenderNode#nSetBottom",
-        "android.view.RenderNode#nSetLeftTopRightBottom",
-        "android.view.RenderNode#nSetPivotX",
-        "android.view.RenderNode#nGetPivotX",
-        "android.view.RenderNode#nSetPivotY",
-        "android.view.RenderNode#nGetPivotY",
-        "android.view.RenderNode#nSetScaleX",
-        "android.view.RenderNode#nGetScaleX",
-        "android.view.RenderNode#nSetScaleY",
-        "android.view.RenderNode#nGetScaleY",
-        "android.view.RenderNode#nIsPivotExplicitlySet",
+        "android.graphics.RenderNode#getMatrix",
+        "android.graphics.RenderNode#nCreate",
+        "android.graphics.RenderNode#nGetNativeFinalizer",
+        "android.graphics.RenderNode#nSetElevation",
+        "android.graphics.RenderNode#nGetElevation",
+        "android.graphics.RenderNode#nSetTranslationX",
+        "android.graphics.RenderNode#nGetTranslationX",
+        "android.graphics.RenderNode#nSetTranslationY",
+        "android.graphics.RenderNode#nGetTranslationY",
+        "android.graphics.RenderNode#nSetTranslationZ",
+        "android.graphics.RenderNode#nGetTranslationZ",
+        "android.graphics.RenderNode#nSetRotation",
+        "android.graphics.RenderNode#nGetRotation",
+        "android.graphics.RenderNode#nSetLeft",
+        "android.graphics.RenderNode#nSetTop",
+        "android.graphics.RenderNode#nSetRight",
+        "android.graphics.RenderNode#nSetBottom",
+        "android.graphics.RenderNode#nSetLeftTopRightBottom",
+        "android.graphics.RenderNode#nSetPivotX",
+        "android.graphics.RenderNode#nGetPivotX",
+        "android.graphics.RenderNode#nSetPivotY",
+        "android.graphics.RenderNode#nGetPivotY",
+        "android.graphics.RenderNode#nSetScaleX",
+        "android.graphics.RenderNode#nGetScaleX",
+        "android.graphics.RenderNode#nSetScaleY",
+        "android.graphics.RenderNode#nGetScaleY",
+        "android.graphics.RenderNode#nIsPivotExplicitlySet",
+        "android.provider.DeviceConfig#getBoolean",
+        "android.provider.DeviceConfig#getFloat",
+        "android.provider.DeviceConfig#getInt",
+        "android.provider.DeviceConfig#getLong",
+        "android.provider.DeviceConfig#getString",
         "android.view.PointerIcon#loadResource",
+        "android.view.PointerIcon#registerDisplayListener",
+        "android.view.SurfaceControl#nativeCreateTransaction",
+        "android.view.SurfaceControl#nativeGetNativeTransactionFinalizer",
         "android.view.ViewGroup#drawChild",
         "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
         "com.android.internal.util.XmlUtils#convertValueToInt",
+        "dalvik.system.VMRuntime#getNotifyNativeInterval",
         "dalvik.system.VMRuntime#newUnpaddedArray",
         "libcore.io.MemoryMappedFile#mmapRO",
         "libcore.io.MemoryMappedFile#close",
         "libcore.io.MemoryMappedFile#bigEndianIterator",
         "libcore.util.NativeAllocationRegistry#applyFreeFunction",
+        "libcore.util.NativeAllocationRegistry#registerNativeAllocation",
     };
 
     /**
@@ -239,10 +254,13 @@
         "android.graphics.Bitmap",
         "android.graphics.BitmapFactory",
         "android.graphics.BitmapShader",
+        "android.graphics.BlendModeColorFilter",
         "android.graphics.BlurMaskFilter",
         "android.graphics.Canvas",
+        "android.graphics.Color",
         "android.graphics.ColorFilter",
         "android.graphics.ColorMatrixColorFilter",
+        "android.graphics.ColorSpace$Rgb",
         "android.graphics.ComposePathEffect",
         "android.graphics.ComposeShader",
         "android.graphics.CornerPathEffect",
@@ -271,10 +289,13 @@
         "android.graphics.Typeface",
         "android.graphics.drawable.AnimatedVectorDrawable",
         "android.graphics.drawable.VectorDrawable",
+        "android.graphics.fonts.Font$Builder",
+        "android.graphics.fonts.FontFamily$Builder",
+        "android.graphics.text.MeasuredText",
+        "android.graphics.text.MeasuredText$Builder",
+        "android.graphics.text.LineBreaker",
         "android.os.SystemClock",
         "android.os.SystemProperties",
-        "android.text.MeasuredParagraph",
-        "android.text.StaticLayout",
         "android.util.PathParser",
         "android.view.Display",
         "com.android.internal.util.VirtualRefBasePtr",
@@ -321,7 +342,9 @@
     private final static String[] EXCLUDED_CLASSES =
         new String[] {
             "android.preference.PreferenceActivity",
+            "java.**",
             "org.kxml2.io.KXmlParser",
+            "sun.**",
         };
 
     /**
@@ -332,8 +355,17 @@
         "android.graphics.drawable.VectorDrawable#mVectorState",
         "android.view.Choreographer#mLastFrameTimeNanos",
         "android.graphics.FontFamily#mBuilderPtr",
+        "android.graphics.Typeface#DEFAULT_FAMILY",
         "android.graphics.Typeface#sDynamicTypefaceCache",
         "android.graphics.drawable.AdaptiveIconDrawable#sMask",
+        "android.animation.PropertyValuesHolder#sSetterPropertyMap",
+        "android.animation.PropertyValuesHolder#sGetterPropertyMap",
+        "android.animation.PropertyValuesHolder$IntPropertyValuesHolder#sJNISetterPropertyMap",
+        "android.animation.PropertyValuesHolder$FloatPropertyValuesHolder#sJNISetterPropertyMap",
+        "android.animation.PropertyValuesHolder$MultiFloatValuesHolder#sJNISetterPropertyMap",
+        "android.animation.PropertyValuesHolder$MultiIntValuesHolder#sJNISetterPropertyMap",
+        "libcore.util.NativeAllocationRegistry#freeFunction",
+        "libcore.util.NativeAllocationRegistry#size",
     };
 
     /**
@@ -341,6 +373,8 @@
      * if possible.
      */
     private final static String[] PROMOTED_CLASSES = new String[] {
+        "libcore.util.NativeAllocationRegistry$CleanerRunner",
+        "libcore.util.NativeAllocationRegistry$CleanerThunk",
     };
 
     /**
diff --git a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index eca1c07..42d5727 100644
--- a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -77,7 +77,7 @@
      */
     String[] getJavaPkgClasses();
 
-    Set<String> getExcludedClasses();
+    String[] getExcludedClasses();
 
     /**
      * Returns a list of fields which should be promoted to public visibility. The array values
diff --git a/create/src/com/android/tools/layoutlib/create/JarUtil.java b/create/src/com/android/tools/layoutlib/create/JarUtil.java
new file mode 100644
index 0000000..2836642
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/JarUtil.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Function;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+public class JarUtil {
+    private JarUtil() {
+    }
+
+    /**
+     * Writes the JAR file.
+     *
+     * @param outStream The file output stream were to write the JAR.
+     * @param all The map of all classes to output.
+     * @param transform Transform to apply to the class files
+     *
+     * @throws IOException if an I/O error has occurred
+     */
+    public static void createJar(@NotNull FileOutputStream outStream,
+            @NotNull Map<String, byte[]> all, @NotNull Function<byte[], byte[]> transform)
+            throws IOException {
+        JarOutputStream jar = new JarOutputStream(outStream);
+        for (Entry<String, byte[]> entry : all.entrySet()) {
+            String name = entry.getKey();
+            JarEntry jar_entry = new JarEntry(name);
+            jar.putNextEntry(jar_entry);
+            if (name.endsWith(".class")) {
+                jar.write(transform.apply(entry.getValue()));
+            } else {
+                // This is just a file,
+                jar.write(entry.getValue());
+            }
+            jar.closeEntry();
+        }
+        jar.flush();
+        jar.close();
+    }
+
+    /**
+     * Writes the JAR file.
+     *
+     * @param outStream The file output stream were to write the JAR.
+     * @param all The map of all classes to output.
+     *
+     * @throws IOException if an I/O error has occurred
+     */
+    public static void createJar(@NotNull FileOutputStream outStream,
+            @NotNull Map<String, byte[]> all) throws IOException {
+        createJar(outStream, all, Function.identity());
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/Main.java b/create/src/com/android/tools/layoutlib/create/Main.java
index 74eed83..4636a1c 100644
--- a/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/create/src/com/android/tools/layoutlib/create/Main.java
@@ -16,13 +16,22 @@
 
 package com.android.tools.layoutlib.create;
 
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.Opcodes;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.common.io.Files;
 
 
 /**
@@ -49,14 +58,15 @@
  */
 public class Main {
 
-    public static class Options {
-        public boolean listAllDeps = false;
-        public boolean listOnlyMissingDeps = false;
+    private static class Options {
+        private boolean listAllDeps = false;
+        private boolean listOnlyMissingDeps = false;
+        private boolean createStubLib = false;
     }
 
     public static final int ASM_VERSION = Opcodes.ASM6;
 
-    public static final Options sOptions = new Options();
+    private static final Options sOptions = new Options();
 
     public static void main(String[] args) {
 
@@ -66,7 +76,7 @@
         String[] osDestJar = { null };
 
         if (!processArgs(log, args, osJarPath, osDestJar)) {
-            log.error("Usage: layoutlib_create [-v] output.jar input.jar ...");
+            log.error("Usage: layoutlib_create [-v] [--create-stub] output.jar input.jar ...");
             log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ...");
             System.exit(1);
         }
@@ -90,10 +100,9 @@
 
         try {
             CreateInfo info = new CreateInfo();
-            Set<String> excludeClasses = info.getExcludedClasses();
-            AsmGenerator agen = new AsmGenerator(log, osDestJar, info);
+            AsmGenerator agen = new AsmGenerator(log, info);
 
-            AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
+            AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath,
                     new String[] {                          // derived from
                         "android.view.View",
                         "android.app.Fragment"
@@ -126,13 +135,33 @@
                         "com.android.internal.transition.EpicenterTranslateClipReveal",
                         "com.android.internal.graphics.drawable.AnimationScaleListDrawable",
                     },
-                    excludeClasses,
+                    info.getExcludedClasses(),
                     new String[] {
                         "com/android/i18n/phonenumbers/data/*",
                         "android/icu/impl/data/**"
                     });
-            aa.analyze();
-            agen.generate();
+            agen.setAnalysisResult(aa.analyze());
+
+            Map<String, byte[]> outputClasses = agen.generate();
+            JarUtil.createJar(new FileOutputStream(osDestJar), outputClasses);
+            log.info("Created JAR file %s", osDestJar);
+
+            if (sOptions.createStubLib) {
+                File osDestJarFile = new File(osDestJar);
+                String extension = Files.getFileExtension(osDestJarFile.getName());
+                if (!extension.isEmpty()) {
+                    extension = '.' + extension;
+                }
+                String stubDestJarFile = osDestJarFile.getParent() + File.separatorChar +
+                        Files.getNameWithoutExtension(osDestJarFile.getName()) + "-stubs" +
+                        extension;
+
+                Map<String, byte[]> toStubClasses = outputClasses.entrySet().stream().filter(entry -> entry.getKey().startsWith("android/")).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+                JarUtil.createJar(new FileOutputStream(stubDestJarFile), toStubClasses,
+                        input -> StubClassAdapter.stubClass(log, input));
+                log.info("Created stub JAR file %s", stubDestJarFile);
+            }
+
 
             // Throw an error if any class failed to get renamed by the generator
             //
@@ -159,8 +188,6 @@
             return 0;
         } catch (IOException e) {
             log.exception(e, "Failed to load jar");
-        } catch (LogAbortException e) {
-            e.error(log);
         }
 
         return 1;
@@ -201,6 +228,8 @@
             } else if (s.equals("--missing-deps")) {
                 sOptions.listOnlyMissingDeps = true;
                 needs_dest = false;
+            } else if (s.equals("--create-stub")) {
+                sOptions.createStubLib = true;
             } else if (!s.startsWith("-")) {
                 if (needs_dest && osDestJar[0] == null) {
                     osDestJar[0] = s;
diff --git a/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index bf94415..2967b05 100644
--- a/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -73,33 +73,10 @@
             }
         });
 
-        // Case 2: java.util.Locale.toLanguageTag() and java.util.Locale.getScript()
-        METHOD_REPLACERS.add(new MethodReplacer() {
-
-            private final String LOCALE_TO_STRING =
-                    Type.getMethodDescriptor(STRING, Type.getType(Locale.class));
-
-            @Override
-            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
-                return JAVA_LOCALE_CLASS.equals(owner) && "()Ljava/lang/String;".equals(desc) &&
-                        ("toLanguageTag".equals(name) || "getScript".equals(name));
-            }
-
-            @Override
-            public void replace(MethodInformation mi) {
-                mi.opcode = Opcodes.INVOKESTATIC;
-                mi.owner = ANDROID_LOCALE_CLASS;
-                mi.desc = LOCALE_TO_STRING;
-            }
-        });
-
-        // Case 3: java.util.Locale.adjustLanguageCode() or java.util.Locale.forLanguageTag() or
-        // java.util.Locale.getDefault()
+        // Case 2: java.util.Locale.adjustLanguageCode() or java.util.Locale.getDefault()
         METHOD_REPLACERS.add(new MethodReplacer() {
 
             private final String STRING_TO_STRING = Type.getMethodDescriptor(STRING, STRING);
-            private final String STRING_TO_LOCALE = Type.getMethodDescriptor(
-                    Type.getType(Locale.class), STRING);
             private final String VOID_TO_LOCALE =
                     Type.getMethodDescriptor(Type.getType(Locale.class));
 
@@ -107,7 +84,6 @@
             public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
                 return JAVA_LOCALE_CLASS.equals(owner) &&
                         ("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) ||
-                        "forLanguageTag".equals(name) && desc.equals(STRING_TO_LOCALE) ||
                         "getDefault".equals(name) && desc.equals(VOID_TO_LOCALE));
             }
 
@@ -117,7 +93,7 @@
             }
         });
 
-        // Case 4: java.lang.System.log?()
+        // Case 3: java.lang.System.log?()
         METHOD_REPLACERS.add(new MethodReplacer() {
             @Override
             public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
@@ -134,7 +110,7 @@
             }
         });
 
-        // Case 5: java.lang.System time calls
+        // Case 4: java.lang.System time calls
         METHOD_REPLACERS.add(new MethodReplacer() {
             @Override
             public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
@@ -160,7 +136,7 @@
             }
         });
 
-        // Case 6: java.util.LinkedHashMap.eldest()
+        // Case 5: java.util.LinkedHashMap.eldest()
         METHOD_REPLACERS.add(new MethodReplacer() {
 
             private final String VOID_TO_MAP_ENTRY =
@@ -183,7 +159,7 @@
             }
         });
 
-        // Case 7: android.content.Context.getClassLoader() in LayoutInflater
+        // Case 6: android.content.Context.getClassLoader() in LayoutInflater
         METHOD_REPLACERS.add(new MethodReplacer() {
             // When LayoutInflater asks for a class loader, we must return the class loader that
             // cannot return app's custom views/classes. This is so that in case of any failure
diff --git a/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/StubCallMethodAdapter.java
similarity index 98%
rename from create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
rename to create/src/com/android/tools/layoutlib/create/StubCallMethodAdapter.java
index 4ba7237..f4cfc1d 100644
--- a/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/StubCallMethodAdapter.java
@@ -27,7 +27,7 @@
  * This method adapter rewrites a method by discarding the original code and generating
  * a stub depending on the return type. Original annotations are passed along unchanged.
  */
-class StubMethodAdapter extends MethodVisitor {
+class StubCallMethodAdapter extends MethodVisitor {
 
     private static final String CONSTRUCTOR = "<init>";
     private static final String CLASS_INIT = "<clinit>";
@@ -48,7 +48,7 @@
     private final boolean mIsStatic;
     private final boolean mIsNative;
 
-    public StubMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
+    public StubCallMethodAdapter(MethodVisitor mv, String methodName, Type returnType,
             String invokeSignature, boolean isStatic, boolean isNative) {
         super(Main.ASM_VERSION);
         mParentVisitor = mv;
diff --git a/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java b/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
new file mode 100644
index 0000000..e1d2df2
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Class adapter that can stub some or all of the methods of the class.
+ */
+class StubClassAdapter extends ClassVisitor {
+    public interface MethodVisitorFactory {
+        @NotNull
+        MethodVisitor create(@NotNull MethodVisitor mv,
+                @NotNull String methodName,
+                @NotNull Type returnType,
+                @NotNull String invokeSignature,
+                boolean isStatic, boolean isNative);
+    }
+
+    public static class Builder {
+        private Log mLogger;
+        private Set<String> mDeleteReturns;
+        private String mClassName;
+        private ClassVisitor mCv;
+        private boolean mStubNativesOnly;
+        private boolean mRemoveStaticInitializers;
+        private boolean mRemovePrivates;
+        private MethodVisitorFactory mMethodVisitorFactory;
+
+        private Builder(@NotNull Log log, @NotNull ClassVisitor classVisitor) {
+            mLogger = log;
+            mCv = classVisitor;
+        }
+
+        @NotNull
+        public Builder withDeleteReturns(@Nullable Set<String> deleteReturns) {
+            mDeleteReturns = deleteReturns;
+            return this;
+        }
+
+        @NotNull
+        public Builder withNewClassName(@Nullable String className) {
+            mClassName = className;
+            return this;
+        }
+
+        public Builder useOnlyStubNative(boolean stubNativesOnly) {
+            mStubNativesOnly = stubNativesOnly;
+            return this;
+        }
+
+        @NotNull
+        public Builder withMethodVisitorFactory(@Nullable MethodVisitorFactory factory) {
+            mMethodVisitorFactory = factory;
+            return this;
+        }
+
+        @NotNull
+        public Builder removePrivates() {
+            mRemovePrivates = true;
+            return this;
+        }
+
+        @NotNull
+        public Builder removeStaticInitializers() {
+            mRemoveStaticInitializers = true;
+            return this;
+        }
+
+        public StubClassAdapter build() {
+            return new StubClassAdapter(mLogger,
+                    mDeleteReturns != null ? mDeleteReturns : Collections.emptySet(),
+                    mClassName,
+                    mCv,
+                    mMethodVisitorFactory != null ? mMethodVisitorFactory : StubCallMethodAdapter::new,
+                    mStubNativesOnly,
+                    mRemovePrivates,
+                    mRemoveStaticInitializers);
+        }
+    }
+
+    /** True if all methods should be stubbed, false if only native ones must be stubbed. */
+    private final boolean mStubAll;
+    /** True if the class is an interface. */
+    private boolean mIsInterface;
+    private final String mClassName;
+    private final Log mLog;
+    private final Set<String> mDeleteReturns;
+    private final MethodVisitorFactory mMethodVisitorFactory;
+    private final boolean mRemovePrivates;
+    private final boolean mRemoveStaticInitalizers;
+
+
+    @NotNull
+    public static StubClassAdapter.Builder builder(@NotNull Log log,
+            @NotNull ClassVisitor classVisitor) {
+        return new Builder(log, classVisitor);
+    }
+    /**
+     * Creates a new class adapter that will stub some or all methods.
+     * @param deleteReturns list of types that trigger the deletion of methods returning them.
+     * @param className Optional new name for the class being modified
+     * @param cv The parent class writer visitor
+     * @param stubNativesOnly True if only native methods should be stubbed. False if all
+     * @param removePrivates If true, all private methods and fields will be removed
+     * @param removeStaticInitializers If true, static initializers will be removed
+     */
+    private StubClassAdapter(@NotNull Log logger,
+            @NotNull Set<String> deleteReturns, @Nullable String className,
+            @NotNull ClassVisitor cv, @NotNull MethodVisitorFactory methodVisitorFactory,
+            boolean stubNativesOnly, boolean removePrivates, boolean removeStaticInitializers) {
+        super(Main.ASM_VERSION, cv);
+        mLog = logger;
+        mClassName = className;
+        mStubAll = !stubNativesOnly;
+        mIsInterface = false;
+        mDeleteReturns = deleteReturns;
+        mMethodVisitorFactory = methodVisitorFactory;
+        mRemovePrivates = removePrivates;
+        mRemoveStaticInitalizers = removeStaticInitializers;
+    }
+
+    /**
+     * Utility method that receives a class in serialized form and returns the stubbed version.
+     */
+    @VisibleForTesting
+    @NotNull
+    static byte[] stubClass(@NotNull Log log, @NotNull byte[] bytes,
+            @Nullable String newName) {
+        ClassReader classReader = new ClassReader(bytes);
+        ClassWriter classWriter = new ClassWriter(0);
+
+        // We replace every method with a Stub exception throw and remove all private
+        // methods and static initializers since we only care about the interfaces.
+        ClassVisitor cv = StubClassAdapter.builder(log, classWriter)
+                .withNewClassName(newName)
+                .withMethodVisitorFactory(StubExceptionMethodAdapter::new)
+                .removePrivates()
+                .removeStaticInitializers()
+                .build();
+
+        classReader.accept(cv, 0);
+
+        return classWriter.toByteArray();
+    }
+
+    /**
+     * Utility method that receives a class in serialized form and returns the stubbed version.
+     */
+    @NotNull
+    public static byte[] stubClass(@NotNull Log log, @NotNull byte[] bytes) {
+        return stubClass(log, bytes, null);
+    }
+
+    /* Visits the class header. */
+    @Override
+    public void visit(int version, int access, String name,
+            String signature, String superName, String[] interfaces) {
+
+        if (mClassName != null) {
+            // This class might be being renamed.
+            name = mClassName;
+        }
+
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    /* Visits the header of an inner class. */
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+        // note: leave abstract classes as such
+        // don't try to implement stub for interfaces
+
+        super.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    /* Visits a method. */
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        if (mRemovePrivates && (access & Opcodes.ACC_PRIVATE) != 0) {
+            return null;
+        }
+
+        if (mRemoveStaticInitalizers && "<clinit>".equals(name)) {
+            return null;
+        }
+
+        if (mDeleteReturns != null) {
+            Type t = Type.getReturnType(desc);
+            if (t.getSort() == Type.OBJECT) {
+                String returnType = t.getInternalName();
+                if (returnType != null) {
+                    if (mDeleteReturns.contains(returnType)) {
+                        return null;
+                    }
+                }
+            }
+        }
+
+        String methodSignature = mClassName != null ?
+                mClassName.replace('/', '.') + "#" + name :
+                signature;
+
+        // remove final
+        access = access & ~Opcodes.ACC_FINAL;
+
+        // stub this method if they are all to be stubbed or if it is a native method
+        // and don't try to stub interfaces nor abstract non-native methods.
+        if (!mIsInterface &&
+            ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) &&
+            (mStubAll ||
+             (access & Opcodes.ACC_NATIVE) != 0)) {
+
+            boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+            boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+            // remove abstract, final and native
+            access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
+
+            String invokeSignature = methodSignature + desc;
+            mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
+
+            MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+            return mMethodVisitorFactory.create(mw, name, returnType(desc), invokeSignature,
+                    isStatic, isNative);
+
+        } else {
+            mLog.debug("  Keep: %s %s", name, desc);
+            return super.visitMethod(access, name, desc, signature, exceptions);
+        }
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc, String signature,
+            Object value) {
+        if (mRemovePrivates && (access & Opcodes.ACC_PRIVATE) != 0) {
+            return null;
+        }
+
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+    /**
+     * Extracts the return {@link Type} of this descriptor.
+     */
+    private static Type returnType(String desc) {
+        if (desc != null) {
+            try {
+                return Type.getReturnType(desc);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // ignore, not a valid type.
+            }
+        }
+        return null;
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java
new file mode 100644
index 0000000..73a57a2
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+
+/**
+ * {@link MethodVisitor} that replaces the method the implementation of the method with
+ * <code>
+ * throw new RuntimeException("Stub!");
+ * </code>
+ */
+public class StubExceptionMethodAdapter extends MethodVisitor {
+    private final MethodVisitor mParentVisitor;
+
+    public StubExceptionMethodAdapter(@NotNull MethodVisitor mv, @NotNull String methodName,
+            @NotNull Type returnType, @NotNull String invokeSignature, boolean isStatic,
+            boolean isNative) {
+        super(Main.ASM_VERSION, null);
+
+        mParentVisitor = mv;
+    }
+
+    @Override
+    public void visitCode() {
+        mParentVisitor.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException");
+        mParentVisitor.visitInsn(Opcodes.DUP);
+        mParentVisitor.visitLdcInsn("Stub!");
+        mParentVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
+                "<init>", "(Ljava" + "/lang/String;)V", false);
+        mParentVisitor.visitInsn(Opcodes.ATHROW);
+        mParentVisitor.visitMaxs(3, 1);
+        mParentVisitor.visitEnd();
+
+    }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
deleted file mode 100644
index a28ae69..0000000
--- a/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.layoutlib.create;
-
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-
-import java.util.Set;
-
-/**
- * Class adapter that can stub some or all of the methods of the class.
- */
-class TransformClassAdapter extends ClassVisitor {
-
-    /** True if all methods should be stubbed, false if only native ones must be stubbed. */
-    private final boolean mStubAll;
-    /** True if the class is an interface. */
-    private boolean mIsInterface;
-    private final String mClassName;
-    private final Log mLog;
-    private final Set<String> mStubMethods;
-    private Set<String> mDeleteReturns;
-
-    /**
-     * Creates a new class adapter that will stub some or all methods.
-     * @param stubMethods  list of method signatures to always stub out
-     * @param deleteReturns list of types that trigger the deletion of methods returning them.
-     * @param className The name of the class being modified
-     * @param cv The parent class writer visitor
-     * @param stubNativesOnly True if only native methods should be stubbed. False if all
-     *                        methods should be stubbed.
-     */
-    public TransformClassAdapter(Log logger, Set<String> stubMethods,
-            Set<String> deleteReturns, String className, ClassVisitor cv,
-            boolean stubNativesOnly) {
-        super(Main.ASM_VERSION, cv);
-        mLog = logger;
-        mStubMethods = stubMethods;
-        mClassName = className;
-        mStubAll = !stubNativesOnly;
-        mIsInterface = false;
-        mDeleteReturns = deleteReturns;
-    }
-
-    /* Visits the class header. */
-    @Override
-    public void visit(int version, int access, String name,
-            String signature, String superName, String[] interfaces) {
-
-        // This class might be being renamed.
-        name = mClassName;
-
-        // remove final
-        access = access & ~Opcodes.ACC_FINAL;
-        // note: leave abstract classes as such
-        // don't try to implement stub for interfaces
-
-        mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
-        super.visit(version, access, name, signature, superName, interfaces);
-    }
-
-    /* Visits the header of an inner class. */
-    @Override
-    public void visitInnerClass(String name, String outerName, String innerName, int access) {
-        // remove final
-        access = access & ~Opcodes.ACC_FINAL;
-        // note: leave abstract classes as such
-        // don't try to implement stub for interfaces
-
-        super.visitInnerClass(name, outerName, innerName, access);
-    }
-
-    /* Visits a method. */
-    @Override
-    public MethodVisitor visitMethod(int access, String name, String desc,
-            String signature, String[] exceptions) {
-
-        if (mDeleteReturns != null) {
-            Type t = Type.getReturnType(desc);
-            if (t.getSort() == Type.OBJECT) {
-                String returnType = t.getInternalName();
-                if (returnType != null) {
-                    if (mDeleteReturns.contains(returnType)) {
-                        return null;
-                    }
-                }
-            }
-        }
-
-        String methodSignature = mClassName.replace('/', '.') + "#" + name;
-
-        // remove final
-        access = access & ~Opcodes.ACC_FINAL;
-
-        // stub this method if they are all to be stubbed or if it is a native method
-        // and don't try to stub interfaces nor abstract non-native methods.
-        if (!mIsInterface &&
-            ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) != Opcodes.ACC_ABSTRACT) &&
-            (mStubAll ||
-             (access & Opcodes.ACC_NATIVE) != 0) ||
-             mStubMethods.contains(methodSignature)) {
-
-            boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
-            boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
-
-            // remove abstract, final and native
-            access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
-
-            String invokeSignature = methodSignature + desc;
-            mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
-
-            MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
-            return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature,
-                    isStatic, isNative);
-
-        } else {
-            mLog.debug("  Keep: %s %s", name, desc);
-            return super.visitMethod(access, name, desc, signature, exceptions);
-        }
-    }
-
-    /**
-     * Extracts the return {@link Type} of this descriptor.
-     */
-    Type returnType(String desc) {
-        if (desc != null) {
-            try {
-                return Type.getReturnType(desc);
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // ignore, not a valid type.
-            }
-        }
-        return null;
-    }
-}
diff --git a/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index f86917a..21fc212 100644
--- a/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -18,8 +18,8 @@
 package com.android.tools.layoutlib.create;
 
 import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
+import com.android.tools.layoutlib.create.AsmAnalyzer.Result;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.objectweb.asm.ClassReader;
 
@@ -28,8 +28,8 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.TreeMap;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -40,45 +40,50 @@
  * Unit tests for some methods of {@link AsmAnalyzer}.
  */
 public class AsmAnalyzerTest {
+    private static final List<String> MOCK_ANDROID_JAR;
+    private static final String[] DEFAULT_EXCLUDES = new String[]{"notjava.lang.JavaClass"};
+    private static final String[] DEFAULT_INCLUDE_FILES = new String[]{"mock_android/data/data*"};
 
-    private MockLog mLog;
-    private ArrayList<String> mOsJarPath;
-    private AsmAnalyzer mAa;
+    static {
+        List<String> mockJar = new ArrayList<>();
+        URL url = AsmAnalyzerTest.class.getClassLoader().getResource("data/mock_android.jar");
+        assert url != null : "Unable to locate mock_android.jar";
+        mockJar.add(url.getFile());
+        MOCK_ANDROID_JAR = Collections.unmodifiableList(mockJar);
+    }
 
-    @Before
-    public void setUp() throws Exception {
-        mLog = new MockLog();
-        URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
-
-        mOsJarPath = new ArrayList<>();
-        //noinspection ConstantConditions
-        mOsJarPath.add(url.getFile());
-
-        Set<String> excludeClasses = Collections.singleton("java.lang.JavaClass");
-
-        String[] includeFiles = new String[]{"mock_android/data/data*"};
-        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */, null /* deriveFrom */,
-                null /* includeGlobs */, excludeClasses, includeFiles);
+    private static AsmAnalyzer getDefaultAnalyzer() {
+        MockLog log = new MockLog();
+        return new AsmAnalyzer(log, MOCK_ANDROID_JAR, null ,
+                null /* includeGlobs */, DEFAULT_EXCLUDES, DEFAULT_INCLUDE_FILES);
     }
 
     @Test
     public void testParseZip() throws IOException {
-
         Map<String, ClassReader> map = new TreeMap<>();
         Map<String, InputStream> filesFound = new TreeMap<>();
 
-        mAa.parseZip(mOsJarPath, map, filesFound);
+        getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, map, filesFound);
 
         assertArrayEquals(new String[] {
-                "java.lang.JavaClass",
+                "mock_android.dummy.DummyClass",
                 "mock_android.dummy.InnerTest",
+                "mock_android.dummy.InnerTest$1",
                 "mock_android.dummy.InnerTest$DerivingClass",
                 "mock_android.dummy.InnerTest$MyGenerics1",
                 "mock_android.dummy.InnerTest$MyIntEnum",
                 "mock_android.dummy.InnerTest$MyStaticInnerClass",
                 "mock_android.dummy.InnerTest$NotStaticInner1",
                 "mock_android.dummy.InnerTest$NotStaticInner2",
+                "mock_android.dummy.subpackage.SubpackageClassA",
+                "mock_android.dummy.subpackage.SubpackageClassB",
+                "mock_android.dummy.subpackage.SubpackageClassC",
+                "mock_android.dummy.subpackage.SubpackageClassC$InnerClass",
+                "mock_android.dummy.subpackage.SubpackageClassC$StaticInnerClass",
+                "mock_android.dummy2.DummyClass",
+                "mock_android.dummy2.keep.DoNotRemove",
                 "mock_android.util.EmptyArray",
+                "mock_android.util.NotNeeded",
                 "mock_android.view.View",
                 "mock_android.view.ViewGroup",
                 "mock_android.view.ViewGroup$LayoutParams",
@@ -86,7 +91,8 @@
                 "mock_android.widget.LinearLayout",
                 "mock_android.widget.LinearLayout$LayoutParams",
                 "mock_android.widget.TableLayout",
-                "mock_android.widget.TableLayout$LayoutParams"
+                "mock_android.widget.TableLayout$LayoutParams",
+                "notjava.lang.JavaClass",
             },
             map.keySet().toArray());
         assertArrayEquals(new String[] {"mock_android/data/dataFile"},
@@ -94,15 +100,14 @@
     }
 
     @Test
-    public void testFindClass() throws IOException, LogAbortException {
-
+    public void testFindClass() throws IOException {
         Map<String, ClassReader> zipClasses = new TreeMap<>();
         Map<String, InputStream> filesFound = new TreeMap<>();
 
-        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
+        getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, zipClasses, filesFound);
         TreeMap<String, ClassReader> found = new TreeMap<>();
 
-        ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
+        ClassReader cr = AsmAnalyzer.findClass("mock_android.view.ViewGroup$LayoutParams",
                 zipClasses, found);
 
         assertNotNull(cr);
@@ -113,78 +118,46 @@
     }
 
     @Test
-    public void testFindGlobs() throws IOException, LogAbortException {
-
-        Map<String, ClassReader> zipClasses = new TreeMap<>();
-        Map<String, InputStream> filesFound = new TreeMap<>();
-
-        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
-        TreeMap<String, ClassReader> found = new TreeMap<>();
-
-        // this matches classes, a package match returns nothing
-        found.clear();
-        mAa.findGlobs("mock_android.view", zipClasses, found);
-
-        assertArrayEquals(new String[] { },
-            found.keySet().toArray());
-
-        // a complex glob search. * is a search pattern that matches names, not dots
-        mAa.findGlobs("mock_android.*.*Group$*Layout*", zipClasses, found);
-
+    public void testInclude() throws IOException {
+        AsmAnalyzer analyzer = new AsmAnalyzer(new MockLog(), MOCK_ANDROID_JAR, null,
+                new String[] {
+                    "mock_android.util.EmptyArray", // Single class select
+                    "mock_android.dummy.**", // Multi package select
+                    "mock_android.dummy2.*", // Exclude subpackages select
+                },
+                DEFAULT_EXCLUDES,
+                DEFAULT_INCLUDE_FILES);
+        Result result = analyzer.analyze();
         assertArrayEquals(new String[] {
-                "mock_android.view.ViewGroup$LayoutParams",
-                "mock_android.view.ViewGroup$MarginLayoutParams"
-            },
-            found.keySet().toArray());
-
-        // a complex glob search. ** is a search pattern that matches names including dots
-        mAa.findGlobs("mock_android.**Group*", zipClasses, found);
-
-        assertArrayEquals(new String[] {
-                "mock_android.view.ViewGroup",
-                "mock_android.view.ViewGroup$LayoutParams",
-                "mock_android.view.ViewGroup$MarginLayoutParams"
-            },
-            found.keySet().toArray());
-
-        // matches a single class
-        found.clear();
-        mAa.findGlobs("mock_android.view.View", zipClasses, found);
-
-        assertArrayEquals(new String[] {
-                "mock_android.view.View"
-            },
-            found.keySet().toArray());
-
-        // matches everyting inside the given package but not sub-packages
-        found.clear();
-        mAa.findGlobs("mock_android.view.*", zipClasses, found);
-
-        assertArrayEquals(new String[] {
-                "mock_android.view.View",
-                "mock_android.view.ViewGroup",
-                "mock_android.view.ViewGroup$LayoutParams",
-                "mock_android.view.ViewGroup$MarginLayoutParams"
-            },
-            found.keySet().toArray());
-
-        for (String key : found.keySet()) {
-            ClassReader value = found.get(key);
-            assertNotNull("No value for " + key, value);
-            assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
-        }
+                        "mock_android.dummy.DummyClass",
+                        "mock_android.dummy.InnerTest$MyIntEnum",
+                        "mock_android.util.EmptyArray",
+                        "mock_android.dummy.InnerTest$DerivingClass",
+                        "mock_android.dummy2.DummyClass",
+                        "mock_android.dummy.subpackage.SubpackageClassC$InnerClass",
+                        "mock_android.dummy.InnerTest$MyGenerics1",
+                        "mock_android.dummy.subpackage.SubpackageClassC$StaticInnerClass",
+                        "mock_android.dummy.InnerTest$MyStaticInnerClass",
+                        "mock_android.dummy.InnerTest$NotStaticInner1",
+                        "mock_android.dummy.InnerTest$NotStaticInner2",
+                        "mock_android.dummy.subpackage.SubpackageClassA",
+                        "mock_android.dummy.InnerTest",
+                        "mock_android.dummy.InnerTest$1",
+                        "mock_android.dummy.subpackage.SubpackageClassC",
+                        "mock_android.dummy.subpackage.SubpackageClassB",
+                },
+                result.getFound().keySet().toArray());
     }
 
     @Test
-    public void testFindClassesDerivingFrom() throws LogAbortException, IOException {
-
+    public void testFindClassesDerivingFrom() throws IOException {
         Map<String, ClassReader> zipClasses = new TreeMap<>();
         Map<String, InputStream> filesFound = new TreeMap<>();
 
-        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
+        getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, zipClasses, filesFound);
         TreeMap<String, ClassReader> found = new TreeMap<>();
 
-        mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
+        AsmAnalyzer.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
 
         assertArrayEquals(new String[] {
                 "mock_android.view.View",
@@ -202,19 +175,18 @@
     }
 
     @Test
-    public void testDependencyVisitor() throws IOException, LogAbortException {
-
+    public void testDependencyVisitor() throws IOException {
         Map<String, ClassReader> zipClasses = new TreeMap<>();
         Map<String, InputStream> filesFound = new TreeMap<>();
 
-        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
+        getDefaultAnalyzer().parseZip(MOCK_ANDROID_JAR, zipClasses, filesFound);
         TreeMap<String, ClassReader> keep = new TreeMap<>();
         TreeMap<String, ClassReader> new_keep = new TreeMap<>();
         TreeMap<String, ClassReader> in_deps = new TreeMap<>();
         TreeMap<String, ClassReader> out_deps = new TreeMap<>();
 
-        ClassReader cr = mAa.findClass("mock_android.widget.LinearLayout", zipClasses, keep);
-        DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
+        ClassReader cr = AsmAnalyzer.findClass("mock_android.widget.LinearLayout", zipClasses, keep);
+        DependencyVisitor visitor = getDefaultAnalyzer().getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
 
         // get first level dependencies
         cr.accept(visitor, 0 /* flags */);
@@ -253,6 +225,7 @@
         assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
         assertArrayEquals(new String[] {
                 "mock_android.widget.LinearLayout",
+                "notjava.lang.JavaClass",
         }, keep.keySet().toArray());
     }
 }
diff --git a/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index e718fb9..9d7b26d 100644
--- a/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -29,6 +29,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
@@ -37,11 +38,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
@@ -61,7 +60,7 @@
     private File mTempFile;
 
     // ASM internal name for the the class in java package that should be refactored.
-    private static final String JAVA_CLASS_NAME = "java/lang/JavaClass";
+    private static final String JAVA_CLASS_NAME = "notjava.lang.JavaClass";
 
     @Before
     public void setUp() throws Exception {
@@ -69,7 +68,6 @@
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
 
         mOsJarPath = new ArrayList<>();
-        //noinspection ConstantConditions
         mOsJarPath.add(url.getFile());
 
         mTempFile = File.createTempFile("mock", ".jar");
@@ -78,7 +76,7 @@
     }
 
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         if (mTempFile != null) {
             //noinspection ResultOfMethodCallIgnored
             mTempFile.delete();
@@ -87,7 +85,7 @@
     }
 
     @Test
-    public void testClassRenaming() throws IOException, LogAbortException {
+    public void testClassRenaming() throws IOException {
 
         ICreateInfo ci = new CreateInfoAdapter() {
             @Override
@@ -95,21 +93,21 @@
                 // classes to rename (so that we can replace them)
                 return new String[] {
                         "mock_android.view.View", "mock_android.view._Original_View",
-                        "not.an.actual.ClassName", "anoter.fake.NewClassName",
+                        "not.an.actual.ClassName", "another.fake.NewClassName",
                 };
             }
         };
 
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+        AsmGenerator agen = new AsmGenerator(mLog, ci);
 
-        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
                 null,                 // derived from
                 new String[] {        // include classes
                     "**"
                 },
-                Collections.emptySet() /* excluded classes */,
+                new String[]{}  /* excluded classes */,
                 new String[]{} /* include files */);
-        aa.analyze();
+        agen.setAnalysisResult(aa.analyze());
         agen.generate();
 
         Set<String> notRenamed = agen.getClassesNotRenamed();
@@ -118,7 +116,7 @@
     }
 
     @Test
-    public void testJavaClassRefactoring() throws IOException, LogAbortException {
+    public void testJavaClassRefactoring() throws IOException {
         ICreateInfo ci = new CreateInfoAdapter() {
             @Override
             public Class<?>[] getInjectedClasses() {
@@ -132,34 +130,33 @@
             public String[] getJavaPkgClasses() {
              // classes to refactor (so that we can replace them)
                 return new String[] {
-                        "java.lang.JavaClass", "com.android.tools.layoutlib.create.dataclass.JavaClass",
+                        JAVA_CLASS_NAME, "com.android.tools.layoutlib.create.dataclass.JavaClass",
                 };
             }
 
             @Override
-            public Set<String> getExcludedClasses() {
-                return Collections.singleton("java.lang.JavaClass");
+            public String[] getExcludedClasses() {
+                return new String[]{JAVA_CLASS_NAME};
             }
         };
 
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+        AsmGenerator agen = new AsmGenerator(mLog, ci);
 
-        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
                 null,                 // derived from
                 new String[] {        // include classes
                     "**"
                 },
-                Collections.emptySet(),
+                new String[]{},
                 new String[] {        /* include files */
                     "mock_android/data/data*"
                 });
-        aa.analyze();
-        agen.generate();
-        Map<String, ClassReader> output = new TreeMap<>();
-        Map<String, InputStream> filesFound = new TreeMap<>();
-        parseZip(mOsDestJar, output, filesFound);
+        agen.setAnalysisResult(aa.analyze());
+        Map<String, byte[]> output = agen.generate();
         RecordingClassVisitor cv = new RecordingClassVisitor();
-        for (ClassReader cr: output.values()) {
+        for (Map.Entry<String, byte[]> entry: output.entrySet()) {
+            if (!entry.getKey().endsWith(".class")) continue;
+            ClassReader cr = new ClassReader(entry.getValue());
             cr.accept(cv, 0);
         }
         assertTrue(cv.mVisitedClasses.contains(
@@ -167,11 +164,11 @@
         assertFalse(cv.mVisitedClasses.contains(
                 JAVA_CLASS_NAME));
         assertArrayEquals(new String[] {"mock_android/data/dataFile"},
-                filesFound.keySet().toArray());
+                findFileNames(output));
     }
 
     @Test
-    public void testClassRefactoring() throws IOException, LogAbortException {
+    public void testClassRefactoring() throws IOException {
         ICreateInfo ci = new CreateInfoAdapter() {
             @Override
             public Class<?>[] getInjectedClasses() {
@@ -190,21 +187,20 @@
             }
         };
 
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+        AsmGenerator agen = new AsmGenerator(mLog, ci);
 
-        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
                 null,                 // derived from
                 new String[] {        // include classes
                         "**"
                 },
-                Collections.emptySet(),
+                new String[]{},
                 new String[] {});
-        aa.analyze();
-        agen.generate();
-        Map<String, ClassReader> output = new TreeMap<>();
-        parseZip(mOsDestJar, output, new TreeMap<>());
+        agen.setAnalysisResult(aa.analyze());
+        Map<String, byte[]> output = agen.generate();
         RecordingClassVisitor cv = new RecordingClassVisitor();
-        for (ClassReader cr: output.values()) {
+        for (byte[] classContent: output.values()) {
+            ClassReader cr = new ClassReader(classContent);
             cr.accept(cv, 0);
         }
         assertTrue(cv.mVisitedClasses.contains(
@@ -214,43 +210,53 @@
     }
 
     @Test
-    public void testClassExclusion() throws IOException, LogAbortException {
+    public void testClassExclusion() throws IOException {
         ICreateInfo ci = new CreateInfoAdapter() {
             @Override
-            public Set<String> getExcludedClasses() {
-                Set<String> set = new HashSet<>(2);
-                set.add("mock_android.dummy.InnerTest");
-                set.add("java.lang.JavaClass");
-                return set;
+            public String[] getExcludedClasses() {
+                return new String[] {
+                        "mock_android.dummy2.*",
+                        "mock_android.dummy.**",
+                        "mock_android.util.NotNeeded",
+                        JAVA_CLASS_NAME
+                };
             }
         };
 
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
-        Set<String> excludedClasses = ci.getExcludedClasses();
-        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+        AsmGenerator agen = new AsmGenerator(mLog, ci);
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
                 null,                 // derived from
                 new String[] {        // include classes
                         "**"
                 },
-                excludedClasses,
+                ci.getExcludedClasses(),
                 new String[] {        /* include files */
                         "mock_android/data/data*"
                 });
-        aa.analyze();
-        agen.generate();
-        Map<String, ClassReader> output = new TreeMap<>();
-        Map<String, InputStream> filesFound = new TreeMap<>();
-        parseZip(mOsDestJar, output, filesFound);
-        for (String s : output.keySet()) {
-            assertFalse(excludedClasses.contains(s));
-        }
+        agen.setAnalysisResult(aa.analyze());
+        Map<String, byte[]> output = agen.generate();
+        // Everything in .dummy.** should be filtered
+        // Only things is .dummy2.* should be filtered
+        assertArrayEquals(new String[] {
+                "mock_android.dummy2.keep.DoNotRemove",
+                "mock_android.util.EmptyArray",
+                "mock_android.view.View",
+                "mock_android.view.ViewGroup",
+                "mock_android.view.ViewGroup$LayoutParams",
+                "mock_android.view.ViewGroup$MarginLayoutParams",
+                "mock_android.widget.LinearLayout",
+                "mock_android.widget.LinearLayout$LayoutParams",
+                "mock_android.widget.TableLayout",
+                "mock_android.widget.TableLayout$LayoutParams"},
+                findClassNames(output)
+        );
         assertArrayEquals(new String[] {"mock_android/data/dataFile"},
-                filesFound.keySet().toArray());
+                findFileNames(output));
     }
 
     @Test
-    public void testMethodInjection() throws IOException, LogAbortException,
-            ClassNotFoundException, IllegalAccessException, InstantiationException,
+    public void testMethodInjection() throws IOException, ClassNotFoundException,
+            IllegalAccessException, InstantiationException,
             NoSuchMethodException, InvocationTargetException {
         ICreateInfo ci = new CreateInfoAdapter() {
             @Override
@@ -260,8 +266,8 @@
             }
         };
 
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
-        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+        AsmGenerator agen = new AsmGenerator(mLog, ci);
+        AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
                 null,                 // derived from
                 new String[] {        // include classes
                         "**"
@@ -270,11 +276,9 @@
                 new String[] {        /* include files */
                         "mock_android/data/data*"
                 });
-        aa.analyze();
-        agen.generate();
-        Map<String, ClassReader> output = new TreeMap<>();
-        Map<String, InputStream> filesFound = new TreeMap<>();
-        parseZip(mOsDestJar, output, filesFound);
+        agen.setAnalysisResult(aa.analyze());
+        JarUtil.createJar(new FileOutputStream(mOsDestJar), agen.generate());
+
         final String modifiedClass = "mock_android.util.EmptyArray";
         final String modifiedClassPath = modifiedClass.replace('.', '/').concat(".class");
         ZipFile zipFile = new ZipFile(mOsDestJar);
@@ -310,27 +314,23 @@
         return bos.toByteArray();
     }
 
-    private void parseZip(String jarPath,
-            Map<String, ClassReader> classes,
-            Map<String, InputStream> filesFound) throws IOException {
 
-            ZipFile zip = new ZipFile(jarPath);
-            Enumeration<? extends ZipEntry> entries = zip.entries();
-            ZipEntry entry;
-            while (entries.hasMoreElements()) {
-                entry = entries.nextElement();
-                if (entry.getName().endsWith(".class")) {
-                    ClassReader cr = new ClassReader(zip.getInputStream(entry));
-                    String className = classReaderToClassName(cr);
-                    classes.put(className, cr);
-                } else {
-                    filesFound.put(entry.getName(), zip.getInputStream(entry));
-                }
-            }
-
+    private static String[] findClassNames(Map<String, byte[]> content) {
+        return content.entrySet().stream()
+                .filter(entry -> entry.getKey().endsWith(".class"))
+                .map(entry -> classReaderToClassName(new ClassReader(entry.getValue())))
+                .sorted()
+                .toArray(String[]::new);
     }
 
-    private String classReaderToClassName(ClassReader classReader) {
+    private static String[] findFileNames(Map<String, byte[]> content) {
+        return content.keySet().stream()
+                .filter(entry -> !entry.endsWith(".class"))
+                .sorted()
+                .toArray(String[]::new);
+    }
+
+    private static String classReaderToClassName(ClassReader classReader) {
         if (classReader == null) {
             return null;
         } else {
diff --git a/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java
index ad7cb9a..675159a 100644
--- a/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java
+++ b/create/tests/com/android/tools/layoutlib/create/CreateInfoAdapter.java
@@ -59,8 +59,8 @@
     }
 
     @Override
-    public Set<String> getExcludedClasses() {
-        return Collections.emptySet();
+    public String[] getExcludedClasses() {
+        return EMPTY_STRING_ARRAY;
     }
 
     @Override
diff --git a/create/tests/com/android/tools/layoutlib/create/StubClassAdapterTest.java b/create/tests/com/android/tools/layoutlib/create/StubClassAdapterTest.java
new file mode 100644
index 0000000..5b3ef20
--- /dev/null
+++ b/create/tests/com/android/tools/layoutlib/create/StubClassAdapterTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+import com.android.tools.layoutlib.create.dataclass.StubClass;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+import com.google.common.io.ByteStreams;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class StubClassAdapterTest {
+    private static final String STUB_CLASS_NAME = StubClass.class.getName();
+
+    @Test
+    public void testStubbedClass()
+            throws ClassNotFoundException, IOException, IllegalAccessException,
+            InstantiationException {
+        // Always rename the class to avoid conflict with the original class.
+        byte[] classContent = ByteStreams.toByteArray(ClassLoader.getSystemResourceAsStream(
+                STUB_CLASS_NAME.replace('.', '/') + ".class"));
+
+        String newClassName = STUB_CLASS_NAME + '_';
+        classContent =
+                StubClassAdapter.stubClass(new Log(), classContent, newClassName.replace('.', '/'));
+        TestClassLoader myClassLoader = new TestClassLoader(newClassName, classContent);
+        Class<?> aClass = myClassLoader.loadClass(newClassName);
+        assertTrue("StubClass not loaded by the classloader. Likely a bug in the test.",
+                myClassLoader.wasClassLoaded(newClassName));
+        try {
+            aClass.newInstance();
+            fail("Method should throw a RuntimeException");
+        } catch (RuntimeException e) {
+            assertEquals("Stub!", e.getMessage());
+        }
+    }
+}
diff --git a/create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java b/create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
index 3db3e23..fead4a6 100644
--- a/create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
+++ b/create/tests/com/android/tools/layoutlib/create/StubMethodAdapterTest.java
@@ -61,10 +61,10 @@
         String newClassName = STUB_CLASS_NAME + '_';
         new ClassReader(STUB_CLASS_NAME).accept(
                 new ClassAdapter(newClassName, writer, methodPredicate), 0);
-        MyClassLoader myClassLoader = new MyClassLoader(newClassName, writer.toByteArray());
+        TestClassLoader myClassLoader = new TestClassLoader(newClassName, writer.toByteArray());
         Class<?> aClass = myClassLoader.loadClass(newClassName);
         assertTrue("StubClass not loaded by the classloader. Likely a bug in the test.",
-                myClassLoader.findClassCalled);
+                myClassLoader.wasClassLoaded(newClassName));
         Method method = aClass.getMethod(methodName);
         Object o = aClass.newInstance();
         assertion.accept((Boolean) method.invoke(o));
@@ -103,30 +103,11 @@
             if (mMethodPredicate.test(name, descriptor)) {
                 String methodSignature = mClassName + "#" + name;
                 String invokeSignature = methodSignature + desc;
-                return new StubMethodAdapter(originalMethod, name, descriptor.getReturnType(),
+                return new StubCallMethodAdapter(originalMethod, name, descriptor.getReturnType(),
                         invokeSignature, isStatic, isNative);
             }
             return originalMethod;
         }
     }
 
-    private static class MyClassLoader extends ClassLoader {
-        private final String mName;
-        private final byte[] mBytes;
-        private boolean findClassCalled;
-
-        private MyClassLoader(String name, byte[] bytes) {
-            mName = name;
-            mBytes = bytes;
-        }
-
-        @Override
-        protected Class<?> findClass(String name) throws ClassNotFoundException {
-            if (name.equals(mName)) {
-                findClassCalled = true;
-                return defineClass(name, mBytes, 0, mBytes.length);
-            }
-            return super.findClass(name);
-        }
-    }
 }
diff --git a/create/tests/com/android/tools/layoutlib/create/TestClassLoader.java b/create/tests/com/android/tools/layoutlib/create/TestClassLoader.java
new file mode 100644
index 0000000..a869141
--- /dev/null
+++ b/create/tests/com/android/tools/layoutlib/create/TestClassLoader.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import java.util.HashSet;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+
+class TestClassLoader extends ClassLoader {
+    private final Map<String, byte[]> mClassDefinitions;
+    private final HashSet<String> mLoadedClasses = new HashSet<>();
+
+    private TestClassLoader(Map<String, byte[]> classDefinitions) {
+        mClassDefinitions = classDefinitions;
+    }
+
+    TestClassLoader(String name, byte[] bytes) {
+        this(ImmutableMap.of(name, bytes));
+    }
+
+    public boolean wasClassLoaded(String name) {
+        return mLoadedClasses.contains(name);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        byte[] classContent = mClassDefinitions.get(name);
+        if (classContent != null) {
+            mLoadedClasses.add(name);
+            return defineClass(name, classContent, 0, classContent.length);
+        }
+        return super.findClass(name);
+    }
+}
diff --git a/create/tests/data/mock_android.jar b/create/tests/data/mock_android.jar
index c6ca3c4..580e6f1 100644
--- a/create/tests/data/mock_android.jar
+++ b/create/tests/data/mock_android.jar
Binary files differ
diff --git a/create/tests/mock_data/Android.mk b/create/tests/mock_data/Android.mk
new file mode 100644
index 0000000..7a735f3
--- /dev/null
+++ b/create/tests/mock_data/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under,.)
+LOCAL_JAVA_RESOURCE_DIRS := .
+
+LOCAL_MODULE := mock_android
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
\ No newline at end of file
diff --git a/create/tests/mock_data/java/lang/JavaClass.java b/create/tests/mock_data/mock_android/dummy/DummyClass.java
similarity index 62%
copy from create/tests/mock_data/java/lang/JavaClass.java
copy to create/tests/mock_data/mock_android/dummy/DummyClass.java
index 59612e9..de3b0e0 100644
--- a/create/tests/mock_data/java/lang/JavaClass.java
+++ b/create/tests/mock_data/mock_android/dummy/DummyClass.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 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,
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package java.lang;
+package mock_android.dummy;
 
-public class JavaClass {
+public class DummyClass {
 
-    public static String test = "test";
-}
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/java/lang/JavaClass.java b/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassA.java
similarity index 62%
copy from create/tests/mock_data/java/lang/JavaClass.java
copy to create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassA.java
index 59612e9..eaf1302 100644
--- a/create/tests/mock_data/java/lang/JavaClass.java
+++ b/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassA.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 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,
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package java.lang;
+package mock_android.dummy.subpackage;
 
-public class JavaClass {
+public class SubpackageClassA {
 
-    public static String test = "test";
-}
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/java/lang/JavaClass.java b/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassB.java
similarity index 62%
copy from create/tests/mock_data/java/lang/JavaClass.java
copy to create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassB.java
index 59612e9..7f67db1 100644
--- a/create/tests/mock_data/java/lang/JavaClass.java
+++ b/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassB.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 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,
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package java.lang;
+package mock_android.dummy.subpackage;
 
-public class JavaClass {
+public class SubpackageClassB {
 
-    public static String test = "test";
-}
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassC.java b/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassC.java
new file mode 100644
index 0000000..e456432
--- /dev/null
+++ b/create/tests/mock_data/mock_android/dummy/subpackage/SubpackageClassC.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package mock_android.dummy.subpackage;
+
+public class SubpackageClassC {
+    public static class StaticInnerClass {
+
+    }
+
+    public class InnerClass {
+
+    }
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/java/lang/JavaClass.java b/create/tests/mock_data/mock_android/dummy2/DummyClass.java
similarity index 62%
copy from create/tests/mock_data/java/lang/JavaClass.java
copy to create/tests/mock_data/mock_android/dummy2/DummyClass.java
index 59612e9..9f7cfde 100644
--- a/create/tests/mock_data/java/lang/JavaClass.java
+++ b/create/tests/mock_data/mock_android/dummy2/DummyClass.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 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,
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package java.lang;
+package mock_android.dummy2;
 
-public class JavaClass {
+public class DummyClass {
 
-    public static String test = "test";
-}
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/java/lang/JavaClass.java b/create/tests/mock_data/mock_android/dummy2/keep/DoNotRemove.java
similarity index 62%
copy from create/tests/mock_data/java/lang/JavaClass.java
copy to create/tests/mock_data/mock_android/dummy2/keep/DoNotRemove.java
index 59612e9..8a232a6 100644
--- a/create/tests/mock_data/java/lang/JavaClass.java
+++ b/create/tests/mock_data/mock_android/dummy2/keep/DoNotRemove.java
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2018 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,
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package java.lang;
+package mock_android.dummy2.keep;
 
-public class JavaClass {
+public class DoNotRemove {
 
-    public static String test = "test";
-}
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/mock_android/util/EmptyArray.java b/create/tests/mock_data/mock_android/util/EmptyArray.java
index aaeebf6..11f8c86 100644
--- a/create/tests/mock_data/mock_android/util/EmptyArray.java
+++ b/create/tests/mock_data/mock_android/util/EmptyArray.java
@@ -16,7 +16,7 @@
 
 package mock_android.util;
 
-import java.lang.JavaClass;
+import notjava.lang.JavaClass;
 
 public class EmptyArray {
 
diff --git a/create/tests/mock_data/mock_android/util/NotNeeded.java b/create/tests/mock_data/mock_android/util/NotNeeded.java
new file mode 100644
index 0000000..3239e76
--- /dev/null
+++ b/create/tests/mock_data/mock_android/util/NotNeeded.java
@@ -0,0 +1,4 @@
+package mock_android.util;
+
+public class NotNeeded {
+}
\ No newline at end of file
diff --git a/create/tests/mock_data/mock_android/view/View.java b/create/tests/mock_data/mock_android/view/View.java
index 84ec8a9..7116888 100644
--- a/create/tests/mock_data/mock_android/view/View.java
+++ b/create/tests/mock_data/mock_android/view/View.java
@@ -16,7 +16,7 @@
 
 package mock_android.view;
 
-import java.lang.JavaClass;
+import notjava.lang.JavaClass;
 
 public class View {
 
diff --git a/create/tests/mock_data/java/lang/JavaClass.java b/create/tests/mock_data/notjava/lang/JavaClass.java
similarity index 96%
rename from create/tests/mock_data/java/lang/JavaClass.java
rename to create/tests/mock_data/notjava/lang/JavaClass.java
index 59612e9..77cd6aa 100644
--- a/create/tests/mock_data/java/lang/JavaClass.java
+++ b/create/tests/mock_data/notjava/lang/JavaClass.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package java.lang;
+package notjava.lang;
 
 public class JavaClass {
 
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
index 99143ad..8cd61f9 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
@@ -83,10 +83,13 @@
     }
 
     @Override
-    public boolean init(Map<String, String> platformProperties, File fontLocation,
-            Map<String, Map<String, Integer>> enumValueMap, LayoutLog log) {
+    public boolean init(Map<String, String> platformProperties,
+            File fontLocation,
+            String icuDataPath,
+            Map<String, Map<String, Integer>> enumValueMap,
+            LayoutLog log) {
         try {
-            return mDelegate.init(platformProperties, fontLocation, enumValueMap,
+            return mDelegate.init(platformProperties, fontLocation, icuDataPath, enumValueMap,
                     RemoteLayoutLogAdapter.create(log));
         } catch (RemoteException e) {
             throw new RuntimeException(e);
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteActionBarCallbackAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteActionBarCallbackAdapter.java
index e1cfe15..9f3da0c 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteActionBarCallbackAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteActionBarCallbackAdapter.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.common.rendering.api.ActionBarCallback;
 import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.layout.remote.api.RemoteActionBarCallback;
 import com.android.tools.layoutlib.annotations.NotNull;
 
@@ -39,8 +40,8 @@
     }
 
     @Override
-    public List<String> getMenuIdNames() {
-        return mDelegate.getMenuIdNames();
+    public List<ResourceReference> getMenuIds() {
+        return mDelegate.getMenuIds();
     }
 
     @Override
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteILayoutPullParserAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteILayoutPullParserAdapter.java
index 451d93e..841d714 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteILayoutPullParserAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteILayoutPullParserAdapter.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.remote.client.adapters;
 
 import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.layout.remote.api.RemoteILayoutPullParser;
 import com.android.tools.layoutlib.annotations.NotNull;
 
@@ -36,7 +37,12 @@
     }
 
     @Override
-    public Object getViewCookie() throws RemoteException {
+    public Object getViewCookie() {
         return ((ILayoutPullParser) mDelegate).getViewCookie();
     }
+
+    @Override
+    public ResourceNamespace getLayoutNamespace() {
+        return ((ILayoutPullParser) mDelegate).getLayoutNamespace();
+    }
 }
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
index b9b1a00..b4236e9 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -17,8 +17,8 @@
 package com.android.layoutlib.bridge.remote.client.adapters;
 
 import com.android.ide.common.rendering.api.AdapterBinding;
-import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback.ViewAttribute;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams.Key;
@@ -27,10 +27,8 @@
 import com.android.layout.remote.api.RemoteLayoutlibCallback;
 import com.android.layout.remote.api.RemoteParserFactory;
 import com.android.layout.remote.api.RemoteXmlPullParser;
-import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.util.Pair;
 
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -58,8 +56,7 @@
     }
 
     @Override
-    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
-            throws Exception {
+    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) {
         throw new UnsupportedOperationException("Not implemented yet");
     }
 
@@ -69,20 +66,13 @@
     }
 
     @Override
-    public RemoteResolveResult resolveResourceId(int id) {
-        Pair<ResourceType, String> result = mDelegate.resolveResourceId(id);
-        return result != null ? new RemoteResolveResult(result.getFirst(), result.getSecond()) :
-                null;
-    }
-
-    @Override
-    public String resolveResourceId(int[] id) {
+    public ResourceReference resolveResourceId(int id) {
         return mDelegate.resolveResourceId(id);
     }
 
     @Override
-    public Integer getResourceId(ResourceType type, String name) {
-        return mDelegate.getResourceId(type, name);
+    public int getOrGenerateResourceId(ResourceReference resource) {
+        return mDelegate.getOrGenerateResourceId(resource);
     }
 
     @Override
@@ -122,15 +112,6 @@
         return mDelegate.getFlag(key);
     }
 
-    @Override
-    public RemoteParserFactory getParserFactory() {
-        try {
-            return RemoteParserFactoryAdapter.create(mDelegate.getParserFactory());
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     @Nullable
     @Override
     public Path findClassPath(String name) {
@@ -148,10 +129,29 @@
         return null;
     }
 
+
     @Override
-    public RemoteXmlPullParser getXmlFileParser(String fileName) {
+    public RemoteXmlPullParser createXmlParserForPsiFile(String fileName) {
         try {
-            return RemoteXmlPullParserAdapter.create(mDelegate.getXmlFileParser(fileName));
+            return RemoteXmlPullParserAdapter.create(mDelegate.createXmlParserForPsiFile(fileName));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public RemoteXmlPullParser createXmlParserForFile(String fileName) {
+        try {
+            return RemoteXmlPullParserAdapter.create(mDelegate.createXmlParserForFile(fileName));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public RemoteXmlPullParser createXmlParser() {
+        try {
+            return RemoteXmlPullParserAdapter.create(mDelegate.createXmlParser());
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteParserFactoryAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteParserFactoryAdapter.java
deleted file mode 100644
index d1f0411..0000000
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteParserFactoryAdapter.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.remote.client.adapters;
-
-import com.android.ide.common.rendering.api.ParserFactory;
-import com.android.layout.remote.api.RemoteParserFactory;
-import com.android.layout.remote.api.RemoteXmlPullParser;
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.rmi.RemoteException;
-import java.rmi.server.UnicastRemoteObject;
-
-public class RemoteParserFactoryAdapter implements RemoteParserFactory {
-
-    private final ParserFactory mDelegate;
-
-    private RemoteParserFactoryAdapter(@NotNull ParserFactory delegate) {
-        mDelegate = delegate;
-    }
-
-    public static RemoteParserFactory create(@NotNull ParserFactory factory)
-            throws RemoteException {
-        return (RemoteParserFactory) UnicastRemoteObject.exportObject(
-                new RemoteParserFactoryAdapter(factory), 0);
-    }
-
-    @Override
-    public RemoteXmlPullParser createParser(String debugName) throws RemoteException {
-        try {
-            return RemoteXmlPullParserAdapter.create(mDelegate.createParser(debugName));
-        } catch (XmlPullParserException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
index a3950d7..b9f4e9e 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.common.rendering.api.IImageFactory;
 import com.android.ide.common.rendering.api.RenderParams;
+import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.Key;
 import com.android.layout.remote.api.RemoteAssetRepository;
@@ -125,7 +126,7 @@
     }
 
     @Override
-    public String getAppIcon() {
+    public ResourceValue getAppIcon() {
         return mDelegate.getAppIcon();
     }
 
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderResourcesAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderResourcesAdapter.java
index 9ae58a9..b477eea 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderResourcesAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderResourcesAdapter.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layoutlib.bridge.remote.client.adapters;
 
 import com.android.ide.common.rendering.api.RenderResources;
@@ -21,14 +20,17 @@
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layout.remote.api.RemoteRenderResources;
-import com.android.resources.ResourceType;
+import com.android.layout.remote.api.RemoteResourceValue;
 import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
 
 import java.rmi.RemoteException;
 import java.rmi.server.UnicastRemoteObject;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class RemoteRenderResourcesAdapter implements RemoteRenderResources {
+
     private final RenderResources mDelegate;
 
     private RemoteRenderResourcesAdapter(@NotNull RenderResources delegate) {
@@ -42,13 +44,13 @@
     }
 
     @Override
-    public StyleResourceValue getDefaultTheme() {
-        return mDelegate.getDefaultTheme();
+    public RemoteResourceValue<StyleResourceValue> getDefaultTheme() {
+        return RemoteResourceValue.fromResourceValue(mDelegate.getDefaultTheme());
     }
 
     @Override
-    public void applyStyle(StyleResourceValue theme, boolean useAsPrimary) {
-        mDelegate.applyStyle(theme, useAsPrimary);
+    public void applyStyle(RemoteResourceValue<StyleResourceValue> theme, boolean useAsPrimary) {
+        mDelegate.applyStyle(theme.toResourceValue(), useAsPrimary);
     }
 
     @Override
@@ -57,68 +59,58 @@
     }
 
     @Override
-    public List<StyleResourceValue> getAllThemes() {
-        return mDelegate.getAllThemes();
+    public List<RemoteResourceValue<StyleResourceValue>> getAllThemes() {
+        return mDelegate.getAllThemes().stream().map(
+                RemoteResourceValue::fromResourceValue).collect(Collectors.toList());
     }
 
     @Override
-    public StyleResourceValue getTheme(String name, boolean frameworkTheme) {
-        return mDelegate.getTheme(name, frameworkTheme);
+    @Nullable
+    public RemoteResourceValue<ResourceValue> getResolvedResource(
+            @NotNull ResourceReference reference) {
+        return RemoteResourceValue.fromResourceValue(mDelegate.getResolvedResource(reference));
     }
 
     @Override
-    public boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme) {
-        return mDelegate.themeIsParentOf(parentTheme, childTheme);
+    public RemoteResourceValue<ResourceValue> findItemInTheme(ResourceReference attr) {
+        return RemoteResourceValue.fromResourceValue(mDelegate.findItemInTheme(attr));
     }
 
     @Override
-    public ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName) {
-        return mDelegate.getFrameworkResource(resourceType, resourceName);
+    public RemoteResourceValue<ResourceValue> findItemInStyle(
+            RemoteResourceValue<StyleResourceValue> style, ResourceReference attr) {
+        return RemoteResourceValue.fromResourceValue(
+                mDelegate.findItemInStyle(style.toResourceValue(), attr));
     }
 
     @Override
-    public ResourceValue getProjectResource(ResourceType resourceType, String resourceName) {
-        return mDelegate.getProjectResource(resourceType, resourceName);
+    public RemoteResourceValue<ResourceValue> resolveValue(
+            RemoteResourceValue<ResourceValue> value) {
+        return RemoteResourceValue.fromResourceValue(
+                mDelegate.resolveResValue(value.toResourceValue()));
     }
 
     @Override
-    public ResourceValue findItemInTheme(ResourceReference attr) {
-        return mDelegate.findItemInTheme(attr);
+    public RemoteResourceValue<StyleResourceValue> getParent(
+            RemoteResourceValue<StyleResourceValue> style) {
+        return RemoteResourceValue.fromResourceValue(mDelegate.getParent(style.toResourceValue()));
     }
 
     @Override
-    public ResourceValue findItemInStyle(StyleResourceValue style, ResourceReference attr) {
-        return mDelegate.findItemInStyle(style, attr);
+    @Nullable
+    public RemoteResourceValue<StyleResourceValue> getStyle(@NotNull ResourceReference reference) {
+        return RemoteResourceValue.fromResourceValue(mDelegate.getStyle(reference));
     }
 
     @Override
-    public ResourceValue resolveValue(ResourceValue value) {
-        return mDelegate.resolveResValue(value);
+    public RemoteResourceValue<ResourceValue> dereference(
+            RemoteResourceValue<ResourceValue> resourceValue) {
+        return RemoteResourceValue.fromResourceValue(
+                mDelegate.dereference(resourceValue.toResourceValue()));
     }
 
     @Override
-    public ResourceValue resolveValue(ResourceType type, String name, String value,
-            boolean isFrameworkValue) {
-        return mDelegate.resolveValue(type, name, value, isFrameworkValue);
-    }
-
-    @Override
-    public StyleResourceValue getParent(StyleResourceValue style) {
-        return mDelegate.getParent(style);
-    }
-
-    @Override
-    public StyleResourceValue getStyle(String styleName, boolean isFramework) {
-        return mDelegate.getStyle(styleName, isFramework);
-    }
-
-    @Override
-    public ResourceValue dereference(ResourceValue resourceValue) {
-        return mDelegate.dereference(resourceValue);
-    }
-
-    @Override
-    public ResourceValue getUnresolvedResource(ResourceReference reference) {
-        return mDelegate.getUnresolvedResource(reference);
+    public RemoteResourceValue<ResourceValue> getUnresolvedResource(ResourceReference reference) {
+        return RemoteResourceValue.fromResourceValue(mDelegate.getUnresolvedResource(reference));
     }
 }
diff --git a/remote/common/remote common.iml b/remote/common/remote common.iml
index e56c17c..1562b6d 100644
--- a/remote/common/remote common.iml
+++ b/remote/common/remote common.iml
@@ -20,5 +20,6 @@
     </orderEntry>
     <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
     <orderEntry type="module" module-name="common" />
+    <orderEntry type="library" name="framework.jar" level="project" />
   </component>
 </module>
\ No newline at end of file
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteActionBarCallback.java b/remote/common/src/com/android/layout/remote/api/RemoteActionBarCallback.java
index 153a575..988fa98 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteActionBarCallback.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteActionBarCallback.java
@@ -13,35 +13,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.layout.remote.api;
 
 import com.android.ide.common.rendering.api.ActionBarCallback;
 import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.ide.common.rendering.api.ResourceReference;
 
 import java.rmi.Remote;
 import java.rmi.RemoteException;
 import java.util.List;
 
+import android.annotation.NonNull;
+
 /**
- * Remote version of the {@link ActionBarCallback} class
+ * Remote version of the {@link ActionBarCallback} class.
  */
 public interface RemoteActionBarCallback extends Remote {
-
-    List<String> getMenuIdNames() throws RemoteException;
-
+    @NonNull
+    List<ResourceReference> getMenuIds() throws RemoteException;
 
     boolean getSplitActionBarWhenNarrow() throws RemoteException;
 
-
     int getNavigationMode() throws RemoteException;
 
-
     String getSubTitle() throws RemoteException;
 
-
     HomeButtonStyle getHomeButtonStyle() throws RemoteException;
 
-
     boolean isOverflowPopupNeeded() throws RemoteException;
 }
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteBridge.java b/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
index 8188198..74a02b3 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
@@ -62,6 +62,7 @@
      *
      * @param platformProperties The build properties for the platform.
      * @param fontLocation the location of the fonts.
+     * @param icuDataPath the location of the ICU data used natively.
      * @param enumValueMap map attrName ⇒ { map enumFlagName ⇒ Integer value }. This is typically
      * read from attrs.xml in the SDK target.
      * @param log a {@link LayoutLog} object. Can be null.
@@ -69,8 +70,8 @@
      * @return true if success.
      */
     boolean init(@NotNull Map<String, String> platformProperties, File fontLocation,
-            @NotNull Map<String, Map<String, Integer>> enumValueMap, @Nullable RemoteLayoutLog log)
-            throws RemoteException;
+            String icuDataPath, @NotNull Map<String, Map<String, Integer>> enumValueMap,
+            @Nullable RemoteLayoutLog log) throws RemoteException;
 
     /**
      * Prepares the layoutlib to be unloaded.
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java b/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java
index 585535b..9f8ef8b 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java
@@ -17,6 +17,7 @@
 package com.android.layout.remote.api;
 
 import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 
 import java.rmi.RemoteException;
 
@@ -25,4 +26,6 @@
  */
 public interface RemoteILayoutPullParser extends RemoteXmlPullParser {
     Object getViewCookie() throws RemoteException;
+
+    ResourceNamespace getLayoutNamespace() throws RemoteException;
 }
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
index 0f315ca..94e5185 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
@@ -17,15 +17,14 @@
 package com.android.layout.remote.api;
 
 import com.android.ide.common.rendering.api.AdapterBinding;
-import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.LayoutlibCallback.ViewAttribute;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams.Key;
-import com.android.resources.ResourceType;
-import com.android.util.Pair;
 
-import java.io.Serializable;
+import org.xmlpull.v1.XmlPullParser;
+
 import java.nio.file.Path;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
@@ -37,15 +36,13 @@
     boolean supports(int ideFeature) throws RemoteException;
 
     Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
-            throws Exception, RemoteException;
+            throws Exception;
 
     String getNamespace() throws RemoteException;
 
-    RemoteResolveResult resolveResourceId(int id) throws RemoteException;
+    ResourceReference resolveResourceId(int id) throws RemoteException;
 
-    String resolveResourceId(int[] id) throws RemoteException;
-
-    Integer getResourceId(ResourceType type, String name) throws RemoteException;
+    int getOrGenerateResourceId(ResourceReference resource) throws RemoteException;
 
     RemoteILayoutPullParser getParser(ResourceValue layoutResource) throws RemoteException;
 
@@ -61,23 +58,11 @@
 
     <T> T getFlag(Key<T> key) throws RemoteException;
 
-    RemoteParserFactory getParserFactory() throws RemoteException;
-
     Path findClassPath(String name) throws RemoteException;
 
-    RemoteXmlPullParser getXmlFileParser(String fileName) throws RemoteException;
+    RemoteXmlPullParser createXmlParserForPsiFile(String fileName) throws RemoteException;
 
-    class RemoteResolveResult implements Serializable {
-        private ResourceType type;
-        private String value;
+    RemoteXmlPullParser createXmlParserForFile(String fileName) throws RemoteException;
 
-        public RemoteResolveResult(ResourceType type, String value) {
-            this.type = type;
-            this.value = value;
-        }
-
-        public Pair<ResourceType, String> asPair() {
-            return Pair.of(type, value);
-        }
-    }
+    RemoteXmlPullParser createXmlParser() throws RemoteException;
 }
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteNamespaceResolver.java b/remote/common/src/com/android/layout/remote/api/RemoteNamespaceResolver.java
new file mode 100644
index 0000000..2adf016
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteNamespaceResolver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layout.remote.api;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.io.Serializable;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteNamespaceResolver extends Remote {
+    static final RemoteNamespaceResolver EMPTY_RESOLVER = new EmptyResolver();
+
+    /** Returns the full URI of an XML namespace for a given prefix, if defined. */
+    @Nullable
+    String remotePrefixToUri(@NotNull String namespacePrefix) throws RemoteException;
+
+    @Nullable
+    String remoteUriToPrefix(@NotNull String namespaceUri) throws RemoteException;
+
+    class EmptyResolver implements Serializable, RemoteNamespaceResolver {
+        private EmptyResolver() {
+        }
+
+        @Override
+        public String remotePrefixToUri(String namespacePrefix) {
+            return null;
+        }
+
+        @Override
+        public String remoteUriToPrefix(String namespaceUri) {
+            return null;
+        }
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java b/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java
index 31a35b2..4f187d1 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java
@@ -16,7 +16,6 @@
 
 package com.android.layout.remote.api;
 
-import com.android.ide.common.rendering.api.ParserFactory;
 import com.android.tools.layoutlib.annotations.Nullable;
 
 import java.rmi.Remote;
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java b/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
index da2bd8e..5dff1fb 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
@@ -17,6 +17,7 @@
 package com.android.layout.remote.api;
 
 import com.android.ide.common.rendering.api.IImageFactory;
+import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams.Key;
 import com.android.tools.layoutlib.annotations.Nullable;
 
@@ -49,7 +50,7 @@
 
     IImageFactory getImageFactory() throws RemoteException;
 
-    String getAppIcon() throws RemoteException;
+    ResourceValue getAppIcon() throws RemoteException;
 
     String getAppLabel() throws RemoteException;
 
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteRenderResources.java b/remote/common/src/com/android/layout/remote/api/RemoteRenderResources.java
index f54d44d..530b19e 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteRenderResources.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteRenderResources.java
@@ -20,7 +20,8 @@
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
-import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
 
 import java.rmi.Remote;
 import java.rmi.RemoteException;
@@ -30,43 +31,31 @@
  * Remote version of the {@link RenderResources} class
  */
 public interface RemoteRenderResources extends Remote {
-    StyleResourceValue getDefaultTheme() throws RemoteException;
+    RemoteResourceValue<StyleResourceValue> getDefaultTheme() throws RemoteException;
 
-    void applyStyle(StyleResourceValue theme, boolean useAsPrimary) throws RemoteException;
+    void applyStyle(RemoteResourceValue<StyleResourceValue> theme, boolean useAsPrimary) throws RemoteException;
 
     void clearStyles() throws RemoteException;
 
-    List<StyleResourceValue> getAllThemes() throws RemoteException;
+    List<RemoteResourceValue<StyleResourceValue>> getAllThemes() throws RemoteException;
 
+    @Nullable
+    RemoteResourceValue<ResourceValue> getResolvedResource(@NotNull ResourceReference reference) throws RemoteException;
 
-    StyleResourceValue getTheme(String name, boolean frameworkTheme) throws RemoteException;
+    RemoteResourceValue<ResourceValue> findItemInTheme(ResourceReference attr) throws RemoteException;
 
-
-    boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme)
+    RemoteResourceValue<ResourceValue> findItemInStyle(RemoteResourceValue<StyleResourceValue> style,
+            ResourceReference attr)
             throws RemoteException;
 
-    ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName)
-            throws RemoteException;
+    RemoteResourceValue<ResourceValue> resolveValue(RemoteResourceValue<ResourceValue> value) throws RemoteException;
 
-    ResourceValue getProjectResource(ResourceType resourceType, String resourceName)
-            throws RemoteException;
+    RemoteResourceValue<StyleResourceValue> getParent(RemoteResourceValue<StyleResourceValue> style) throws RemoteException;
 
+    @Nullable
+    RemoteResourceValue<StyleResourceValue> getStyle(@NotNull ResourceReference reference) throws RemoteException;
 
-    ResourceValue findItemInTheme(ResourceReference attr) throws RemoteException;
+    RemoteResourceValue<ResourceValue> dereference(RemoteResourceValue<ResourceValue> resourceValue) throws RemoteException;
 
-    ResourceValue findItemInStyle(StyleResourceValue style, ResourceReference attr)
-            throws RemoteException;
-
-    ResourceValue resolveValue(ResourceValue value) throws RemoteException;
-
-    ResourceValue resolveValue(ResourceType type, String name, String value,
-            boolean isFrameworkValue) throws RemoteException;
-
-    StyleResourceValue getParent(StyleResourceValue style) throws RemoteException;
-
-    StyleResourceValue getStyle(String styleName, boolean isFramework) throws RemoteException;
-
-    ResourceValue dereference(ResourceValue resourceValue) throws RemoteException;
-
-    ResourceValue getUnresolvedResource(ResourceReference reference) throws RemoteException;
+    RemoteResourceValue<ResourceValue> getUnresolvedResource(ResourceReference reference) throws RemoteException;
 }
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteResourceValue.java b/remote/common/src/com/android/layout/remote/api/RemoteResourceValue.java
new file mode 100644
index 0000000..fc19e4f
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteResourceValue.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layout.remote.api;
+
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.layout.remote.util.RemoteResolverAdapter;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.io.Serializable;
+import java.rmi.RemoteException;
+
+/**
+ * Wrapper for {@link ResourceValue} that can be transferred to a different VM.
+ *
+ * @param <T> the ResourceValue instance type
+ */
+public class RemoteResourceValue<T extends ResourceValue> implements Serializable {
+    private static final RemoteResourceValue<ResourceValue> NULL_INSTANCE =
+            new RemoteResourceValue<>(null, null);
+
+    private final T mResourceValue;
+    private final RemoteNamespaceResolver mRemoteResolver;
+
+    private RemoteResourceValue(@Nullable T resourceValue,
+            @Nullable RemoteNamespaceResolver remoteResolver) {
+        mResourceValue = resourceValue;
+        mRemoteResolver = remoteResolver;
+    }
+
+    /**
+     * Returns a RemoteResourceValue that wraps the given {@link ResourceValue} instance. The passed
+     * resource value can be null.
+     */
+    @NotNull
+    public static <T extends ResourceValue> RemoteResourceValue<T> fromResourceValue(
+            T resourceValue) {
+        if (resourceValue == null) {
+            //noinspection unchecked
+            return (RemoteResourceValue<T>) NULL_INSTANCE;
+        }
+        try {
+            return new RemoteResourceValue<>(resourceValue,
+                    RemoteResolverAdapter.create(resourceValue.getNamespaceResolver()));
+
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the {@link ResourceValue} wrapped by a remote wrapper
+     */
+    @NotNull
+    private static <T extends ResourceValue> T toResourceValue(
+            @NotNull RemoteResourceValue<T> remoteResourceValue) {
+        T remoteValue = remoteResourceValue.mResourceValue;
+        if (remoteValue == null) {
+            return null;
+        }
+
+        // The Resolver is not transferred in the ResourceValue (it's transient) so we use the
+        // information in the wrapper to reconstruct it.
+        RemoteNamespaceResolver remoteResolver = remoteResourceValue.mRemoteResolver;
+
+//        TODO: Rethink this as setNamespaceResolver is not available in prebuilts any more
+//        if (remoteResolver != null) {
+//            remoteValue.setNamespaceResolver(new Resolver() {
+//                @Override
+//                public String prefixToUri(String namespacePrefix) {
+//                    try {
+//                        return remoteResolver.remotePrefixToUri(namespacePrefix);
+//                    } catch (RemoteException e) {
+//                        throw new RuntimeException(e);
+//                    }
+//                }
+//
+//                @Override
+//                public String uriToPrefix(String namespaceUri) {
+//                    try {
+//                        return remoteResolver.remoteUriToPrefix(namespaceUri);
+//                    } catch (RemoteException e) {
+//                        throw new RuntimeException(e);
+//                    }
+//                }
+//            });
+//        }
+//        else {
+//            remoteValue.setNamespaceResolver(Resolver.EMPTY_RESOLVER);
+//        }
+
+        return remoteValue;
+    }
+
+    /**
+     * Returns the {@link ResourceValue} wrapped by this remote wrapper
+     */
+    @NotNull
+    public T toResourceValue() {
+        return RemoteResourceValue.toResourceValue(this);
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/util/RemoteResolverAdapter.java b/remote/common/src/com/android/layout/remote/util/RemoteResolverAdapter.java
new file mode 100644
index 0000000..f575b95
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/util/RemoteResolverAdapter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layout.remote.util;
+
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.layout.remote.api.RemoteNamespaceResolver;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteResolverAdapter implements RemoteNamespaceResolver {
+    private final Resolver mDelegate;
+
+    private RemoteResolverAdapter(Resolver delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteNamespaceResolver create(@NotNull ResourceNamespace.Resolver delegate)
+            throws RemoteException {
+        assert !(delegate instanceof RemoteResolverAdapter);
+
+        return (RemoteNamespaceResolver) UnicastRemoteObject.exportObject(
+                new RemoteResolverAdapter(delegate), 0);
+    }
+
+    @Override
+    public String remotePrefixToUri(String namespacePrefix) {
+        return mDelegate.prefixToUri(namespacePrefix);
+    }
+
+    @Override
+    public String remoteUriToPrefix(String namespaceUri) {
+        return mDelegate.uriToPrefix(namespaceUri);
+    }
+}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
index 2593afc..0013218 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
@@ -74,9 +74,9 @@
     }
 
     @Override
-    public boolean init(Map<String, String> platformProperties, File fontLocation,
+    public boolean init(Map<String, String> platformProperties, File fontLocation, String icuDataPath,
             Map<String, Map<String, Integer>> enumValueMap, RemoteLayoutLog log) {
-        return mBridge.init(platformProperties, fontLocation, enumValueMap,
+        return mBridge.init(platformProperties, fontLocation, icuDataPath, enumValueMap,
                 log != null ? new RemoteLayoutLogAdapter(log) : null);
     }
 
@@ -111,7 +111,7 @@
             String projectKey = mCachedProjectKeys.putIfAbsent(remoteParams.getProjectKey(),
                     remoteParams.getProjectKey());
 
-            // Unpack the remote params and convert it into the local SessionParams
+            // Unpack the remote params and convert it into the local SessionParams.
             SessionParams params = new SessionParams(
                     new RemoteILayoutPullParserAdapter(remoteParams.getLayoutDescription()),
                     remoteParams.getRenderingMode(), projectKey,
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/ServerMain.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/ServerMain.java
index 09c27fd..4c23afc 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/ServerMain.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/ServerMain.java
@@ -21,6 +21,7 @@
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
@@ -36,6 +37,7 @@
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
@@ -65,6 +67,16 @@
         }
     }
 
+    private static Thread createOutputProcessor(String outputProcessorName,
+            InputStream inputStream,
+            Consumer<String> consumer) {
+        BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream));
+        Thread thread = new Thread(() -> inputReader.lines().forEach(consumer));
+        thread.setName(outputProcessorName);
+        thread.start();
+        return thread;
+    }
+
     /**
      * This will start a new JVM and connect to the new JVM RMI registry.
      * <p/>
@@ -110,30 +122,31 @@
                 .start();
 
         BlockingQueue<String> outputQueue = new ArrayBlockingQueue<>(10);
-        Thread outputThread = new Thread(() -> {
-            BufferedReader inputStream = new BufferedReader(
-                    new InputStreamReader(process.getInputStream()));
-            inputStream.lines()
-                    .forEach(outputQueue::offer);
-
-        });
-        outputThread.setName("output thread");
-        outputThread.start();
+        Thread outputThread = createOutputProcessor("output", process.getInputStream(),
+                outputQueue::offer);
+        Thread errorThread = createOutputProcessor("error", process.getErrorStream(),
+                System.err::println);
 
         Runnable killServer = () -> {
             process.destroyForcibly();
             outputThread.interrupt();
+            errorThread.interrupt();
             try {
                 outputThread.join();
             } catch (InterruptedException ignore) {
             }
+
+            try {
+                errorThread.join();
+            } catch (InterruptedException ignore) {
+            }
         };
 
         // Try to read the "Running on port" line in 10 lines. If it's not there just fail.
         for (int i = 0; i < 10; i++) {
-            String line = outputQueue.poll(1000, TimeUnit.SECONDS);
+            String line = outputQueue.poll(5, TimeUnit.SECONDS);
 
-            if (line.startsWith(RUNNING_SERVER_STR)) {
+            if (line != null && line.startsWith(RUNNING_SERVER_STR)) {
                 int runningPort = Integer.parseInt(line.substring(RUNNING_SERVER_STR.length()));
                 System.out.println("Running on port " + runningPort);
 
@@ -182,6 +195,14 @@
         throw lastException;
     }
 
+    /**
+     * Starts an RMI server that runs in the current JVM. Only for debugging.
+     */
+    public static ServerMain startLocalJvmServer() throws RemoteException {
+        System.err.println("Starting server in the local JVM");
+        return startServer(REGISTRY_BASE_PORT, 10);
+    }
+
     public static void main(String[] args) throws RemoteException {
         int basePort = args.length > 0 ? Integer.parseInt(args[0]) : REGISTRY_BASE_PORT;
         int limit = args.length > 1 ? Integer.parseInt(args[1]) : 10;
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteActionBarCallbackAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteActionBarCallbackAdapter.java
index b5c040e..d87a94f 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteActionBarCallbackAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteActionBarCallbackAdapter.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.remote.server.adapters;
 
 import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.layout.remote.api.RemoteActionBarCallback;
 import com.android.tools.layoutlib.annotations.NotNull;
 
@@ -31,9 +32,9 @@
     }
 
     @Override
-    public List<String> getMenuIdNames() {
+    public List<ResourceReference> getMenuIds() {
         try {
-            return mDelegate.getMenuIdNames();
+            return mDelegate.getMenuIds();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteILayoutPullParserAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteILayoutPullParserAdapter.java
index b717feb..3ca0176 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteILayoutPullParserAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteILayoutPullParserAdapter.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.remote.server.adapters;
 
 import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.ResourceNamespace;
 import com.android.layout.remote.api.RemoteILayoutPullParser;
 import com.android.tools.layoutlib.annotations.NotNull;
 
@@ -38,9 +39,11 @@
     }
 
     @Override
-    public ILayoutPullParser getParser(String layoutName) {
-        throw new UnsupportedOperationException();
+    public ResourceNamespace getLayoutNamespace() {
+        try {
+            return ((RemoteILayoutPullParser) mDelegate).getLayoutNamespace();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
     }
-
-
 }
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
index b685098..a6885ef 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -20,17 +20,13 @@
 import com.android.ide.common.rendering.api.AdapterBinding;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.ide.common.rendering.api.ParserFactory;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.SessionParams.Key;
 import com.android.layout.remote.api.RemoteLayoutlibCallback;
-import com.android.layout.remote.api.RemoteLayoutlibCallback.RemoteResolveResult;
 import com.android.layoutlib.bridge.MockView;
-import com.android.resources.ResourceType;
 import com.android.tools.layoutlib.annotations.NotNull;
 import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.util.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -170,17 +166,7 @@
     }
 
     @Override
-    public Pair<ResourceType, String> resolveResourceId(int id) {
-        try {
-            RemoteResolveResult result = mDelegate.resolveResourceId(id);
-            return result != null ? result.asPair() : null;
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public String resolveResourceId(int[] id) {
+    public ResourceReference resolveResourceId(int id) {
         try {
             return mDelegate.resolveResourceId(id);
         } catch (RemoteException e) {
@@ -189,20 +175,15 @@
     }
 
     @Override
-    public Integer getResourceId(ResourceType type, String name) {
+    public int getOrGenerateResourceId(ResourceReference resource) {
         try {
-            return mDelegate.getResourceId(type, name);
+            return mDelegate.getOrGenerateResourceId(resource);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
-    public ILayoutPullParser getParser(String layoutName) {
-        return null;
-    }
-
-    @Override
     public ILayoutPullParser getParser(ResourceValue layoutResource) {
         try {
             return new RemoteILayoutPullParserAdapter(mDelegate.getParser(layoutResource));
@@ -255,23 +236,32 @@
     }
 
     @Override
-    public ParserFactory getParserFactory() {
+    public Class<?> findClass(String name) throws ClassNotFoundException {
+        return mPathClassLoader.loadClass(name);
+    }
+
+    @Override
+    public XmlPullParser createXmlParserForPsiFile(String fileName) {
         try {
-            return new RemoteParserFactoryAdapter(mDelegate.getParserFactory());
+            return new RemoteXmlPullParserAdapter(mDelegate.createXmlParserForPsiFile(fileName));
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
     }
 
     @Override
-    public Class<?> findClass(String name) throws ClassNotFoundException {
-        return mPathClassLoader.loadClass(name);
+    public XmlPullParser createXmlParserForFile(String fileName) {
+        try {
+            return new RemoteXmlPullParserAdapter(mDelegate.createXmlParserForFile(fileName));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     @Override
-    public XmlPullParser getXmlFileParser(String fileName) {
+    public XmlPullParser createXmlParser() {
         try {
-            return new RemoteXmlPullParserAdapter(mDelegate.getXmlFileParser(fileName));
+            return new RemoteXmlPullParserAdapter(mDelegate.createXmlParser());
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteParserFactoryAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteParserFactoryAdapter.java
deleted file mode 100644
index f4ce421..0000000
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteParserFactoryAdapter.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.remote.server.adapters;
-
-
-import com.android.ide.common.rendering.api.ParserFactory;
-import com.android.layout.remote.api.RemoteParserFactory;
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.rmi.RemoteException;
-
-class RemoteParserFactoryAdapter extends ParserFactory {
-    private final RemoteParserFactory mDelegate;
-
-    RemoteParserFactoryAdapter(@NotNull RemoteParserFactory remote) {
-        mDelegate = remote;
-    }
-
-    @Override
-    public XmlPullParser createParser(String debugName) throws XmlPullParserException {
-        try {
-            return new RemoteXmlPullParserAdapter(mDelegate.createParser(debugName));
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderResourcesAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderResourcesAdapter.java
index 3f261df..2e19bf7 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderResourcesAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderResourcesAdapter.java
@@ -22,11 +22,12 @@
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
 import com.android.layout.remote.api.RemoteRenderResources;
-import com.android.resources.ResourceType;
+import com.android.layout.remote.api.RemoteResourceValue;
 import com.android.tools.layoutlib.annotations.NotNull;
 
 import java.rmi.RemoteException;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class RemoteRenderResourcesAdapter extends RenderResources {
     private final RemoteRenderResources mDelegate;
@@ -36,25 +37,14 @@
     }
 
     @Override
-    public void setFrameworkResourceIdProvider(FrameworkResourceIdProvider provider) {
-        // Ignored for remote operations.
-    }
-
-    @Override
     public void setLogger(LayoutLog logger) {
         // Ignored for remote operations.
     }
 
-    @SuppressWarnings("deprecation")
-    @Override
-    public StyleResourceValue getCurrentTheme() {
-        throw new UnsupportedOperationException();
-    }
-
     @Override
     public StyleResourceValue getDefaultTheme() {
         try {
-            return mDelegate.getDefaultTheme();
+            return mDelegate.getDefaultTheme().toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -63,7 +53,7 @@
     @Override
     public void applyStyle(StyleResourceValue theme, boolean useAsPrimary) {
         try {
-            mDelegate.applyStyle(theme, useAsPrimary);
+            mDelegate.applyStyle(RemoteResourceValue.fromResourceValue(theme), useAsPrimary);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -81,43 +71,9 @@
     @Override
     public List<StyleResourceValue> getAllThemes() {
         try {
-            return mDelegate.getAllThemes();
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public StyleResourceValue getTheme(String name, boolean frameworkTheme) {
-        try {
-            return mDelegate.getTheme(name, frameworkTheme);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme) {
-        try {
-            return mDelegate.themeIsParentOf(parentTheme, childTheme);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName) {
-        try {
-            return mDelegate.getFrameworkResource(resourceType, resourceName);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public ResourceValue getProjectResource(ResourceType resourceType, String resourceName) {
-        try {
-            return mDelegate.getProjectResource(resourceType, resourceName);
+            return mDelegate.getAllThemes().stream()
+                    .map(RemoteResourceValue::toResourceValue)
+                    .collect(Collectors.toList());
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -126,7 +82,7 @@
     @Override
     public ResourceValue findItemInTheme(ResourceReference attr) {
         try {
-            return mDelegate.findItemInTheme(attr);
+            return mDelegate.findItemInTheme(attr).toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -135,7 +91,8 @@
     @Override
     public ResourceValue findItemInStyle(StyleResourceValue style, ResourceReference attr) {
         try {
-            return mDelegate.findItemInStyle(style, attr);
+            return mDelegate.findItemInStyle(RemoteResourceValue.fromResourceValue(style), attr)
+                    .toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -144,7 +101,8 @@
     @Override
     public ResourceValue dereference(ResourceValue resourceValue) {
         try {
-            return mDelegate.dereference(resourceValue);
+            return mDelegate.dereference(RemoteResourceValue.fromResourceValue(resourceValue))
+                    .toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -154,18 +112,7 @@
     @Override
     public ResourceValue getUnresolvedResource(ResourceReference reference) {
         try {
-            return mDelegate.getUnresolvedResource(reference);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public ResourceValue resolveValue(ResourceType type, String name, String value,
-            boolean isFrameworkValue) {
-        try {
-            return mDelegate.resolveValue(type, name, value, isFrameworkValue);
+            return mDelegate.getUnresolvedResource(reference).toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -174,7 +121,8 @@
     @Override
     public ResourceValue resolveResValue(ResourceValue value) {
         try {
-            return mDelegate.resolveValue(value);
+            return mDelegate.resolveValue(RemoteResourceValue.fromResourceValue(value))
+                    .toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -183,10 +131,19 @@
     @Override
     public StyleResourceValue getParent(StyleResourceValue style) {
         try {
-            return mDelegate.getParent(style);
+            return mDelegate.getParent(RemoteResourceValue.fromResourceValue(style))
+                    .toResourceValue();
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
     }
 
+    @Override
+    public StyleResourceValue getStyle(ResourceReference reference) {
+        try {
+            return mDelegate.getStyle(reference).toResourceValue();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/remote/tests/out/failures/activity.png b/remote/tests/out/failures/activity.png
deleted file mode 100644
index 2920b7d..0000000
--- a/remote/tests/out/failures/activity.png
+++ /dev/null
Binary files differ
diff --git a/remote/tests/out/failures/delta-activity.png b/remote/tests/out/failures/delta-activity.png
deleted file mode 100644
index 32d9415..0000000
--- a/remote/tests/out/failures/delta-activity.png
+++ /dev/null
Binary files differ
diff --git a/remote/tests/out/failures/delta-remote_component_load.png b/remote/tests/out/failures/delta-remote_component_load.png
deleted file mode 100644
index ddb7f64..0000000
--- a/remote/tests/out/failures/delta-remote_component_load.png
+++ /dev/null
Binary files differ
diff --git a/remote/tests/out/failures/remote_component_load.png b/remote/tests/out/failures/remote_component_load.png
deleted file mode 100644
index 0ed85d1..0000000
--- a/remote/tests/out/failures/remote_component_load.png
+++ /dev/null
Binary files differ
diff --git a/remote/tests/src/RemoteBridgeTest.java b/remote/tests/src/RemoteBridgeTest.java
index e0b0610..6ad10cb 100644
--- a/remote/tests/src/RemoteBridgeTest.java
+++ b/remote/tests/src/RemoteBridgeTest.java
@@ -63,8 +63,11 @@
 
     @Before
     public void setupServer() throws IOException, NotBoundException, InterruptedException {
+        long startTime = System.currentTimeMillis();
         mServerMain = ServerMain.forkAndStartServer(ServerMain.REGISTRY_BASE_PORT, 10);
         mClient = RemoteBridgeClient.getRemoteBridge(mServerMain.getPort());
+        System.out.printf("Server started in %dms\n", System.currentTimeMillis() - startTime);
+        startTime = System.currentTimeMillis();
 
         File data_dir = new File(PLATFORM_DIR, "data");
         File res = new File(data_dir, "res");
@@ -72,8 +75,10 @@
         File buildProp = new File(PLATFORM_DIR, "build.prop");
         File attrs = new File(res, "values" + File.separator + "attrs.xml");
 
-        mClient.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
+        mClient.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null,
                 ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+        System.out.printf("Remote client init took %dms\n",
+                System.currentTimeMillis() - startTime);
     }
 
     @After