Merge Android Pie into master

Bug: 112104996
Change-Id: I91f57f766e04b216f4c4c3ad48fb0e7a852fd7f1
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..b879e0e
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,76 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <option name="RIGHT_MARGIN" value="100" />
+    <option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
+    <option name="WRAP_COMMENTS" value="true" />
+    <JavaCodeStyleSettings>
+      <option name="FIELD_NAME_PREFIX" value="m" />
+      <option name="STATIC_FIELD_NAME_PREFIX" value="s" />
+      <option name="CLASS_NAMES_IN_JAVADOC" value="3" />
+      <option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
+      <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
+      <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
+      <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
+        <value />
+      </option>
+      <option name="IMPORT_LAYOUT_TABLE">
+        <value>
+          <package name="com.android" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="org" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="android" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="java" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="" withSubpackages="true" static="false" />
+          <emptyLine />
+          <package name="" withSubpackages="true" static="true" />
+        </value>
+      </option>
+      <option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
+      <option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
+      <option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />
+      <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
+    </JavaCodeStyleSettings>
+    <XML>
+      <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+    </XML>
+    <codeStyleSettings language="JAVA">
+      <option name="KEEP_LINE_BREAKS" value="false" />
+      <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
+      <option name="CALL_PARAMETERS_WRAP" value="1" />
+      <option name="METHOD_PARAMETERS_WRAP" value="1" />
+      <option name="THROWS_LIST_WRAP" value="1" />
+      <option name="EXTENDS_KEYWORD_WRAP" value="1" />
+      <option name="THROWS_KEYWORD_WRAP" value="1" />
+      <option name="BINARY_OPERATION_WRAP" value="1" />
+      <option name="TERNARY_OPERATION_WRAP" value="1" />
+      <option name="ARRAY_INITIALIZER_WRAP" value="1" />
+      <option name="ASSIGNMENT_WRAP" value="1" />
+      <option name="ASSERT_STATEMENT_WRAP" value="1" />
+      <option name="IF_BRACE_FORCE" value="3" />
+      <option name="DOWHILE_BRACE_FORCE" value="3" />
+      <option name="WHILE_BRACE_FORCE" value="3" />
+      <option name="FOR_BRACE_FORCE" value="3" />
+      <option name="WRAP_LONG_LINES" value="true" />
+      <arrangement>
+        <groups>
+          <group>
+            <type>GETTERS_AND_SETTERS</type>
+            <order>KEEP</order>
+          </group>
+          <group>
+            <type>OVERRIDDEN_METHODS</type>
+            <order>KEEP</order>
+          </group>
+        </groups>
+      </arrangement>
+    </codeStyleSettings>
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+    </codeStyleSettings>
+  </code_scheme>
+</component>
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 4654a5c..8d61f6c 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -6,6 +6,10 @@
       <module fileurl="file://$PROJECT_DIR$/common/common.iml" filepath="$PROJECT_DIR$/common/common.iml" />
       <module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
       <module fileurl="file://$PROJECT_DIR$/legacy/legacy.iml" filepath="$PROJECT_DIR$/legacy/legacy.iml" />
+      <module fileurl="file://$PROJECT_DIR$/remote/client/remote client.iml" filepath="$PROJECT_DIR$/remote/client/remote client.iml" group="remote" />
+      <module fileurl="file://$PROJECT_DIR$/remote/common/remote common.iml" filepath="$PROJECT_DIR$/remote/common/remote common.iml" group="remote" />
+      <module fileurl="file://$PROJECT_DIR$/remote/server/remote server.iml" filepath="$PROJECT_DIR$/remote/server/remote server.iml" group="remote" />
+      <module fileurl="file://$PROJECT_DIR$/remote/tests/remote tests.iml" filepath="$PROJECT_DIR$/remote/tests/remote tests.iml" group="remote" />
       <module fileurl="file://$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" filepath="$PROJECT_DIR$/studio-custom-widgets/studio-android-widgets.iml" />
     </modules>
   </component>
diff --git a/Android.mk b/Android.mk
index a9f09f4..e9aa138 100644
--- a/Android.mk
+++ b/Android.mk
@@ -13,6 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+ifeq (,$(PRODUCT_MINIMIZE_JAVA_DEBUG_INFO))
+
 LOCAL_PATH := $(my-dir)
 include $(CLEAR_VARS)
 
@@ -88,3 +90,5 @@
 # Include the subdir makefiles.
 #
 include $(call all-makefiles-under,$(LOCAL_PATH))
+
+endif
diff --git a/bridge/.idea/bridge.iml b/bridge/.idea/bridge.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/bridge/.idea/bridge.iml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/bridge/.idea/compiler.xml b/bridge/.idea/compiler.xml
new file mode 100644
index 0000000..40ed937
--- /dev/null
+++ b/bridge/.idea/compiler.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <wildcardResourcePatterns>
+      <entry name="!?*.java" />
+      <entry name="!?*.form" />
+      <entry name="!?*.class" />
+      <entry name="!?*.groovy" />
+      <entry name="!?*.scala" />
+      <entry name="!?*.flex" />
+      <entry name="!?*.kt" />
+      <entry name="!?*.clj" />
+    </wildcardResourcePatterns>
+  </component>
+</project>
\ No newline at end of file
diff --git a/bridge/.idea/encodings.xml b/bridge/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/bridge/.idea/encodings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="PROJECT" charset="UTF-8" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/bridge/.idea/misc.xml b/bridge/.idea/misc.xml
new file mode 100644
index 0000000..54f4c9f
--- /dev/null
+++ b/bridge/.idea/misc.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MavenImportPreferences">
+    <option name="generalSettings">
+      <MavenGeneralSettings>
+        <option name="mavenHome" value="Bundled (Maven 3)" />
+      </MavenGeneralSettings>
+    </option>
+  </component>
+  <component name="ProjectDictionaryState">
+    <dictionary name="diegoperez" />
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" />
+</project>
\ No newline at end of file
diff --git a/bridge/.idea/modules.xml b/bridge/.idea/modules.xml
new file mode 100644
index 0000000..4683164
--- /dev/null
+++ b/bridge/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/bridge.iml" filepath="$PROJECT_DIR$/.idea/bridge.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/bridge/.idea/workspace.xml b/bridge/.idea/workspace.xml
new file mode 100644
index 0000000..b48406a
--- /dev/null
+++ b/bridge/.idea/workspace.xml
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="8a92e766-359b-4589-8d1f-ac1a44fb0c89" name="Default" comment="" />
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="TRACKING_ENABLED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf />
+  </component>
+  <component name="GradleLocalSettings">
+    <option name="modificationStamps">
+      <map>
+        <entry key="$PROJECT_DIR$/../../../../nyc-dev/tools/base/layoutlib-api" value="1486045798848" />
+      </map>
+    </option>
+    <option name="externalProjectsViewState">
+      <projects_view />
+    </option>
+  </component>
+  <component name="ProjectFrameBounds">
+    <option name="x" value="2560" />
+    <option name="y" value="18" />
+    <option name="width" value="3840" />
+    <option name="height" value="2123" />
+  </component>
+  <component name="ProjectInspectionProfilesVisibleTreeState">
+    <entry key="Project Default">
+      <profile-state>
+        <expanded-state>
+          <State>
+            <id />
+          </State>
+          <State>
+            <id>Android</id>
+          </State>
+          <State>
+            <id>Android Lint</id>
+          </State>
+        </expanded-state>
+        <selected-state>
+          <State>
+            <id>Android</id>
+          </State>
+        </selected-state>
+      </profile-state>
+    </entry>
+  </component>
+  <component name="ProjectView">
+    <navigator currentView="ProjectPane" proportions="" version="1">
+      <flattenPackages />
+      <showMembers />
+      <showModules />
+      <showLibraryContents />
+      <hideEmptyPackages />
+      <abbreviatePackageNames />
+      <autoscrollToSource />
+      <autoscrollFromSource />
+      <sortByType />
+      <manualOrder />
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="PackagesPane" />
+      <pane id="Scratches" />
+      <pane id="Scope" />
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="bridge" type="b2602c69:ProjectViewProjectNode" />
+              <item name="bridge" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="GoToClass.includeLibraries" value="false" />
+    <property name="GoToClass.toSaveIncludeLibraries" value="false" />
+    <property name="GoToFile.includeJavaFiles" value="false" />
+    <property name="MemberChooser.sorted" value="false" />
+    <property name="MemberChooser.showClasses" value="true" />
+    <property name="MemberChooser.copyJavadoc" value="false" />
+    <property name="options.lastSelected" value="Errors" />
+    <property name="options.splitter.main.proportions" value="0.3" />
+    <property name="options.splitter.details.proportions" value="0.2" />
+    <property name="options.searchVisible" value="true" />
+    <property name="settings.editor.selected.configurable" value="editor.preferences.import" />
+    <property name="settings.editor.splitter.proportion" value="0.2" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="RunManager">
+    <configuration default="true" type="Applet" factoryName="Applet">
+      <option name="WIDTH" value="400" />
+      <option name="HEIGHT" value="300" />
+      <option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
+      <module />
+    </configuration>
+    <configuration default="true" type="Application" factoryName="Application">
+      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="VM_PARAMETERS" />
+      <option name="PROGRAM_PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="ENABLE_SWING_INSPECTOR" value="false" />
+      <option name="ENV_VARIABLES" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <module name="" />
+      <envs />
+    </configuration>
+    <configuration default="true" type="JUnit" factoryName="JUnit">
+      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+      <module name="" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="TEST_OBJECT" value="class" />
+      <option name="VM_PARAMETERS" value="-ea" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+      <option name="ENV_VARIABLES" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="moduleWithDependencies" />
+      </option>
+      <envs />
+      <patterns />
+    </configuration>
+    <configuration default="true" type="Remote" factoryName="Remote">
+      <option name="USE_SOCKET_TRANSPORT" value="true" />
+      <option name="SERVER_MODE" value="false" />
+      <option name="SHMEM_ADDRESS" value="javadebug" />
+      <option name="HOST" value="localhost" />
+      <option name="PORT" value="5005" />
+    </configuration>
+    <configuration default="true" type="TestNG" factoryName="TestNG">
+      <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+      <module name="" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" />
+      <option name="SUITE_NAME" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" />
+      <option name="METHOD_NAME" />
+      <option name="GROUP_NAME" />
+      <option name="TEST_OBJECT" value="CLASS" />
+      <option name="VM_PARAMETERS" value="-ea" />
+      <option name="PARAMETERS" />
+      <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+      <option name="OUTPUT_DIRECTORY" />
+      <option name="ANNOTATION_TYPE" />
+      <option name="ENV_VARIABLES" />
+      <option name="PASS_PARENT_ENVS" value="true" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="moduleWithDependencies" />
+      </option>
+      <option name="USE_DEFAULT_REPORTERS" value="false" />
+      <option name="PROPERTIES_FILE" />
+      <envs />
+      <properties />
+      <listeners />
+    </configuration>
+    <configuration default="true" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" factoryName="Plugin">
+      <module name="" />
+      <option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
+      <option name="PROGRAM_PARAMETERS" />
+      <predefined_log_file id="idea.log" enabled="true" />
+    </configuration>
+    <configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
+      <Host>localhost</Host>
+      <Port>5050</Port>
+    </configuration>
+  </component>
+  <component name="ShelveChangesManager" show_recycled="false">
+    <option name="remove_strategy" value="false" />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="8a92e766-359b-4589-8d1f-ac1a44fb0c89" name="Default" comment="" />
+      <created>1511883242491</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1511883242491</updated>
+    </task>
+    <servers />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="2560" y="18" width="3840" height="2123" extended-state="0" />
+    <layout>
+      <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32860184" sideWeight="0.49526584" order="6" side_tool="false" content_ui="tabs" />
+      <window_info id="Palette&#9;" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.34767523" sideWeight="0.43609673" order="7" side_tool="true" content_ui="tabs" />
+      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.34698126" sideWeight="0.5639033" order="2" side_tool="false" content_ui="tabs" />
+      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.32338655" sideWeight="0.4821803" order="9" side_tool="false" content_ui="tabs" />
+      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.35600278" sideWeight="0.49666223" order="10" side_tool="false" content_ui="tabs" />
+      <window_info id="Designer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+      <window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.3227345" sideWeight="0.48438585" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="JProfiler" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4338588" sideWeight="0.47117063" order="12" side_tool="false" content_ui="tabs" />
+      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.24960127" sideWeight="0.49757785" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3650243" sideWeight="0.85578585" order="3" side_tool="false" content_ui="tabs" />
+      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.16108453" sideWeight="0.51561415" order="7" side_tool="true" content_ui="tabs" />
+      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+      <window_info id="Nl-Palette" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
+      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="Gerrit" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.333564" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
+      <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
+      <window_info id="Analyze Dataflow to" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32941177" sideWeight="0.49821427" order="13" side_tool="false" content_ui="tabs" />
+      <window_info id="Properties" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+      <window_info id="Capture Tool" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.22288676" sideWeight="0.49111968" order="2" side_tool="false" content_ui="combo" />
+      <window_info id="Memory View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.00996016" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+      <window_info id="Inspection Results" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3293623" sideWeight="0.49248666" order="16" side_tool="false" content_ui="tabs" />
+      <window_info id="Dynamic Properties" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32988048" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
+      <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.13046496" sideWeight="0.26683939" order="8" side_tool="false" content_ui="tabs" />
+      <window_info id="Image Layers" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+      <window_info id="Capture Analysis" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+      <window_info id="Run Dashboard" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="15" side_tool="false" content_ui="tabs" />
+      <window_info id="Documentation" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="FLOATING" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="12" side_tool="false" content_ui="tabs" x="1292" y="82" width="2360" height="1327" />
+      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+      <window_info id="Coverage" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25976095" sideWeight="0.5061444" order="11" side_tool="true" content_ui="tabs" />
+      <window_info id="Runtime Dashboard" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="14" side_tool="false" content_ui="tabs" />
+      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.2984039" sideWeight="0.8713299" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Theme Preview" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
+    </layout>
+  </component>
+  <component name="VcsContentAnnotationSettings">
+    <option name="myLimit" value="2678400000" />
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager />
+    <watches-manager />
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/bridge.iml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+  </component>
+  <component name="masterDetails">
+    <states>
+      <state key="ProjectJDKs.UI">
+        <settings>
+          <last-edited>1.6</last-edited>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.2" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+      <state key="ScopeChooserConfigurable.UI">
+        <settings>
+          <splitter-proportions>
+            <option name="proportions">
+              <list>
+                <option value="0.23280424" />
+              </list>
+            </option>
+          </splitter-proportions>
+        </settings>
+      </state>
+    </states>
+  </component>
+</project>
\ No newline at end of file
diff --git a/bridge/Android.mk b/bridge/Android.mk
index 333e6bb..89a34a3 100644
--- a/bridge/Android.mk
+++ b/bridge/Android.mk
@@ -32,6 +32,8 @@
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
+$(call dist-for-goals, layoutlib, $(LOCAL_INSTALLED_MODULE))
+
 # Build all sub-directories
 include $(call all-makefiles-under,$(LOCAL_PATH))
 
diff --git a/bridge/src/android/content/res/AssetManager_Delegate.java b/bridge/src/android/content/res/AssetManager_Delegate.java
index b4d5288..3b47ea7 100644
--- a/bridge/src/android/content/res/AssetManager_Delegate.java
+++ b/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -20,6 +20,9 @@
 
 import android.util.SparseArray;
 
+import java.io.IOException;
+import java.io.InputStream;
+
 /**
  * Delegate used to provide implementation of a select few native methods of {@link AssetManager}
  * <p/>
@@ -30,6 +33,20 @@
 public class AssetManager_Delegate {
 
     @LayoutlibDelegate
+    public static InputStream open(AssetManager mgr, String fileName) throws IOException {
+        return mgr.open_Original(fileName);
+    }
+
+    @LayoutlibDelegate
+    public static InputStream open(AssetManager mgr, String fileName, int accessMode)
+            throws IOException {
+        if (!(mgr instanceof BridgeAssetManager)) {
+            return mgr.open_Original(fileName, accessMode);
+        }
+        return ((BridgeAssetManager) mgr).getAssetRepository().openAsset(fileName, accessMode);
+    }
+
+    @LayoutlibDelegate
     /*package*/ static long newTheme(AssetManager manager) {
         return Resources_Theme_Delegate.getDelegateManager()
                 .addNewDelegate(new Resources_Theme_Delegate());
diff --git a/bridge/src/android/content/res/BridgeAssetManager.java b/bridge/src/android/content/res/BridgeAssetManager.java
index 2691e56..a1a4a19 100644
--- a/bridge/src/android/content/res/BridgeAssetManager.java
+++ b/bridge/src/android/content/res/BridgeAssetManager.java
@@ -36,7 +36,6 @@
             // Note that AssetManager() creates a system AssetManager and we override it
             // with our BridgeAssetManager.
             AssetManager.sSystem = new BridgeAssetManager();
-            AssetManager.sSystem.makeStringBlocks(null);
         }
         return AssetManager.sSystem;
     }
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index 5536c4f..9505993 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -29,7 +29,6 @@
 import com.android.resources.ResourceType;
 
 import android.annotation.Nullable;
-import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.graphics.Typeface;
 import android.graphics.Typeface_Accessor;
@@ -676,6 +675,13 @@
             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]),
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index c1e9cd3..77ae90f 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -651,6 +651,26 @@
     }
 
     @LayoutlibDelegate
+    static float getFloat(Resources resources, int id) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    try {
+                        return Float.parseFloat(v);
+                    } catch (NumberFormatException ignore) {
+                    }
+                }
+            }
+        }
+        return 0;
+    }
+
+    @LayoutlibDelegate
     static boolean getBoolean(Resources resources, int id) throws NotFoundException {
         Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
@@ -866,31 +886,26 @@
     }
 
     @LayoutlibDelegate
+    static void getValueForDensity(Resources resources, int id, int density, TypedValue outValue,
+            boolean resolveRefs) throws NotFoundException {
+        getValue(resources, id, outValue, resolveRefs);
+    }
+
+    @LayoutlibDelegate
     static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
-        if (value != null) {
-            String v = value.getSecond().getValue();
+        if (v != null) {
+            ResourceValue value = v.getSecond();
 
-            if (v != null) {
-                // check this is a file
-                File f = new File(v);
-                if (f.isFile()) {
-                    try {
-                        XmlPullParser parser = ParserFactory.create(f);
-
-                        return new BridgeXmlBlockParser(parser, getContext(resources),
-                                mPlatformResourceFlag[0]);
-                    } catch (XmlPullParserException e) {
-                        NotFoundException newE = new NotFoundException();
-                        newE.initCause(e);
-                        throw newE;
-                    } catch (FileNotFoundException e) {
-                        NotFoundException newE = new NotFoundException();
-                        newE.initCause(e);
-                        throw newE;
-                    }
-                }
+            try {
+                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*/);
+                // we'll return null below.
+            } catch (FileNotFoundException e) {
+                // this shouldn't happen since we check above.
             }
         }
 
diff --git a/bridge/src/android/content/res/Resources_Theme_Delegate.java b/bridge/src/android/content/res/Resources_Theme_Delegate.java
index f1e8fc2..8aa9216 100644
--- a/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -56,7 +56,8 @@
             Resources thisResources, Theme thisTheme,
             int[] attrs) {
         boolean changed = setupResources(thisTheme);
-        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs);
+        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().internalObtainStyledAttributes(
+                0, attrs);
         ta.setTheme(thisTheme);
         restoreResources(changed);
         return ta;
@@ -68,8 +69,8 @@
             int resid, int[] attrs)
             throws NotFoundException {
         boolean changed = setupResources(thisTheme);
-        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid,
-                attrs);
+        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().internalObtainStyledAttributes(
+                resid, attrs);
         ta.setTheme(thisTheme);
         restoreResources(changed);
         return ta;
@@ -80,7 +81,7 @@
             Resources thisResources, Theme thisTheme,
             AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
         boolean changed = setupResources(thisTheme);
-        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(set,
+        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().internalObtainStyledAttributes(set,
                 attrs, defStyleAttr, defStyleRes);
         ta.setTheme(thisTheme);
         restoreResources(changed);
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
index f1c63e6..9260099 100644
--- a/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ b/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -477,40 +477,39 @@
 
     @LayoutlibDelegate
     /*package*/ static void nDrawText(long nativeCanvas, char[] text, int index, int count,
-            float startX, float startY, int flags, long paint, long typeface) {
+            float startX, float startY, int flags, long paint) {
         drawText(nativeCanvas, text, index, count, startX, startY, flags,
-                paint, typeface);
+                paint);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nDrawText(long nativeCanvas, String text,
-            int start, int end, float x, float y, final int flags, long paint,
-            long typeface) {
+            int start, int end, float x, float y, final int flags, long paint) {
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
+        nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
     }
 
     @LayoutlibDelegate
     /*package*/ static void nDrawTextRun(long nativeCanvas, String text,
             int start, int end, int contextStart, int contextEnd,
-            float x, float y, boolean isRtl, long paint, long typeface) {
+            float x, float y, boolean isRtl, long paint) {
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
         drawText(nativeCanvas, buffer, 0, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR,
-                paint,
-                typeface);
+                paint);
     }
 
     @LayoutlibDelegate
     /*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 typeface) {
-        drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint, typeface);
+            float x, float y, boolean isRtl, long paint,
+            long nativeMeasuredText, int measuredTextOffset) {
+        drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint);
     }
 
     @LayoutlibDelegate
@@ -519,7 +518,7 @@
             int count, long path,
             float hOffset,
             float vOffset, int bidiFlags,
-            long paint, long typeface) {
+            long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -530,8 +529,7 @@
             String text, long path,
             float hOffset,
             float vOffset,
-            int bidiFlags, long paint,
-            long typeface) {
+            int bidiFlags, long paint) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -576,16 +574,13 @@
 
     private static void drawText(long nativeCanvas, final char[] text, final int index,
             final int count, final float startX, final float startY, final int bidiFlags,
-            long paint, final long typeface) {
+            long paint) {
 
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 (graphics, paintDelegate) -> {
                     // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
                     // Any change to this method should be reflected in Paint.measureText
 
-                    // assert that the typeface passed is actually the one stored in paint.
-                    assert (typeface == paintDelegate.mNativeTypeface);
-
                     // Paint.TextAlign indicates how the text is positioned relative to X.
                     // LEFT is the default and there's nothing to do.
                     float x = startX;
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
index 63691c3..9a102c1 100644
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ b/bridge/src/android/graphics/BidiRenderer.java
@@ -33,6 +33,7 @@
 import java.awt.font.GlyphVector;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -42,7 +43,10 @@
  */
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
-    private static String JAVA_VENDOR = System.getProperty("java.vendor");
+    private static final String JETBRAINS_VENDOR_ID = "JetBrains s.r.o";
+    private static final String JAVA_VENDOR = System.getProperty("java.vendor");
+    /** When scaleX is bigger than this, we need to apply the workaround for http://b.android.com/211659 */
+    private static final double SCALEX_WORKAROUND_LIMIT = 9;
 
     private static class ScriptRun {
         private final int start;
@@ -62,6 +66,8 @@
     // Bounds of the text drawn so far.
     private RectF mBounds;
     private float mBaseline;
+    private final Bidi mBidi = new Bidi();
+
 
     /**
      * @param graphics May be null.
@@ -97,10 +103,10 @@
      */
     public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
             int advancesIndex, boolean draw) {
-        Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
-        mText = bidi.getText();
-        for (int i = 0; i < bidi.countRuns(); i++) {
-            BidiRun visualRun = bidi.getVisualRun(i);
+        mBidi.setPara(Arrays.copyOfRange(mText, start, limit), (byte)getIcuFlags(bidiFlags), null);
+        mText = mBidi.getText();
+        for (int i = 0; i < mBidi.countRuns(); i++) {
+            BidiRun visualRun = mBidi.getVisualRun(i);
             boolean isRtl = visualRun.getDirection() == Bidi.RTL;
             renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
                     advancesIndex, draw);
@@ -197,8 +203,7 @@
 
     private static void logFontWarning() {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                "Some fonts could not be loaded. The rendering may not be perfect. " +
-                        "Try running the IDE with JRE 7.", null, null);
+                "Some fonts could not be loaded. The rendering may not be perfect.", null, null);
     }
 
     /**
@@ -207,28 +212,52 @@
      */
     private void render(int start, int limit, Font font, int flag, float[] advances,
             int advancesIndex, boolean draw) {
+        FontRenderContext frc = mGraphics != null ? mGraphics.getFontRenderContext() :
+                    Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
 
-        FontRenderContext frc;
-        if (mGraphics != null) {
-            frc = mGraphics.getFontRenderContext();
-        } else {
-            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+        boolean frcIsAntialiased = frc.isAntiAliased();
+        boolean useAntialiasing = mPaint.isAntiAliased();
 
-            // Metrics obtained this way don't have anti-aliasing set. So,
-            // we create a new FontRenderContext with anti-aliasing set.
+        if (frcIsAntialiased) {
+            if (!useAntialiasing) {
+                // The context has antialiasing enabled but the paint does not. We need to
+                // disable it
+                frc = new FontRenderContext(font.getTransform(), false,
+                        frc.usesFractionalMetrics());
+            } else {
+                // In this case both the paint and the context antialising match but we need
+                // to check for a bug in the JDK
+                // Workaround for http://b.android.com/211659 (disable antialiasing)
+                if (font.isTransformed()) {
+                    AffineTransform transform = font.getTransform();
+                    if (transform.getScaleX() >= SCALEX_WORKAROUND_LIMIT &&
+                            JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
+                        frc = new FontRenderContext(transform, false, frc.usesFractionalMetrics());
+                    }
+                }
+            }
+        } else if (useAntialiasing) {
+            // The context does not have antialiasing enabled but the paint does. We need to
+            // enable it unless we need to avoid the JDK bug
+
             AffineTransform transform = font.getTransform();
-            if (mPaint.isAntiAliased() &&
-                    // Workaround for http://b.android.com/211659
-                    (transform.getScaleX() <= 9.9 ||
-                    !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
-                frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
+            // Workaround for http://b.android.com/211659 (disable antialiasing)
+            if (transform.getScaleX() < SCALEX_WORKAROUND_LIMIT ||
+                    !JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
+                frc = new FontRenderContext(font.getTransform(), true, frc.usesFractionalMetrics());
             }
         }
+
         GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
         int[] ci = gv.getGlyphCharIndices(0, ng, null);
         if (advances != null) {
             for (int i = 0; i < ng; i++) {
+                if (mText[ci[i]] == '\uFEFF') {
+                    // Workaround for bug in JetBrains JDK
+                    // where the character \uFEFF is associated a glyph with non-zero width
+                    continue;
+                }
                 int adv_idx = advancesIndex + ci[i];
                 advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX();
             }
@@ -285,15 +314,17 @@
     @NonNull
     private static Font getScriptFont(char[] text, int start, int limit, List<FontInfo> fonts) {
         for (FontInfo fontInfo : fonts) {
-            if (fontInfo.mFont == null) {
-                logFontWarning();
-                continue;
-            }
             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;
     }
 
diff --git a/bridge/src/android/graphics/BitmapFactory_Delegate.java b/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 8bd2a7a..ee099e1 100644
--- a/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -101,20 +101,26 @@
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
             Rect padding, Options opts) {
-        opts.inBitmap = null;
+        if (opts != null) {
+            opts.inBitmap = null;
+        }
         return null;
     }
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeAsset(long asset, Rect padding, Options opts) {
-        opts.inBitmap = null;
+        if (opts != null) {
+            opts.inBitmap = null;
+        }
         return null;
     }
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts) {
-        opts.inBitmap = null;
+        if (opts != null) {
+            opts.inBitmap = null;
+        }
         return null;
     }
 
diff --git a/bridge/src/android/graphics/BitmapShader_Delegate.java b/bridge/src/android/graphics/BitmapShader_Delegate.java
index 4914a48..891b07f 100644
--- a/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -31,7 +31,9 @@
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
+import java.awt.image.DataBufferInt;
 import java.awt.image.Raster;
+import java.awt.image.SampleModel;
 
 /**
  * Delegate implementing the native methods of android.graphics.BitmapShader
@@ -189,9 +191,9 @@
                     }
                 }
 
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
+                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
+                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
+                return Raster.createWritableRaster(colorModel, dataBuffer, null);
             }
         }
 
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
index 0064537..6c72cb2 100644
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -608,7 +608,8 @@
         if (delegate == null) {
             return 0;
         }
-        return nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
+        int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
+        return size < 0 ? Integer.MAX_VALUE : size;
 
     }
 
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index 47216ee..a23244b 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -141,9 +141,6 @@
     }
 
     @LayoutlibDelegate
-    public static void nSetHighContrastText(long nativeCanvas, boolean highContrastText){}
-
-    @LayoutlibDelegate
     public static int nGetWidth(long nativeCanvas) {
         // get the delegate from the native int.
         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
@@ -473,6 +470,11 @@
         return sFinalizer;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
+        // Unsupported by layoutlib, do nothing
+    }
+
     private Canvas_Delegate(Bitmap_Delegate bitmap) {
         super(bitmap);
     }
diff --git a/bridge/src/android/graphics/FontFamily_Delegate.java b/bridge/src/android/graphics/FontFamily_Delegate.java
index d8e049a..1ad1f8f 100644
--- a/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -27,7 +27,6 @@
 import android.content.res.AssetManager;
 import android.content.res.BridgeAssetManager;
 import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
 
 import java.awt.Font;
 import java.awt.FontFormatException;
@@ -42,6 +41,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Scanner;
 import java.util.Set;
 
@@ -97,6 +97,28 @@
         Font mFont;
         int mWeight;
         boolean mIsItalic;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            FontInfo fontInfo = (FontInfo) o;
+            return mWeight == fontInfo.mWeight && mIsItalic == fontInfo.mIsItalic;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mWeight, mIsItalic);
+        }
+
+        @Override
+        public String toString() {
+            return "FontInfo{" + "mWeight=" + mWeight + ", mIsItalic=" + mIsItalic + '}';
+        }
     }
 
     // ---- delegate manager ----
@@ -111,7 +133,10 @@
 
 
     // ---- delegate data ----
-    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+
+    // 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.
@@ -147,7 +172,7 @@
         File allFonts = new File(fontLocation, FN_ALL_FONTS_LIST);
         // Current number of fonts is 103. Use the next round number to leave scope for more fonts
         // in the future.
-        Set<String> allFontsList = new HashSet<String>(128);
+        Set<String> allFontsList = new HashSet<>(128);
         Scanner scanner = null;
         try {
             scanner = new Scanner(allFonts);
@@ -179,23 +204,38 @@
         FontInfo desiredStyle = new FontInfo();
         desiredStyle.mWeight = desiredWeight;
         desiredStyle.mIsItalic = isItalic;
-        FontInfo bestFont = null;
-        int bestMatch = Integer.MAX_VALUE;
-        //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
-        for (int i = 0, n = mFonts.size(); i < n; i++) {
-            FontInfo font = mFonts.get(i);
-            int match = computeMatch(font, desiredStyle);
-            if (match < bestMatch) {
-                bestMatch = match;
-                bestFont = font;
-            }
+
+        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;
+
+            //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
+            for (FontInfo font : mFonts.keySet()) {
+                int match = computeMatch(font, desiredStyle);
+                if (match < bestMatch) {
+                    bestMatch = match;
+                    bestFont = font;
+                }
+            }
+
+            // This would mean that we already had the font so it should be in the set
+            assert bestMatch != 0;
+        }
+
         if (bestFont == null) {
             return null;
         }
-        if (bestMatch == 0) {
-            return bestFont.mFont;
-        }
+
+
         // Derive the font as required and add it to the list of Fonts.
         deriveFont(bestFont, desiredStyle);
         addFont(desiredStyle);
@@ -411,14 +451,6 @@
         sManager.removeJavaReferenceFor(builderPtr);
     }
 
-    /**
-     * @see FontFamily#allowUnsupportedFont
-     */
-    @LayoutlibDelegate
-    /*package*/ static void nAllowUnsupportedFont(long builderPtr) {
-        // Do nothing here as this is used for Minikin fonts
-    }
-
     // ---- private helper methods ----
 
     private void init() {
@@ -461,19 +493,7 @@
     }
 
     private boolean addFont(@NonNull FontInfo fontInfo) {
-        int weight = fontInfo.mWeight;
-        boolean isItalic = fontInfo.mIsItalic;
-        // The list is usually just two fonts big. So iterating over all isn't as bad as it looks.
-        // It's biggest for roboto where the size is 12.
-        //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
-        for (int i = 0, n = mFonts.size(); i < n; i++) {
-            FontInfo font = mFonts.get(i);
-            if (font.mWeight == weight && font.mIsItalic == isItalic) {
-                return false;
-            }
-        }
-        mFonts.add(fontInfo);
-        return true;
+        return mFonts.putIfAbsent(fontInfo, fontInfo.mFont) == null;
     }
 
     /**
@@ -496,28 +516,32 @@
      *                its style
      * @return outFont
      */
-    @NonNull
-    private FontInfo deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
+    private void deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
         int desiredWeight = outFont.mWeight;
         int srcWeight = srcFont.mWeight;
         assert srcFont.mFont != null;
         Font derivedFont = srcFont.mFont;
+        int derivedStyle = 0;
         // Embolden the font if required.
         if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) {
-            derivedFont = derivedFont.deriveFont(Font.BOLD);
+            derivedStyle |= Font.BOLD;
             srcWeight += BOLD_FONT_WEIGHT_DELTA;
         }
         // Italicize the font if required.
         if (outFont.mIsItalic && !srcFont.mIsItalic) {
-            derivedFont = derivedFont.deriveFont(Font.ITALIC);
+            derivedStyle |= Font.ITALIC;
         } else if (outFont.mIsItalic != srcFont.mIsItalic) {
             // The desired font is plain, but the src font is italics. We can't convert it back. So
             // we update the value to reflect the true style of the font we're deriving.
             outFont.mIsItalic = srcFont.mIsItalic;
         }
+
+        if (derivedStyle != 0) {
+            derivedFont = derivedFont.deriveFont(derivedStyle);
+        }
+
         outFont.mFont = derivedFont;
         outFont.mWeight = srcWeight;
         // No need to update mIsItalics, as it's already been handled above.
-        return outFont;
     }
 }
diff --git a/bridge/src/android/graphics/ImageDecoder.java b/bridge/src/android/graphics/ImageDecoder.java
new file mode 100644
index 0000000..506eab5
--- /dev/null
+++ b/bridge/src/android/graphics/ImageDecoder.java
@@ -0,0 +1,704 @@
+/*
+ * 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 android.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.res.AssetManager.AssetInputStream;
+import android.content.res.Resources;
+import android.graphics.drawable.AnimatedImageDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.util.DisplayMetrics;
+import android.util.Size;
+import android.util.TypedValue;
+
+import java.nio.ByteBuffer;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ArrayIndexOutOfBoundsException;
+import java.lang.AutoCloseable;
+import java.lang.NullPointerException;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
+ */
+public final class ImageDecoder implements AutoCloseable {
+
+    /**
+     *  Source of the encoded image data.
+     */
+    public static abstract class Source {
+        private Source() {}
+
+        /* @hide */
+        @Nullable
+        Resources getResources() { return null; }
+
+        /* @hide */
+        int getDensity() { return Bitmap.DENSITY_NONE; }
+
+        /* @hide */
+        int computeDstDensity() {
+            Resources res = getResources();
+            if (res == null) {
+                return Bitmap.getDefaultDensity();
+            }
+
+            return res.getDisplayMetrics().densityDpi;
+        }
+
+        /* @hide */
+        @NonNull
+        abstract ImageDecoder createImageDecoder() throws IOException;
+    };
+
+    private static class ByteArraySource extends Source {
+        ByteArraySource(@NonNull byte[] data, int offset, int length) {
+            mData = data;
+            mOffset = offset;
+            mLength = length;
+        };
+        private final byte[] mData;
+        private final int    mOffset;
+        private final int    mLength;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    private static class ByteBufferSource extends Source {
+        ByteBufferSource(@NonNull ByteBuffer buffer) {
+            mBuffer = buffer;
+        }
+        private final ByteBuffer mBuffer;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    private static class ContentResolverSource extends Source {
+        ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
+            mResolver = resolver;
+            mUri = uri;
+        }
+
+        private final ContentResolver mResolver;
+        private final Uri mUri;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    /**
+     * For backwards compatibility, this does *not* close the InputStream.
+     */
+    private static class InputStreamSource extends Source {
+        InputStreamSource(Resources res, InputStream is, int inputDensity) {
+            if (is == null) {
+                throw new IllegalArgumentException("The InputStream cannot be null");
+            }
+            mResources = res;
+            mInputStream = is;
+            mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+        }
+
+        final Resources mResources;
+        InputStream mInputStream;
+        final int mInputDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() { return mInputDensity; }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    /**
+     * Takes ownership of the AssetInputStream.
+     *
+     * @hide
+     */
+    public static class AssetInputStreamSource extends Source {
+        public AssetInputStreamSource(@NonNull AssetInputStream ais,
+                @NonNull Resources res, @NonNull TypedValue value) {
+            mAssetInputStream = ais;
+            mResources = res;
+
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                mDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                mDensity = value.density;
+            } else {
+                mDensity = Bitmap.DENSITY_NONE;
+            }
+        }
+
+        private AssetInputStream mAssetInputStream;
+        private final Resources  mResources;
+        private final int        mDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() {
+            return mDensity;
+        }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    private static class ResourceSource extends Source {
+        ResourceSource(@NonNull Resources res, int resId) {
+            mResources = res;
+            mResId = resId;
+            mResDensity = Bitmap.DENSITY_NONE;
+        }
+
+        final Resources mResources;
+        final int       mResId;
+        int             mResDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() { return mResDensity; }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    private static class FileSource extends Source {
+        FileSource(@NonNull File file) {
+            mFile = file;
+        }
+
+        private final File mFile;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
+
+    /**
+     *  Contains information about the encoded image.
+     */
+    public static class ImageInfo {
+        private ImageDecoder mDecoder;
+
+        private ImageInfo(@NonNull ImageDecoder decoder) {
+            mDecoder = decoder;
+        }
+
+        /**
+         * Size of the image, without scaling or cropping.
+         */
+        @NonNull
+        public Size getSize() {
+            return new Size(0, 0);
+        }
+
+        /**
+         * The mimeType of the image.
+         */
+        @NonNull
+        public String getMimeType() {
+            return "";
+        }
+
+        /**
+         * Whether the image is animated.
+         *
+         * <p>Calling {@link #decodeDrawable} will return an
+         * {@link AnimatedImageDrawable}.</p>
+         */
+        public boolean isAnimated() {
+            return mDecoder.mAnimated;
+        }
+    };
+
+    /**
+     *  Thrown if the provided data is incomplete.
+     */
+    public static class IncompleteException extends IOException {};
+
+    /**
+     *  Optional listener supplied to {@link #decodeDrawable} or
+     *  {@link #decodeBitmap}.
+     */
+    public interface OnHeaderDecodedListener {
+        /**
+         *  Called when the header is decoded and the size is known.
+         *
+         *  @param decoder allows changing the default settings of the decode.
+         *  @param info Information about the encoded image.
+         *  @param source that created the decoder.
+         */
+        void onHeaderDecoded(@NonNull ImageDecoder decoder,
+                @NonNull ImageInfo info, @NonNull Source source);
+
+    };
+
+    /**
+     *  An Exception was thrown reading the {@link Source}.
+     */
+    public static final int ERROR_SOURCE_EXCEPTION  = 1;
+
+    /**
+     *  The encoded data was incomplete.
+     */
+    public static final int ERROR_SOURCE_INCOMPLETE = 2;
+
+    /**
+     *  The encoded data contained an error.
+     */
+    public static final int ERROR_SOURCE_ERROR      = 3;
+
+    @Retention(SOURCE)
+    public @interface Error {}
+
+    /**
+     *  Optional listener supplied to the ImageDecoder.
+     *
+     *  Without this listener, errors will throw {@link java.io.IOException}.
+     */
+    public interface OnPartialImageListener {
+        /**
+         *  Called when there is only a partial image to display.
+         *
+         *  If decoding is interrupted after having decoded a partial image,
+         *  this listener lets the client know that and allows them to
+         *  optionally finish the rest of the decode/creation process to create
+         *  a partial {@link Drawable}/{@link Bitmap}.
+         *
+         *  @param error indicating what interrupted the decode.
+         *  @param source that had the error.
+         *  @return True to create and return a {@link Drawable}/{@link Bitmap}
+         *      with partial data. False (which is the default) to abort the
+         *      decode and throw {@link java.io.IOException}.
+         */
+        boolean onPartialImage(@Error int error, @NonNull Source source);
+    }
+
+    private boolean mAnimated;
+    private Rect mOutPaddingRect;
+
+    public ImageDecoder() {
+        mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable
+    }
+
+    /**
+     * Create a new {@link Source} from an asset.
+     * @hide
+     *
+     * @param res the {@link Resources} object containing the image data.
+     * @param resId resource ID of the image data.
+     *      // FIXME: Can be an @DrawableRes?
+     * @return a new Source object, which can be passed to
+     *      {@link #decodeDrawable} or {@link #decodeBitmap}.
+     */
+    @NonNull
+    public static Source createSource(@NonNull Resources res, int resId)
+    {
+        return new ResourceSource(res, resId);
+    }
+
+    /**
+     * Create a new {@link Source} from a {@link android.net.Uri}.
+     *
+     * @param cr to retrieve from.
+     * @param uri of the image file.
+     * @return a new Source object, which can be passed to
+     *      {@link #decodeDrawable} or {@link #decodeBitmap}.
+     */
+    @NonNull
+    public static Source createSource(@NonNull ContentResolver cr,
+            @NonNull Uri uri) {
+        return new ContentResolverSource(cr, uri);
+    }
+
+    /**
+     * Create a new {@link Source} from a byte array.
+     *
+     * @param data byte array of compressed image data.
+     * @param offset offset into data for where the decoder should begin
+     *      parsing.
+     * @param length number of bytes, beginning at offset, to parse.
+     * @throws NullPointerException if data is null.
+     * @throws ArrayIndexOutOfBoundsException if offset and length are
+     *      not within data.
+     * @hide
+     */
+    @NonNull
+    public static Source createSource(@NonNull byte[] data, int offset,
+            int length) throws ArrayIndexOutOfBoundsException {
+        if (offset < 0 || length < 0 || offset >= data.length ||
+                offset + length > data.length) {
+            throw new ArrayIndexOutOfBoundsException(
+                    "invalid offset/length!");
+        }
+        return new ByteArraySource(data, offset, length);
+    }
+
+    /**
+     * See {@link #createSource(byte[], int, int).
+     * @hide
+     */
+    @NonNull
+    public static Source createSource(@NonNull byte[] data) {
+        return createSource(data, 0, data.length);
+    }
+
+    /**
+     * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
+     *
+     * <p>The returned {@link Source} effectively takes ownership of the
+     * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
+     * this call.</p>
+     *
+     * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
+     * position after decoding is undefined.
+     */
+    @NonNull
+    public static Source createSource(@NonNull ByteBuffer buffer) {
+        return new ByteBufferSource(buffer);
+    }
+
+    /**
+     * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     * @hide
+     */
+    public static Source createSource(Resources res, InputStream is) {
+        return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
+    }
+
+    /**
+     * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     * @hide
+     */
+    public static Source createSource(Resources res, InputStream is, int density) {
+        return new InputStreamSource(res, is, density);
+    }
+
+    /**
+     * Create a new {@link Source} from a {@link java.io.File}.
+     */
+    @NonNull
+    public static Source createSource(@NonNull File file) {
+        return new FileSource(file);
+    }
+
+    /**
+     *  Return the width and height of a given sample size.
+     *
+     *  <p>This takes an input that functions like
+     *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
+     *  height that can be acheived by sampling the encoded image. Other widths
+     *  and heights may be supported, but will require an additional (internal)
+     *  scaling step. Such internal scaling is *not* supported with
+     *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
+     *
+     *  @param sampleSize Sampling rate of the encoded image.
+     *  @return {@link android.util.Size} of the width and height after
+     *      sampling.
+     */
+    @NonNull
+    public Size getSampledSize(int sampleSize) {
+        return new Size(0, 0);
+    }
+
+    // Modifiers
+    /**
+     *  Resize the output to have the following size.
+     *
+     *  @param width must be greater than 0.
+     *  @param height must be greater than 0.
+     */
+    public void setResize(int width, int height) {
+    }
+
+    /**
+     *  Resize based on a sample size.
+     *
+     *  <p>This has the same effect as passing the result of
+     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
+     *
+     *  @param sampleSize Sampling rate of the encoded image.
+     */
+    public void setResize(int sampleSize) {
+    }
+
+    // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
+    /**
+     *  Use the default allocation for the pixel memory.
+     *
+     *  Will typically result in a {@link Bitmap.Config#HARDWARE}
+     *  allocation, but may be software for small images. In addition, this will
+     *  switch to software when HARDWARE is incompatible, e.g.
+     *  {@link #setMutable}, {@link #setAsAlphaMask}.
+     */
+    public static final int ALLOCATOR_DEFAULT = 0;
+
+    /**
+     *  Use a software allocation for the pixel memory.
+     *
+     *  Useful for drawing to a software {@link Canvas} or for
+     *  accessing the pixels on the final output.
+     */
+    public static final int ALLOCATOR_SOFTWARE = 1;
+
+    /**
+     *  Use shared memory for the pixel memory.
+     *
+     *  Useful for sharing across processes.
+     */
+    public static final int ALLOCATOR_SHARED_MEMORY = 2;
+
+    /**
+     *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
+     *
+     *  When this is combined with incompatible options, like
+     *  {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable}
+     *  / {@link #decodeBitmap} will throw an
+     *  {@link java.lang.IllegalStateException}.
+     */
+    public static final int ALLOCATOR_HARDWARE = 3;
+
+    /** @hide **/
+    @Retention(SOURCE)
+    public @interface Allocator {};
+
+    /**
+     *  Choose the backing for the pixel memory.
+     *
+     *  This is ignored for animated drawables.
+     *
+     *  @param allocator Type of allocator to use.
+     */
+    public ImageDecoder setAllocator(@Allocator int allocator) {
+        return this;
+    }
+
+    /**
+     *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
+     *
+     *  By default, ImageDecoder will create a {@link Bitmap} with
+     *  premultiplied pixels, which is required for drawing with the
+     *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
+     *  this method with a value of {@code true} will result in
+     *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
+     *  pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
+     *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
+     *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
+     */
+    public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+        return this;
+    }
+
+    /**
+     *  Modify the image after decoding and scaling.
+     *
+     *  <p>This allows adding effects prior to returning a {@link Drawable} or
+     *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
+     *  this is the only way to process the image after decoding.</p>
+     *
+     *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
+     *
+     *  <p>For an animated image, the drawing commands drawn on the
+     *  {@link Canvas} will be recorded immediately and then applied to each
+     *  frame.</p>
+     */
+    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
+        return this;
+    }
+
+    /**
+     *  Set (replace) the {@link OnPartialImageListener} on this object.
+     *
+     *  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;
+    }
+
+    /**
+     *  Crop the output to {@code subset} of the (possibly) scaled image.
+     *
+     *  <p>{@code subset} must be contained within the size set by
+     *  {@link #setResize} or the bounds of the image if setResize was not
+     *  called. Otherwise an {@link IllegalStateException} will be thrown by
+     *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
+     *
+     *  <p>NOT intended as a replacement for
+     *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
+     *  but merely crops the output.</p>
+     */
+    public ImageDecoder setCrop(@Nullable Rect subset) {
+        return this;
+    }
+
+    /**
+     *  Set a Rect for retrieving nine patch padding.
+     *
+     *  If the image is a nine patch, this Rect will be set to the padding
+     *  rectangle during decode. Otherwise it will not be modified.
+     *
+     *  @hide
+     */
+    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+        mOutPaddingRect = outPadding;
+        return this;
+    }
+
+    /**
+     *  Specify whether the {@link Bitmap} should be mutable.
+     *
+     *  <p>By default, a {@link Bitmap} created will be immutable, but that can
+     *  be changed with this call.</p>
+     *
+     *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
+     *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
+     *  Attempting to combine them will throw an
+     *  {@link java.lang.IllegalStateException}.</p>
+     *
+     *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable},
+     *  which would require retrieving the Bitmap from the returned Drawable in
+     *  order to modify. Attempting to decode a mutable {@link Drawable} will
+     *  throw an {@link java.lang.IllegalStateException}.</p>
+     */
+    public ImageDecoder setMutable(boolean mutable) {
+        return this;
+    }
+
+    /**
+     *  Specify whether to potentially save RAM at the expense of quality.
+     *
+     *  Setting this to {@code true} may result in a {@link Bitmap} with a
+     *  denser {@link Bitmap.Config}, depending on the image. For example, for
+     *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
+     *  with no alpha information.
+     */
+    public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) {
+        return this;
+    }
+
+    /**
+     *  Specify whether to potentially treat the output as an alpha mask.
+     *
+     *  <p>If this is set to {@code true} and the image is encoded in a format
+     *  with only one channel, treat that channel as alpha. Otherwise this call has
+     *  no effect.</p>
+     *
+     *  <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
+     *  combine them will result in {@link #decodeDrawable}/
+     *  {@link #decodeBitmap} throwing an
+     *  {@link java.lang.IllegalStateException}.</p>
+     */
+    public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
+        return this;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    /**
+     *  Create a {@link Drawable} from a {@code Source}.
+     *
+     *  @param src representing the encoded image.
+     *  @param listener for learning the {@link ImageInfo} and changing any
+     *      default settings on the {@code ImageDecoder}. If not {@code null},
+     *      this will be called on the same thread as {@code decodeDrawable}
+     *      before that method returns.
+     *  @return Drawable for displaying the image.
+     *  @throws IOException if {@code src} is not found, is an unsupported
+     *      format, or cannot be decoded for any reason.
+     */
+    @NonNull
+    public static Drawable decodeDrawable(@NonNull Source src,
+            @Nullable OnHeaderDecodedListener listener) throws IOException {
+        Bitmap bitmap = decodeBitmap(src, listener);
+        return new BitmapDrawable(src.getResources(), bitmap);
+    }
+
+    /**
+     * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}.
+     */
+    @NonNull
+    public static Drawable decodeDrawable(@NonNull Source src)
+            throws IOException {
+        return decodeDrawable(src, null);
+    }
+
+    /**
+     *  Create a {@link Bitmap} from a {@code Source}.
+     *
+     *  @param src representing the encoded image.
+     *  @param listener for learning the {@link ImageInfo} and changing any
+     *      default settings on the {@code ImageDecoder}. If not {@code null},
+     *      this will be called on the same thread as {@code decodeBitmap}
+     *      before that method returns.
+     *  @return Bitmap containing the image.
+     *  @throws IOException if {@code src} is not found, is an unsupported
+     *      format, or cannot be decoded for any reason.
+     */
+    @NonNull
+    public static Bitmap decodeBitmap(@NonNull Source src,
+            @Nullable OnHeaderDecodedListener listener) throws IOException {
+        TypedValue value = new TypedValue();
+        value.density = src.getDensity();
+        ImageDecoder decoder = src.createImageDecoder();
+        if (listener != null) {
+            listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src);
+        }
+        return BitmapFactory.decodeResourceStream(src.getResources(), value,
+                ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null);
+    }
+
+    /**
+     *  See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
+     */
+    @NonNull
+    public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
+        return decodeBitmap(src, null);
+    }
+}
diff --git a/bridge/src/android/graphics/LinearGradient_Delegate.java b/bridge/src/android/graphics/LinearGradient_Delegate.java
index cd4393a..477705c 100644
--- a/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -24,6 +24,9 @@
 import android.graphics.Shader.TileMode;
 
 import java.awt.image.ColorModel;
+import java.awt.image.DataBufferInt;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
 
 /**
  * Delegate implementing the native methods of android.graphics.LinearGradient
@@ -174,10 +177,6 @@
 
             @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(
-                    mColorModel, mColorModel.createCompatibleWritableRaster(w, h),
-                    mColorModel.isAlphaPremultiplied(), null);
-
                 int[] data = new int[w*h];
 
                 int index = 0;
@@ -199,9 +198,9 @@
                     }
                 }
 
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
+                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
+                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
+                return Raster.createWritableRaster(colorModel, dataBuffer, null);
             }
         }
 
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
index 354f919..5ae181d 100644
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/bridge/src/android/graphics/Matrix_Delegate.java
@@ -118,8 +118,7 @@
         return true;
     }
 
-    public static float[] makeValues(AffineTransform matrix) {
-        float[] values = new float[MATRIX_SIZE];
+    private static float[] setValues(AffineTransform matrix, float[] values) {
         values[0] = (float) matrix.getScaleX();
         values[1] = (float) matrix.getShearX();
         values[2] = (float) matrix.getTranslateX();
@@ -133,6 +132,10 @@
         return values;
     }
 
+    public static float[] makeValues(AffineTransform matrix) {
+        return setValues(matrix, new float[MATRIX_SIZE]);
+    }
+
     public static Matrix_Delegate make(AffineTransform matrix) {
         return new Matrix_Delegate(makeValues(matrix));
     }
@@ -616,12 +619,7 @@
         try {
             AffineTransform affineTransform = d.getAffineTransform();
             AffineTransform inverseTransform = affineTransform.createInverse();
-            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
-            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
-            inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
-            inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
-            inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
-            inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+            setValues(inverseTransform, inv_mtx.mValues);
 
             return true;
         } catch (NoninvertibleTransformException e) {
diff --git a/bridge/src/android/graphics/NinePatch_Delegate.java b/bridge/src/android/graphics/NinePatch_Delegate.java
index 43e5b0f..ce2c18b 100644
--- a/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -160,8 +160,12 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nativeFinalize(long chunk) {
-        sManager.removeJavaReferenceFor(chunk);
+    /*package*/ static void nativeFinalize(long nativeNinePatch) {
+        NinePatch_Delegate delegate = sManager.getDelegate(nativeNinePatch);
+        if (delegate != null && delegate.chunk != null) {
+            sChunkCache.remove(delegate.chunk);
+        }
+        sManager.removeJavaReferenceFor(nativeNinePatch);
     }
 
 
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index 406157e..dae966d 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -34,10 +34,11 @@
 import java.awt.Stroke;
 import java.awt.Toolkit;
 import java.awt.geom.AffineTransform;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import libcore.util.NativeAllocationRegistry_Delegate;
 
@@ -55,23 +56,32 @@
  *
  */
 public class Paint_Delegate {
+    private static final float DEFAULT_TEXT_SIZE = 20.f;
+    private static final float DEFAULT_TEXT_SCALE_X = 1.f;
+    private static final float DEFAULT_TEXT_SKEW_X = 0.f;
 
     /**
      * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
      */
     /*package*/ static final class FontInfo {
-        Font mFont;
-        java.awt.FontMetrics mMetrics;
+        final Font mFont;
+        final java.awt.FontMetrics mMetrics;
+
+        FontInfo(@NonNull Font font, @NonNull java.awt.FontMetrics fontMetrics) {
+            this.mFont = font;
+            this.mMetrics = fontMetrics;
+        }
     }
 
     // ---- delegate manager ----
     private static final DelegateManager<Paint_Delegate> sManager =
-            new DelegateManager<Paint_Delegate>(Paint_Delegate.class);
+            new DelegateManager<>(Paint_Delegate.class);
     private static long sFinalizer = -1;
 
     // ---- delegate helper data ----
 
     // This list can contain null elements.
+    @Nullable
     private List<FontInfo> mFonts;
 
     // ---- delegate data ----
@@ -100,11 +110,9 @@
     private PathEffect_Delegate mPathEffect;
     private MaskFilter_Delegate mMaskFilter;
 
+    @SuppressWarnings("FieldCanBeLocal") // Used to store the locale for future use
     private Locale mLocale = Locale.getDefault();
 
-    // Used only to assert invariants.
-    public long mNativeTypeface;
-
     // ---- Public Helper methods ----
 
     @Nullable
@@ -115,7 +123,32 @@
     /**
      * Returns the list of {@link Font} objects.
      */
+    @NonNull
     public List<FontInfo> getFonts() {
+        Typeface_Delegate typeface = mTypeface;
+        if (typeface == null) {
+            if (Typeface.sDefaultTypeface == null) {
+                return Collections.emptyList();
+            }
+
+            typeface = Typeface_Delegate.getDelegate(Typeface.sDefaultTypeface.native_instance);
+        }
+
+        if (mFonts != null) {
+            return mFonts;
+        }
+
+        // Apply an optional transformation for skew and scale
+        AffineTransform affineTransform = mTextScaleX != 1.0 || mTextSkewX != 0 ?
+                new AffineTransform(mTextScaleX, mTextSkewX, 0, 1, 0, 0) :
+                null;
+
+        List<FontInfo> infoList = StreamSupport.stream(typeface.getFonts(mFontVariant).spliterator
+                (), false)
+                .map(font -> getFontInfo(font, mTextSize, affineTransform))
+                .collect(Collectors.toList());
+        mFonts = Collections.unmodifiableList(infoList);
+
         return mFonts;
     }
 
@@ -187,6 +220,7 @@
             if (mPathEffect.isSupported()) {
                 Stroke stroke = mPathEffect.getStroke(this);
                 assert stroke != null;
+                //noinspection ConstantConditions
                 if (stroke != null) {
                     return stroke;
                 }
@@ -483,7 +517,7 @@
 
         if (delegate.mTextSize != textSize) {
             delegate.mTextSize = textSize;
-            delegate.updateFontObject();
+            delegate.invalidateFonts();
         }
     }
 
@@ -508,7 +542,7 @@
 
         if (delegate.mTextScaleX != scaleX) {
             delegate.mTextScaleX = scaleX;
-            delegate.updateFontObject();
+            delegate.invalidateFonts();
         }
     }
 
@@ -533,20 +567,21 @@
 
         if (delegate.mTextSkewX != skewX) {
             delegate.mTextSkewX = skewX;
-            delegate.updateFontObject();
+            delegate.invalidateFonts();
         }
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nAscent(long nativePaint, long nativeTypeface) {
+    /*package*/ static float nAscent(long nativePaint) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return 0;
         }
 
-        if (delegate.mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+        List<FontInfo> fonts = delegate.getFonts();
+        if (fonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
             // Android expects negative ascent so we invert the value from Java.
             return - javaMetrics.getAscent();
         }
@@ -555,15 +590,16 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nDescent(long nativePaint, long nativeTypeface) {
+    /*package*/ static float nDescent(long nativePaint) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return 0;
         }
 
-        if (delegate.mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+        List<FontInfo> fonts = delegate.getFonts();
+        if (fonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
             return javaMetrics.getDescent();
         }
 
@@ -572,7 +608,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetFontMetrics(long nativePaint, long nativeTypeface,
+    /*package*/ static float nGetFontMetrics(long nativePaint,
             FontMetrics metrics) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
@@ -584,16 +620,16 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetFontMetricsInt(long nativePaint,
-            long nativeTypeface, FontMetricsInt fmi) {
+    /*package*/ static int nGetFontMetricsInt(long nativePaint, FontMetricsInt fmi) {
         // get the delegate
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return 0;
         }
 
-        if (delegate.mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+        List<FontInfo> fonts = delegate.getFonts();
+        if (fonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
             if (fmi != null) {
                 // Android expects negative ascent so we invert the value from Java.
                 fmi.top = (int)(- javaMetrics.getMaxAscent() * 1.15);
@@ -610,7 +646,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nBreakText(long nativePaint, long nativeTypeface, char[] text,
+    /*package*/ static int nBreakText(long nativePaint, char[] text,
             int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
 
         // get the delegate
@@ -652,10 +688,9 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nBreakText(long nativePaint, long nativeTypeface, String text,
-            boolean measureForwards,
+    /*package*/ static int nBreakText(long nativePaint, String text, boolean measureForwards,
             float maxWidth, int bidiFlags, float[] measuredWidth) {
-        return nBreakText(nativePaint, nativeTypeface, text.toCharArray(), 0, text.length(),
+        return nBreakText(nativePaint, text.toCharArray(), 0, text.length(),
                 maxWidth, bidiFlags, measuredWidth);
     }
 
@@ -871,20 +906,18 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nSetTypeface(long native_object, long typeface) {
+    /*package*/ static void nSetTypeface(long native_object, long typeface) {
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
         if (delegate == null) {
-            return 0;
+            return;
         }
 
         Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface);
-        if (delegate.mTypeface != typefaceDelegate || delegate.mNativeTypeface != typeface) {
-            delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
-            delegate.mNativeTypeface = typeface;
-            delegate.updateFontObject();
+        if (delegate.mTypeface != typefaceDelegate) {
+            delegate.mTypeface = typefaceDelegate;
+            delegate.invalidateFonts();
         }
-        return typeface;
     }
 
     @LayoutlibDelegate
@@ -922,14 +955,14 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nSetTextLocalesByMinikinLangListId(long paintPtr,
+    /*package*/ static void nSetTextLocalesByMinikinLocaleListId(long paintPtr,
             int mMinikinLangListId) {
         // FIXME
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetTextAdvances(long native_object, long native_typeface,
-            char[] text, int index, int count, int contextIndex, int contextCount,
+    /*package*/ static float nGetTextAdvances(long native_object, char[] text, int index,
+            int count, int contextIndex, int contextCount,
             int bidiFlags, float[] advances, int advancesIndex) {
 
         if (advances != null)
@@ -941,32 +974,25 @@
             return 0.f;
         }
 
-        // native_typeface is passed here since Framework's old implementation did not have the
-        // typeface object associated with the Paint. Since, we follow the new framework way,
-        // we store the typeface with the paint and use it directly.
-        assert (native_typeface == delegate.mNativeTypeface);
-
         RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, bidiFlags);
         return bounds.right - bounds.left;
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetTextAdvances(long native_object, long native_typeface,
-            String text, int start, int end, int contextStart, int contextEnd,
-            int bidiFlags, float[] advances, int advancesIndex) {
+    /*package*/ static float nGetTextAdvances(long native_object, String text, int start, int end,
+            int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex) {
         // FIXME: support contextStart and contextEnd
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        return nGetTextAdvances(native_object, native_typeface, buffer, 0, count,
+        return nGetTextAdvances(native_object, buffer, 0, count,
                 contextStart, contextEnd - contextStart, bidiFlags, advances, advancesIndex);
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, long typefacePtr,
-            char[] text, int contextStart, int contextLength, int flags, int offset,
-            int cursorOpt) {
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
+            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
@@ -974,8 +1000,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, long typefacePtr,
-            String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
+            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
@@ -983,31 +1009,31 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nGetTextPath(long native_object, long native_typeface,
-            int bidiFlags, char[] text, int index, int count, float x, float y, long path) {
+    /*package*/ static void nGetTextPath(long native_object, int bidiFlags, char[] text,
+            int index, int count, float x, float y, long path) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.getTextPath is not supported.", null, null /*data*/);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nGetTextPath(long native_object, long native_typeface,
-            int bidiFlags, String text, int start, int end, float x, float y, long path) {
+    /*package*/ static void nGetTextPath(long native_object, int bidiFlags, String text, int start,
+            int end, float x, float y, long path) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.getTextPath is not supported.", null, null /*data*/);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nGetStringBounds(long nativePaint, long native_typeface,
-            String text, int start, int end, int bidiFlags, Rect bounds) {
-        nGetCharArrayBounds(nativePaint, native_typeface, text.toCharArray(), start,
+    /*package*/ static void nGetStringBounds(long nativePaint, String text, int start, int end,
+            int bidiFlags, Rect bounds) {
+        nGetCharArrayBounds(nativePaint, text.toCharArray(), start,
                 end - start, bidiFlags, bounds);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nGetCharArrayBounds(long nativePaint, long native_typeface,
-            char[] text, int index, int count, int bidiFlags, Rect bounds) {
+    /*package*/ static void nGetCharArrayBounds(long nativePaint, char[] text, int index,
+            int count, int bidiFlags, Rect bounds) {
 
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
@@ -1015,9 +1041,6 @@
             return;
         }
 
-        // assert that the typeface passed is actually the one that we had stored.
-        assert (native_typeface == delegate.mNativeTypeface);
-
         delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
     }
 
@@ -1095,8 +1118,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nHasGlyph(long nativePaint, long nativeTypeface, int bidiFlags,
-            String string) {
+    /*package*/ static boolean nHasGlyph(long nativePaint, int bidiFlags, String string) {
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
         if (delegate == null) {
             return false;
@@ -1109,11 +1131,9 @@
                     "Paint.hasGlyph() is not supported for ligatures.", null, null);
             return false;
         }
-        assert nativeTypeface == delegate.mNativeTypeface;
-        Typeface_Delegate typeface_delegate = Typeface_Delegate.getDelegate(nativeTypeface);
 
         char c = string.charAt(0);
-        for (Font font : typeface_delegate.getFonts(delegate.mFontVariant)) {
+        for (Font font : delegate.mTypeface.getFonts(delegate.mFontVariant)) {
             if (font.canDisplay(c)) {
                 return true;
             }
@@ -1123,14 +1143,14 @@
 
 
     @LayoutlibDelegate
-    /*package*/ static float nGetRunAdvance(long nativePaint, long nativeTypeface,
-            @NonNull char[] text, int start, int end, int contextStart, int contextEnd,
+    /*package*/ static float nGetRunAdvance(long nativePaint, @NonNull char[] text, int start,
+            int end, int contextStart, int contextEnd,
             boolean isRtl, int offset) {
         int count = end - start;
         float[] advances = new float[count];
         int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
-        nGetTextAdvances(nativePaint, nativeTypeface, text, start, count,
-                contextStart, contextEnd - contextStart, bidiFlags, advances, 0);
+        nGetTextAdvances(nativePaint, text, start, count, contextStart,
+                contextEnd - contextStart, bidiFlags, advances, 0);
         int startOffset = offset - start;  // offset from start.
         float sum = 0;
         for (int i = 0; i < startOffset; i++) {
@@ -1140,14 +1160,13 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetOffsetForAdvance(long nativePaint, long nativeTypeface,
-            char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
-            float advance) {
+    /*package*/ static int nGetOffsetForAdvance(long nativePaint, char[] text, int start,
+            int end, int contextStart, int contextEnd, boolean isRtl, float advance) {
         int count = end - start;
         float[] advances = new float[count];
         int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
-        nGetTextAdvances(nativePaint, nativeTypeface, text, start, count,
-                contextStart, contextEnd - contextStart, bidiFlags, advances, 0);
+        nGetTextAdvances(nativePaint, text, start, count, contextStart,
+                contextEnd - contextStart, bidiFlags, advances, 0);
         float sum = 0;
         int i;
         for (i = 0; i < count && sum < advance; i++) {
@@ -1159,25 +1178,30 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetUnderlinePosition(long paintPtr, long typefacePtr) {
+    /*package*/ static float nGetUnderlinePosition(long paintPtr) {
         return (1.0f / 9.0f) * nGetTextSize(paintPtr);
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetUnderlineThickness(long paintPtr, long typefacePtr) {
+    /*package*/ static float nGetUnderlineThickness(long paintPtr) {
         return (1.0f / 18.0f) * nGetTextSize(paintPtr);
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetStrikeThruPosition(long paintPtr, long typefacePtr) {
+    /*package*/ static float nGetStrikeThruPosition(long paintPtr) {
         return (-79.0f / 252.0f) * nGetTextSize(paintPtr);
     }
 
     @LayoutlibDelegate
-    /*package*/ static float nGetStrikeThruThickness(long paintPtr, long typefacePtr) {
+    /*package*/ static float nGetStrikeThruThickness(long paintPtr) {
         return (1.0f / 18.0f) * nGetTextSize(paintPtr);
     }
 
+    @LayoutlibDelegate
+    /*package*/ static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
+        return leftPaintPtr == rightPaintPtr;
+    }
+
     // ---- Private delegate/helper methods ----
 
     /*package*/ Paint_Delegate() {
@@ -1196,26 +1220,24 @@
         mJoin = paint.mJoin;
         mTextAlign = paint.mTextAlign;
 
-        boolean needsFontUpdate = false;
-        if (mTypeface != paint.mTypeface || mNativeTypeface != paint.mNativeTypeface) {
+        if (mTypeface != paint.mTypeface) {
             mTypeface = paint.mTypeface;
-            mNativeTypeface = paint.mNativeTypeface;
-            needsFontUpdate = true;
+            invalidateFonts();
         }
 
         if (mTextSize != paint.mTextSize) {
             mTextSize = paint.mTextSize;
-            needsFontUpdate = true;
+            invalidateFonts();
         }
 
         if (mTextScaleX != paint.mTextScaleX) {
             mTextScaleX = paint.mTextScaleX;
-            needsFontUpdate = true;
+            invalidateFonts();
         }
 
         if (mTextSkewX != paint.mTextSkewX) {
             mTextSkewX = paint.mTextSkewX;
-            needsFontUpdate = true;
+            invalidateFonts();
         }
 
         mStrokeWidth = paint.mStrokeWidth;
@@ -1226,77 +1248,70 @@
         mPathEffect = paint.mPathEffect;
         mMaskFilter = paint.mMaskFilter;
         mHintingMode = paint.mHintingMode;
-
-        if (needsFontUpdate) {
-            updateFontObject();
-        }
     }
 
     private void reset() {
+        Typeface_Delegate defaultTypeface =
+                Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
+
         mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS;
         mColor = 0xFF000000;
         mStyle = Paint.Style.FILL.nativeInt;
         mCap = Paint.Cap.BUTT.nativeInt;
         mJoin = Paint.Join.MITER.nativeInt;
         mTextAlign = 0;
-        mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
-        mNativeTypeface = 0;
+
+        if (mTypeface != defaultTypeface) {
+            mTypeface = defaultTypeface;
+            invalidateFonts();
+        }
+
         mStrokeWidth = 1.f;
         mStrokeMiter = 4.f;
-        mTextSize = 20.f;
-        mTextScaleX = 1.f;
-        mTextSkewX = 0.f;
+
+        if (mTextSize != DEFAULT_TEXT_SIZE) {
+            mTextSize = DEFAULT_TEXT_SIZE;
+            invalidateFonts();
+        }
+
+        if (mTextScaleX != DEFAULT_TEXT_SCALE_X) {
+            mTextScaleX = DEFAULT_TEXT_SCALE_X;
+            invalidateFonts();
+        }
+
+        if (mTextSkewX != DEFAULT_TEXT_SKEW_X) {
+            mTextSkewX = DEFAULT_TEXT_SKEW_X;
+            invalidateFonts();
+        }
+
         mPorterDuffMode = Xfermode.DEFAULT;
         mColorFilter = null;
         mShader = null;
         mPathEffect = null;
         mMaskFilter = null;
-        updateFontObject();
         mHintingMode = Paint.HINTING_ON;
     }
 
-    /**
-     * Update the {@link Font} object from the typeface, text size and scaling
-     */
-    @SuppressWarnings("deprecation")
-    private void updateFontObject() {
-        if (mTypeface != null) {
-            // Get the fonts from the TypeFace object.
-            List<Font> fonts = mTypeface.getFonts(mFontVariant);
+    private void invalidateFonts() {
+        mFonts = null;
+    }
 
-            if (fonts.isEmpty()) {
-                mFonts = Collections.emptyList();
-                return;
-            }
-
-            // create new font objects as well as FontMetrics, based on the current text size
-            // and skew info.
-            int nFonts = fonts.size();
-            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(nFonts);
-            //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
-            for (int i = 0; i < nFonts; i++) {
-                Font font = fonts.get(i);
-                if (font == null) {
-                    // If the font is null, add null to infoList. When rendering the text, if this
-                    // null is reached, a warning will be logged.
-                    infoList.add(null);
-                    continue;
-                }
-                FontInfo info = new FontInfo();
-                info.mFont = font.deriveFont(mTextSize);
-                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
-                    // TODO: support skew
-                    info.mFont = info.mFont.deriveFont(new AffineTransform(
-                            mTextScaleX, mTextSkewX, 0, 1, 0, 0));
-                }
-                // The metrics here don't have anti-aliasing set.
-                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
-
-                infoList.add(info);
-            }
-
-            mFonts = Collections.unmodifiableList(infoList);
+    @Nullable
+    private static FontInfo getFontInfo(@Nullable Font font, float textSize,
+            @Nullable AffineTransform transform) {
+        if (font == null) {
+            return null;
         }
+
+        Font transformedFont = font.deriveFont(textSize);
+        if (transform != null) {
+            // TODO: support skew
+            transformedFont = transformedFont.deriveFont(transform);
+        }
+
+        // The metrics here don't have anti-aliasing set.
+        return new FontInfo(transformedFont,
+                Toolkit.getDefaultToolkit().getFontMetrics(transformedFont));
     }
 
     /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
@@ -1312,8 +1327,9 @@
     }
 
     private float getFontMetrics(FontMetrics metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
+        List<FontInfo> fonts = getFonts();
+        if (fonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
             if (metrics != null) {
                 // Android expects negative ascent so we invert the value from Java.
                 metrics.top = - javaMetrics.getMaxAscent();
diff --git a/bridge/src/android/graphics/RadialGradient_Delegate.java b/bridge/src/android/graphics/RadialGradient_Delegate.java
index b5ba468..25521d2 100644
--- a/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -24,6 +24,9 @@
 import android.graphics.Shader.TileMode;
 
 import java.awt.image.ColorModel;
+import java.awt.image.DataBufferInt;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
 
 /**
  * Delegate implementing the native methods of android.graphics.RadialGradient
@@ -163,10 +166,6 @@
 
             @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(
-                    mColorModel, mColorModel.createCompatibleWritableRaster(w, h),
-                    mColorModel.isAlphaPremultiplied(), null);
-
                 int[] data = new int[w*h];
 
                 // compute distance from each point to the center, and figure out the distance from
@@ -174,6 +173,7 @@
                 int index = 0;
                 float[] pt1 = new float[2];
                 float[] pt2 = new float[2];
+
                 for (int iy = 0 ; iy < h ; iy++) {
                     for (int ix = 0 ; ix < w ; ix++) {
                         // handle the canvas transform
@@ -182,21 +182,21 @@
                         mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
 
                         // handle the local matrix
-                        pt1[0] = pt2[0] - mX;
-                        pt1[1] = pt2[1] - mY;
+                        pt1[0] = pt2[0];
+                        pt1[1] = pt2[1];
                         mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
 
-                        float _x = pt2[0];
-                        float _y = pt2[1];
+                        float _x = pt2[0] - mX;
+                        float _y = pt2[1] - mY;
                         float distance = (float) Math.hypot(_x, _y);
 
                         data[index++] = getGradientColor(distance / mRadius);
                     }
                 }
 
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
+                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
+                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
+                return Raster.createWritableRaster(colorModel, dataBuffer, null);
             }
 
         }
diff --git a/bridge/src/android/graphics/Shader_Delegate.java b/bridge/src/android/graphics/Shader_Delegate.java
index d88be47..eefa929 100644
--- a/bridge/src/android/graphics/Shader_Delegate.java
+++ b/bridge/src/android/graphics/Shader_Delegate.java
@@ -92,6 +92,10 @@
     // ---- Private delegate/helper methods ----
 
     protected Shader_Delegate(long nativeMatrix) {
+        setLocalMatrix(nativeMatrix);
+    }
+
+    public void setLocalMatrix(long nativeMatrix) {
         mLocalMatrix = Matrix_Delegate.getDelegate(nativeMatrix);
     }
 
diff --git a/bridge/src/android/graphics/SweepGradient_Delegate.java b/bridge/src/android/graphics/SweepGradient_Delegate.java
index 30152bc..6e8aca3 100644
--- a/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -21,6 +21,10 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import java.awt.image.DataBufferInt;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+
 /**
  * Delegate implementing the native methods of android.graphics.SweepGradient
  *
@@ -156,9 +160,6 @@
 
             @Override
             public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(
-                    mColorModel, mColorModel.createCompatibleWritableRaster(w, h),
-                    mColorModel.isAlphaPremultiplied(), null);
 
                 int[] data = new int[w*h];
 
@@ -203,9 +204,9 @@
                     }
                 }
 
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
+                DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
+                SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
+                return Raster.createWritableRaster(colorModel, dataBuffer, null);
             }
 
         }
diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java
index a04a324..d793ade 100644
--- a/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/bridge/src/android/graphics/Typeface_Delegate.java
@@ -16,37 +16,57 @@
 
 package android.graphics;
 
+import com.android.SdkConstants;
 import com.android.ide.common.rendering.api.LayoutLog;
 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;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.FontResourcesParser;
 import android.graphics.FontFamily_Delegate.FontVariant;
 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.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Spliterator;
+import java.util.Spliterators;
 
 import static android.graphics.FontFamily_Delegate.getFontLocation;
 
 /**
  * Delegate implementing the native methods of android.graphics.Typeface
- *
- * Through the layoutlib_create tool, the original native methods of Typeface have been replaced
- * by calls to methods of the same name in this delegate class.
- *
+ * <p>
+ * Through the layoutlib_create tool, the original native methods of Typeface 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 Typeface class.
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original Typeface class.
  *
  * @see DelegateManager
- *
  */
 public final class Typeface_Delegate {
 
@@ -54,87 +74,29 @@
 
     // ---- delegate manager ----
     private static final DelegateManager<Typeface_Delegate> sManager =
-            new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
+            new DelegateManager<>(Typeface_Delegate.class);
 
 
     // ---- delegate data ----
-
+    private static long sDefaultTypeface;
     @NonNull
     private final FontFamily_Delegate[] mFontFamilies;  // the reference to FontFamily_Delegate.
     /** @see Font#getStyle() */
     private final int mStyle;
     private final int mWeight;
-
-    private static long sDefaultTypeface;
+    private SoftReference<EnumMap<FontVariant, List<Font>>> mFontsCache = new SoftReference<>(null);
 
 
     // ---- Public Helper methods ----
 
-    public static Typeface_Delegate getDelegate(long nativeTypeface) {
-        return sManager.getDelegate(nativeTypeface);
+    public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) {
+        mFontFamilies = fontFamilies;
+        mStyle = style;
+        mWeight = weight;
     }
 
-    /**
-     * Return a list of fonts that match the style and variant. The list is ordered according to
-     * preference of fonts.
-     *
-     * The list may contain null when the font failed to load. If null is reached when trying to
-     * render with this list of fonts, then a warning should be logged letting the user know that
-     * some font failed to load.
-     *
-     * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or
-     *                {@link FontVariant#ELEGANT}
-     */
-    @NonNull
-    public List<Font> getFonts(FontVariant variant) {
-        assert variant != FontVariant.NONE;
-
-        // Calculate the required weight based on style and weight of this typeface.
-        int weight = mWeight + 50 +
-                ((mStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
-        if (weight > 1000) {
-            weight = 1000;
-        } else if (weight < 100) {
-            weight = 100;
-        }
-        final boolean isItalic = (mStyle & Font.ITALIC) != 0;
-        List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
-        for (int i = 0; i < mFontFamilies.length; i++) {
-            FontFamily_Delegate ffd = mFontFamilies[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 < mFontFamilies.length - 1;
-                    FontFamily_Delegate ffd2 = mFontFamilies[++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;
+    public static Typeface_Delegate getDelegate(long nativeTypeface) {
+        return sManager.getDelegate(nativeTypeface);
     }
 
     /**
@@ -161,13 +123,13 @@
             return 0;
         }
 
-        return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style,
-                delegate.mWeight));
+        return sManager.addNewDelegate(
+                new Typeface_Delegate(delegate.mFontFamilies, style, delegate.mWeight));
     }
 
     @LayoutlibDelegate
-    /*package*/ static long nativeCreateFromTypefaceWithExactStyle(long native_instance,
-            int weight, boolean italic) {
+    /*package*/ static long nativeCreateFromTypefaceWithExactStyle(long native_instance, int weight,
+            boolean italic) {
         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
         if (delegate == null) {
             delegate = sManager.getDelegate(sDefaultTypeface);
@@ -178,7 +140,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));
+        return sManager.addNewDelegate(
+                new Typeface_Delegate(delegate.mFontFamilies, style, weight));
     }
 
     @LayoutlibDelegate
@@ -262,28 +225,250 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static File getSystemFontConfigLocation() {
-        return new File(getFontLocation());
+    /*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 makeFamilyFromParsed(FontConfig.Family family,
-            Map<String, ByteBuffer> bufferForPath) {
-        FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
-        for (FontConfig.Font font : family.getFonts()) {
-            String fullPathName = "/system/fonts/" + font.getFontName();
-            FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, fullPathName,
-                    font.getWeight(), font.isItalic());
+    /*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
+     */
+    @Nullable
+    public static Typeface createFromDisk(@NonNull BridgeContext context, @NonNull String path,
+            boolean isFramework) {
+        // Check if this is an asset that we've already loaded dynamically
+        Typeface typeface = Typeface.findFromCache(context.getAssets(), path);
+        if (typeface != null) {
+            return typeface;
+        }
+
+        String lowerCaseValue = path.toLowerCase();
+        if (lowerCaseValue.endsWith(SdkConstants.DOT_XML)) {
+            // create a block parser for the file
+            Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
+                    RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
+            XmlPullParser parser = null;
+            if (psiParserSupport != null && psiParserSupport) {
+                parser = context.getLayoutlibCallback().getXmlFileParser(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*/);
+                    }
+                }
+            }
+
+            if (parser != null) {
+                BridgeXmlBlockParser blockParser =
+                        new BridgeXmlBlockParser(parser, context, isFramework);
+                try {
+                    FontResourcesParser.FamilyResourceEntry entry =
+                            FontResourcesParser.parse(blockParser, context.getResources());
+                    typeface = Typeface.createFromResources(entry, context.getAssets(), path);
+                } catch (XmlPullParserException | IOException e) {
+                    Bridge.getLog().error(null, "Failed to parse file " + path, e, null /*data*/);
+                } finally {
+                    blockParser.ensurePopped();
+                }
+            } else {
+                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                        String.format("File %s does not exist (or is not a file)", path),
+                        null /*data*/);
+            }
+        } else {
+            typeface = Typeface.createFromResources(context.getAssets(), path, 0);
+        }
+
+        return typeface;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Typeface create(String familyName, int style) {
+        if (familyName != null && Files.exists(Paths.get(familyName))) {
+            // Workaround for b/64137851
+            // Support lib will call this method after failing to create the TypefaceCompat.
+            return Typeface_Delegate.createFromDisk(RenderAction.getCurrentContext(), familyName,
+                    false);
+        }
+        return Typeface.create_Original(familyName, style);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Typeface create(Typeface family, int style) {
+        return Typeface.create_Original(family, style);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static Typeface create(Typeface family, int style, boolean isItalic) {
+        return Typeface.create_Original(family, style, isItalic);
+    }
+
     // ---- Private delegate/helper methods ----
 
-    public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) {
-        mFontFamilies = fontFamilies;
-        mStyle = style;
-        mWeight = weight;
+    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;
+        }
+        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;
+    }
+
+    /**
+     * Return an Iterable of fonts that match the style and variant. The list is ordered
+     * according to preference of fonts.
+     * <p>
+     * The Iterator may contain null when the font failed to load. If null is reached when trying to
+     * render with this list of fonts, then a warning should be logged letting the user know that
+     * some font failed to load.
+     *
+     * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or {@link
+     * FontVariant#ELEGANT}
+     */
+    @NonNull
+    public Iterable<Font> getFonts(final FontVariant variant) {
+        assert variant != FontVariant.NONE;
+
+        return new FontsIterator(mFontFamilies, variant, mWeight, mStyle);
+    }
+
+    private static class FontsIterator implements Iterator<Font>, Iterable<Font> {
+        private final FontFamily_Delegate[] fontFamilies;
+        private final int weight;
+        private final boolean isItalic;
+        private final FontVariant variant;
+
+        private int index = 0;
+
+        private FontsIterator(@NonNull FontFamily_Delegate[] fontFamilies,
+                @NonNull FontVariant variant, int weight, int style) {
+            // Calculate the required weight based on style and weight of this typeface.
+            int boldExtraWeight =
+                    ((style & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
+            this.weight = Math.min(Math.max(100, weight + 50 + boldExtraWeight), 1000);
+            this.isItalic = (style & Font.ITALIC) != 0;
+            this.fontFamilies = fontFamilies;
+            this.variant = variant;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return index < fontFamilies.length;
+        }
+
+        @Override
+        @Nullable
+        public Font next() {
+            FontFamily_Delegate ffd = fontFamilies[index++];
+            if (ffd == null || !ffd.isValid()) {
+                return null;
+            }
+
+            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
+                // 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.
+                return null;
+            }
+
+            FontVariant ffdVariant = ffd.getVariant();
+            if (ffdVariant == FontVariant.NONE) {
+                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 (ffdVariant == variant) {
+                return font;
+            }
+
+            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
+        @Override
+        public Iterator<Font> iterator() {
+            return this;
+        }
+
+        @Override
+        public Spliterator<Font> spliterator() {
+            return Spliterators.spliterator(iterator(), fontFamilies.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 616784c..d9f8692 100644
--- a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -35,7 +35,9 @@
 import android.graphics.PathMeasure;
 import android.graphics.Path_Delegate;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.Region.Op;
+import android.graphics.Shader_Delegate;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -133,6 +135,12 @@
     }
 
     @LayoutlibDelegate
+    static void nSetAntiAlias(long rendererPtr, boolean aa) {
+        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
+        nativePathRenderer.setAntiAlias(aa);
+    }
+
+    @LayoutlibDelegate
     static void nSetAllowCaching(long rendererPtr, boolean allowCaching) {
         // ignored
     }
@@ -143,6 +151,9 @@
         VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
 
         Canvas_Delegate.nSave(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
+        Canvas_Delegate.nClipRect(canvasWrapperPtr,
+                bounds.left, bounds.top, bounds.right, bounds.bottom,
+                Region.Op.INTERSECT.nativeInt);
         Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.left, bounds.top);
 
         if (needsMirroring) {
@@ -1055,6 +1066,7 @@
         private Paint mStrokePaint;
         private Paint mFillPaint;
         private PathMeasure mPathMeasure;
+        private boolean mAntiAlias = true;
 
         private VPathRenderer_Delegate(long rootGroupPtr) {
             mRootGroupPtr = rootGroupPtr;
@@ -1164,7 +1176,7 @@
                     if (mFillPaint == null) {
                         mFillPaint = new Paint();
                         mFillPaint.setStyle(Style.FILL);
-                        mFillPaint.setAntiAlias(true);
+                        mFillPaint.setAntiAlias(mAntiAlias);
                     }
 
                     final Paint fillPaint = mFillPaint;
@@ -1175,17 +1187,30 @@
                     // mFillPaint can not be null at this point so we will have a delegate
                     assert fillPaintDelegate != null;
                     fillPaintDelegate.setColorFilter(filterPtr);
+
+                    Shader_Delegate shaderDelegate =
+                            Shader_Delegate.getDelegate(fullPath.mFillGradient);
+                    if (shaderDelegate != null) {
+                        // 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);
+                    }
+
                     fillPaintDelegate.setShader(fullPath.mFillGradient);
                     Path_Delegate.nSetFillType(mRenderPath.mNativePath, fullPath.mFillType);
                     BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
                             .getNativeInstance());
+                    if (shaderDelegate != null) {
+                        // Remove the local matrix
+                        shaderDelegate.setLocalMatrix(0);
+                    }
                 }
 
                 if (fullPath.mStrokeColor != Color.TRANSPARENT) {
                     if (mStrokePaint == null) {
                         mStrokePaint = new Paint();
                         mStrokePaint.setStyle(Style.STROKE);
-                        mStrokePaint.setAntiAlias(true);
+                        mStrokePaint.setAntiAlias(mAntiAlias);
                     }
 
                     final Paint strokePaint = mStrokePaint;
@@ -1243,6 +1268,10 @@
             return matrixScale;
         }
 
+        private void setAntiAlias(boolean aa) {
+            mAntiAlias = aa;
+        }
+
         @Override
         public void setName(String name) {
         }
diff --git a/bridge/src/android/os/Binder_Delegate.java b/bridge/src/android/os/Binder_Delegate.java
new file mode 100644
index 0000000..03596de
--- /dev/null
+++ b/bridge/src/android/os/Binder_Delegate.java
@@ -0,0 +1,54 @@
+/*
+ * 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 android.os;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+/**
+ * Delegate overriding selected methods of android.os.Binder
+ *
+ * Through the layoutlib_create tool, selected methods of Binder have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class Binder_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Binder_Delegate> sManager =
+            new DelegateManager<>(Binder_Delegate.class);
+    private static long sFinalizer = -1;
+
+    @LayoutlibDelegate
+    /*package*/ static long getNativeBBinderHolder() {
+        return sManager.addNewDelegate(new Binder_Delegate());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long getNativeFinalizer() {
+        synchronized (Binder_Delegate.class) {
+            if (sFinalizer == -1) {
+                sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+                        sManager::removeJavaReferenceFor);
+            }
+        }
+        return sFinalizer;
+    }
+}
diff --git a/bridge/src/android/preference/BridgePreferenceInflater.java b/bridge/src/android/preference/BridgePreferenceInflater.java
index cb56116..3665d86 100644
--- a/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -49,7 +49,8 @@
             preference = super.createItem(name, prefix, attrs);
         } catch (ClassNotFoundException | InflateException exception) {
             // name is probably not a valid preference type
-            if ("android.support.v7.preference".equals(prefix) &&
+            if (("android.support.v7.preference".equals(prefix) ||
+                    "androidx.preference".equals(prefix)) &&
                     "SwitchPreferenceCompat".equals(name)) {
                 preference = super.createItem("SwitchPreference", prefix, attrs);
             }
diff --git a/bridge/src/android/text/AndroidBidi_Delegate.java b/bridge/src/android/text/AndroidBidi_Delegate.java
deleted file mode 100644
index 38171dc..0000000
--- a/bridge/src/android/text/AndroidBidi_Delegate.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.icu.text.Bidi;
-
-/**
- * Delegate used to provide new implementation for the native methods of {@link AndroidBidi}
- *
- * Through the layoutlib_create tool, the original  methods of AndroidBidi have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class AndroidBidi_Delegate {
-
-    @LayoutlibDelegate
-    /*package*/ static int runBidi(int dir, char[] chars, byte[] charInfo, int count,
-            boolean haveInfo) {
-
-        switch (dir) {
-        case 0: // Layout.DIR_REQUEST_LTR
-            dir = Bidi.LTR;
-            break;
-        case 1: // Layout.DIR_REQUEST_RTL
-            dir = Bidi.RTL;
-            break;
-        case -1: // Layout.DIR_REQUEST_DEFAULT_RTL
-            dir = Bidi.LEVEL_DEFAULT_RTL;
-            break;
-        case -2: // Layout.DIR_REQUEST_DEFAULT_LTR
-            dir = Bidi.LEVEL_DEFAULT_LTR;
-            break;
-        default:
-            // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue.
-            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null);
-            dir = Bidi.LEVEL_DEFAULT_LTR;
-        }
-        Bidi bidi = new Bidi(chars, 0, null, 0, count, dir);
-        if (charInfo != null) {
-            for (int i = 0; i < count; ++i)
-            charInfo[i] = bidi.getLevelAt(i);
-        }
-        return bidi.getParaLevel();
-    }
-}
diff --git a/bridge/src/android/text/Hyphenator_Delegate.java b/bridge/src/android/text/Hyphenator_Delegate.java
deleted file mode 100644
index 499e58a..0000000
--- a/bridge/src/android/text/Hyphenator_Delegate.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.io.File;
-import java.nio.ByteBuffer;
-
-/**
- * Delegate that overrides implementation for certain methods in {@link android.text.Hyphenator}
- * <p/>
- * Through the layoutlib_create tool, selected methods of Hyphenator have been replaced
- * by calls to methods of the same name in this delegate class.
- */
-public class Hyphenator_Delegate {
-
-    private static final DelegateManager<Hyphenator_Delegate> sDelegateManager = new
-            DelegateManager<Hyphenator_Delegate>(Hyphenator_Delegate.class);
-
-    @LayoutlibDelegate
-    /*package*/ static File getSystemHyphenatorLocation() {
-        // FIXME
-        return null;
-    }
-
-    /*package*/ @SuppressWarnings("UnusedParameters")  // TODO implement this.
-    static long loadHyphenator(ByteBuffer buffer, int offset, int minPrefix, int minSuffix) {
-        return sDelegateManager.addNewDelegate(new Hyphenator_Delegate());
-    }
-}
diff --git a/bridge/src/android/text/MeasuredParagraph_Delegate.java b/bridge/src/android/text/MeasuredParagraph_Delegate.java
new file mode 100644
index 0000000..4694890
--- /dev/null
+++ b/bridge/src/android/text/MeasuredParagraph_Delegate.java
@@ -0,0 +1,191 @@
+/*
+ * 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 android.text;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.graphics.BidiRenderer;
+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 java.util.ArrayList;
+import java.util.Arrays;
+
+import libcore.util.NativeAllocationRegistry_Delegate;
+
+/**
+ * Delegate that provides implementation for native methods in {@link android.text.MeasuredParagraph}
+ * <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 {
+
+    // ---- 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;
+
+    private long mNativeBuilderPtr;
+
+    @LayoutlibDelegate
+    /*package*/ static long nInitBuilder() {
+        return sBuilderManager.addNewDelegate(new MeasuredParagraphBuilder());
+    }
+
+    /**
+     * Apply style to make native measured text.
+     *
+     * @param nativeBuilderPtr The native MeasuredParagraph 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.
+     * @param isRtl True if the text is RTL.
+     */
+    @LayoutlibDelegate
+    /*package*/ static void nAddStyleRun(long nativeBuilderPtr, long paintPtr, int start,
+            int end, boolean isRtl) {
+        MeasuredParagraphBuilder builder = sBuilderManager.getDelegate(nativeBuilderPtr);
+        if (builder == null) {
+            return;
+        }
+        builder.mRuns.add(new StyleRun(paintPtr, start, end, isRtl));
+    }
+
+    /**
+     * Apply ReplacementRun to make native measured text.
+     *
+     * @param nativeBuilderPtr The native MeasuredParagraph 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.
+     * @param width The width of the replacement.
+     */
+    @LayoutlibDelegate
+    /*package*/ static void nAddReplacementRun(long nativeBuilderPtr, long paintPtr, int start,
+            int end, float width) {
+        MeasuredParagraphBuilder builder = sBuilderManager.getDelegate(nativeBuilderPtr);
+        if (builder == null) {
+            return;
+        }
+        builder.mRuns.add(new ReplacementRun(start, end, width));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nBuildNativeMeasuredParagraph(long nativeBuilderPtr,
+            @NonNull char[] text, boolean computeHyphenation, boolean computeLayout) {
+        MeasuredParagraph_Delegate delegate = new MeasuredParagraph_Delegate();
+        delegate.mNativeBuilderPtr = nativeBuilderPtr;
+        return sManager.addNewDelegate(delegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nFreeBuilder(long nativeBuilderPtr) {
+        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);
+        RectF bounds =
+                new BidiRenderer(null, paint, text).renderText(index, index + count, bidiFlags,
+                        widths, 0, false);
+        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;
+
+        private StyleRun(long nativePaint, int start, int end, boolean isRtl) {
+            super(start, end);
+            mNativePaint = nativePaint;
+            mIsRtl = isRtl;
+        }
+
+        @Override
+        void addTo(Builder builder) {
+            int bidiFlags = mIsRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
+            measureText(mNativePaint, builder.mText, mStart, mEnd - mStart, builder.mWidths,
+                    bidiFlags);
+        }
+    }
+
+    private static class ReplacementRun extends Run {
+        private final float mWidth;
+
+        private ReplacementRun(int start, int end, float width) {
+            super(start, end);
+            mWidth = width;
+        }
+
+        @Override
+        void addTo(Builder builder) {
+            builder.mWidths[mStart] = mWidth;
+            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/text/StaticLayout_Delegate.java b/bridge/src/android/text/StaticLayout_Delegate.java
index 7c59f38..d7cb596 100644
--- a/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/bridge/src/android/text/StaticLayout_Delegate.java
@@ -4,19 +4,15 @@
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.annotation.NonNull;
-import android.graphics.BidiRenderer;
-import android.graphics.Paint;
-import android.graphics.Paint_Delegate;
-import android.graphics.RectF;
+import android.annotation.Nullable;
 import android.icu.text.BreakIterator;
-import android.icu.util.ULocale;
+import android.text.Layout.BreakStrategy;
+import android.text.Layout.HyphenationFrequency;
 import android.text.Primitive.PrimitiveType;
 import android.text.StaticLayout.LineBreaks;
 
-import java.nio.ByteBuffer;
 import java.text.CharacterIterator;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import javax.swing.text.Segment;
@@ -40,107 +36,61 @@
         new DelegateManager<Builder>(Builder.class);
 
     @LayoutlibDelegate
-    /*package*/ static long nNewBuilder() {
-        return sBuilderManager.addNewDelegate(new Builder());
+    /*package*/ static long nInit(
+            @BreakStrategy int breakStrategy,
+            @HyphenationFrequency int hyphenationFrequency,
+            boolean isJustified,
+            @Nullable int[] indents,
+            @Nullable int[] leftPaddings,
+            @Nullable int[] rightPaddings) {
+        Builder builder = new Builder();
+        builder.mBreakStrategy = breakStrategy;
+        return sBuilderManager.addNewDelegate(builder);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nFreeBuilder(long nativeBuilder) {
-        sBuilderManager.removeJavaReferenceFor(nativeBuilder);
+    /*package*/ static void nFinish(long nativePtr) {
+        sBuilderManager.removeJavaReferenceFor(nativePtr);
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nFinishBuilder(long nativeBuilder) {
-    }
+    /*package*/ static int nComputeLineBreaks(
+            /* non zero */ long nativePtr,
 
-    @LayoutlibDelegate
-    /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset, int minPrefix,
-            int minSuffix) {
-        return Hyphenator_Delegate.loadHyphenator(buf, offset, minPrefix, minSuffix);
-    }
+            // Inputs
+            @NonNull char[] text,
+            long measuredTextPtr,
+            int length,
+            float firstWidth,
+            int firstWidthLineCount,
+            float restWidth,
+            @Nullable int[] variableTabStops,
+            int defaultTabStop,
+            int indentsOffset,
 
-    @LayoutlibDelegate
-    /*package*/ static void nSetLocales(long nativeBuilder, String locales,
-            long[] nativeHyphenators) {
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
-        if (builder != null) {
-            builder.mLocales = locales;
-            builder.mNativeHyphenators = nativeHyphenators;
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetIndents(long nativeBuilder, int[] indents) {
-        // TODO.
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nSetupParagraph(long nativeBuilder, char[] text, int length,
-            float firstWidth, int firstWidthLineCount, float restWidth,
-            int[] variableTabStops, int defaultTabStop, int breakStrategy,
-            int hyphenationFrequency, boolean isJustified) {
-        // TODO: implement justified alignment
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+            // Outputs
+            @NonNull LineBreaks recycle,
+            int recycleLength,
+            @NonNull int[] recycleBreaks,
+            @NonNull float[] recycleWidths,
+            @NonNull float[] recycleAscents,
+            @NonNull float[] recycleDescents,
+            @NonNull int[] recycleFlags,
+            @NonNull float[] charWidths) {
+        Builder builder = sBuilderManager.getDelegate(nativePtr);
         if (builder == null) {
-            return;
+            return 0;
         }
 
         builder.mText = text;
         builder.mWidths = new float[length];
         builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
         builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
-    }
 
-    @LayoutlibDelegate
-    /*package*/ static float nAddStyleRun(long nativeBuilder, long nativePaint, long nativeTypeface,
-            int start, int end, boolean isRtl) {
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
-
-        int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
-        return builder == null ? 0 :
-                measureText(nativePaint, builder.mText, start, end - start, builder.mWidths,
-                        bidiFlags);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddMeasuredRun(long nativeBuilder, int start, int end, float[] widths) {
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
-        if (builder != null) {
-            System.arraycopy(widths, start, builder.mWidths, start, end - start);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width) {
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
-        if (builder == null) {
-            return;
-        }
-        builder.mWidths[start] = width;
-        Arrays.fill(builder.mWidths, start + 1, end, 0.0f);
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) {
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
-        if (builder != null) {
-            System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length);
-        }
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static int nComputeLineBreaks(long nativeBuilder,
-            LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths,
-            int[] recycleFlags, int recycleLength) {
-
-        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
-        if (builder == null) {
-            return 0;
-        }
+        MeasuredParagraph_Delegate.computeRuns(measuredTextPtr, builder);
 
         // compute all possible breakpoints.
-        int length = builder.mWidths.length;
-        BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocales));
+        BreakIterator it = BreakIterator.getLineInstance();
         it.setText((CharacterIterator) new Segment(builder.mText, 0, length));
 
         // average word length in english is 5. So, initialize the possible breaks with a guess.
@@ -171,6 +121,7 @@
                         builder.mTabStopCalculator);
         }
         builder.mLineBreaker.computeBreaks(recycle);
+        System.arraycopy(builder.mWidths, 0, charWidths, 0, builder.mWidths.length);
         return recycle.breaks.length;
     }
 
@@ -215,26 +166,28 @@
         return primitives;
     }
 
-    private static float measureText(long nativePaint, char []text, int index, int count,
-            float[] widths, int bidiFlags) {
-        Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
-        RectF bounds = new BidiRenderer(null, paint, text)
-            .renderText(index, index + count, bidiFlags, widths, 0, false);
-        return bounds.right - bounds.left;
-    }
-
     // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker.
     /**
      * Java representation of the native Builder class.
      */
-    private static class Builder {
-        String mLocales;
+    public static class Builder {
         char[] mText;
         float[] mWidths;
-        LineBreaker mLineBreaker;
-        long[] mNativeHyphenators;
-        int mBreakStrategy;
-        LineWidth mLineWidth;
-        TabStops mTabStopCalculator;
+        private LineBreaker mLineBreaker;
+        private int mBreakStrategy;
+        private LineWidth mLineWidth;
+        private TabStops mTabStopCalculator;
+    }
+
+    public abstract static class Run {
+        int mStart;
+        int mEnd;
+
+        Run(int start, int end) {
+            mStart = start;
+            mEnd = end;
+        }
+
+        abstract void addTo(Builder builder);
     }
 }
diff --git a/bridge/src/android/view/AttachInfo_Accessor.java b/bridge/src/android/view/AttachInfo_Accessor.java
index 4445a22..60c13c0 100644
--- a/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/bridge/src/android/view/AttachInfo_Accessor.java
@@ -16,13 +16,12 @@
 
 package android.view;
 
-import com.android.layoutlib.bridge.android.BridgeWindow;
-import com.android.layoutlib.bridge.android.BridgeWindowSession;
-
 import android.content.Context;
 import android.os.Handler;
 import android.view.View.AttachInfo;
 
+import com.android.layoutlib.bridge.util.ReflectionUtils;
+
 /**
  * Class allowing access to package-protected methods/fields.
  */
@@ -33,8 +32,9 @@
         WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
         Display display = wm.getDefaultDisplay();
         ViewRootImpl root = new ViewRootImpl(context, display);
-        AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
-                display, root, new Handler(), null, context);
+        AttachInfo info = new AttachInfo(ReflectionUtils.createProxy(IWindowSession.class),
+                ReflectionUtils.createProxy(IWindow.class), display, root, new Handler(), null,
+                context);
         info.mHasWindowFocus = true;
         info.mWindowVisibility = View.VISIBLE;
         info.mInTouchMode = false; // this is so that we can display selections.
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index 58d8c52..84fd0ed 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -31,6 +31,8 @@
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.util.ReflectionUtils;
 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;
@@ -45,25 +47,13 @@
 import android.widget.NumberPicker;
 
 import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
+import java.util.function.BiFunction;
 
-import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
-import static com.android.SdkConstants.BUTTON;
-import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
-import static com.android.SdkConstants.CHECK_BOX;
-import static com.android.SdkConstants.EDIT_TEXT;
-import static com.android.SdkConstants.IMAGE_BUTTON;
-import static com.android.SdkConstants.IMAGE_VIEW;
-import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
-import static com.android.SdkConstants.RADIO_BUTTON;
-import static com.android.SdkConstants.SEEK_BAR;
-import static com.android.SdkConstants.SPINNER;
-import static com.android.SdkConstants.TEXT_VIEW;
 import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext;
 
 /**
@@ -72,21 +62,7 @@
 public final class BridgeInflater extends LayoutInflater {
 
     private final LayoutlibCallback mLayoutlibCallback;
-    /**
-     * If true, the inflater will try to replace the framework widgets with the AppCompat versions.
-     * Ideally, this should be based on the activity being an AppCompat activity but since that is
-     * not trivial to check from layoutlib, we currently base the decision on the current theme
-     * being an AppCompat theme.
-     */
-    private boolean mLoadAppCompatViews;
-    /**
-     * This set contains the framework views that have an AppCompat version but failed to load.
-     * This might happen because not all widgets are contained in all versions of the support
-     * library.
-     * This will help us to avoid trying to load the AppCompat version multiple times if it
-     * doesn't exist.
-     */
-    private Set<String> mFailedAppCompatViews = new HashSet<>();
+
     private boolean mIsInMerge = false;
     private ResourceReference mResourceReference;
     private Map<View, String> mOpenDrawerLayouts;
@@ -94,15 +70,6 @@
     // Keep in sync with the same value in LayoutInflater.
     private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme };
 
-    private static final String APPCOMPAT_WIDGET_PREFIX = "android.support.v7.widget.AppCompat";
-    /** List of platform widgets that have an AppCompat version */
-    private static final Set<String> APPCOMPAT_VIEWS = Collections.unmodifiableSet(
-            new HashSet<>(
-                    Arrays.asList(TEXT_VIEW, IMAGE_VIEW, BUTTON, EDIT_TEXT, SPINNER,
-                            IMAGE_BUTTON, CHECK_BOX, RADIO_BUTTON, CHECKED_TEXT_VIEW,
-                            AUTO_COMPLETE_TEXT_VIEW, MULTI_AUTO_COMPLETE_TEXT_VIEW, "RatingBar",
-                            SEEK_BAR)));
-
     /**
      * List of class prefixes which are tried first by default.
      * <p/>
@@ -113,6 +80,7 @@
         "android.webkit.",
         "android.app."
     };
+    private BiFunction<String, AttributeSet, View> mCustomInflater;
 
     public static String[] getClassPrefixList() {
         return sClassPrefixList;
@@ -121,13 +89,9 @@
     private BridgeInflater(LayoutInflater original, Context newContext) {
         super(original, newContext);
         newContext = getBaseContext(newContext);
-        if (newContext instanceof BridgeContext) {
-            mLayoutlibCallback = ((BridgeContext) newContext).getLayoutlibCallback();
-            mLoadAppCompatViews = ((BridgeContext) newContext).isAppCompatTheme();
-        } else {
-            mLayoutlibCallback = null;
-            mLoadAppCompatViews = false;
-        }
+        mLayoutlibCallback = (newContext instanceof BridgeContext) ?
+                ((BridgeContext) newContext).getLayoutlibCallback() :
+                null;
     }
 
     /**
@@ -140,26 +104,14 @@
         super(context);
         mLayoutlibCallback = layoutlibCallback;
         mConstructorArgs[0] = context;
-        mLoadAppCompatViews = context.isAppCompatTheme();
     }
 
     @Override
     public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
-        View view = null;
+        View view = createViewFromCustomInflater(name, attrs);
 
-        try {
-            if (mLoadAppCompatViews
-                    && APPCOMPAT_VIEWS.contains(name)
-                    && !mFailedAppCompatViews.contains(name)) {
-                // We are using an AppCompat theme so try to load the appcompat views
-                view = loadCustomView(APPCOMPAT_WIDGET_PREFIX + name, attrs, true);
-
-                if (view == null) {
-                    mFailedAppCompatViews.add(name); // Do not try this one anymore
-                }
-            }
-
-            if (view == null) {
+        if (view == null) {
+            try {
                 // First try to find a class using the default Android prefixes
                 for (String prefix : sClassPrefixList) {
                     try {
@@ -181,19 +133,19 @@
                 } catch (ClassNotFoundException e) {
                     // Ignore. We'll try again using the custom view loader below.
                 }
-            }
 
-            // Finally try again using the custom view loader
-            if (view == null) {
-                view = loadCustomView(name, attrs);
+                // Finally try again using the custom view loader
+                if (view == null) {
+                    view = loadCustomView(name, attrs);
+                }
+            } catch (InflateException e) {
+                // Don't catch the InflateException below as that results in hiding the real cause.
+                throw e;
+            } catch (Exception e) {
+                // Wrap the real exception in a ClassNotFoundException, so that the calling method
+                // can deal with it.
+                throw new ClassNotFoundException("onCreateView", e);
             }
-        } catch (InflateException e) {
-            // Don't catch the InflateException below as that results in hiding the real cause.
-            throw e;
-        } catch (Exception e) {
-            // Wrap the real exception in a ClassNotFoundException, so that the calling method
-            // can deal with it.
-            throw new ClassNotFoundException("onCreateView", e);
         }
 
         setupViewInContext(view, attrs);
@@ -201,6 +153,110 @@
         return view;
     }
 
+    /**
+     * Finds the createView method in the given customInflaterClass. Since createView is
+     * currently package protected, it will show in the declared class so we iterate up the
+     * hierarchy and return the first instance we find.
+     * The returned method will be accessible.
+     */
+    @NotNull
+    private static Method getCreateViewMethod(Class<?> customInflaterClass) throws NoSuchMethodException {
+        Class<?> current = customInflaterClass;
+        do {
+            try {
+                Method method = current.getDeclaredMethod("createView", View.class, String.class,
+                                Context.class, AttributeSet.class, boolean.class, boolean.class,
+                                boolean.class, boolean.class);
+                method.setAccessible(true);
+                return method;
+            } catch (NoSuchMethodException ignore) {
+            }
+            current = current.getSuperclass();
+        } while (current != null && current != Object.class);
+
+        throw new NoSuchMethodException();
+    }
+
+    /**
+     * Finds the custom inflater class. If it's defined in the theme, we'll use that one (if the
+     * class does not exist, null is returned).
+     * If {@code viewInflaterClass} is not defined in the theme, we'll try to instantiate
+     * {@code android.support.v7.app.AppCompatViewInflater}
+     */
+    @Nullable
+    private static Class<?> findCustomInflater(@NotNull BridgeContext bc,
+            @NotNull LayoutlibCallback layoutlibCallback) {
+        ResourceValue value = bc.getRenderResources().findItemInTheme("viewInflaterClass", false);
+        String inflaterName = value != null ? value.getValue() : null;
+
+        if (inflaterName != null) {
+            try {
+                return layoutlibCallback.findClass(inflaterName);
+            } catch (ClassNotFoundException ignore) {
+            }
+
+            // 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
+            try {
+                return layoutlibCallback.findClass("android.support.v7.app.AppCompatViewInflater");
+            } catch (ClassNotFoundException ignore) {
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Checks if there is a custom inflater and, when present, tries to instantiate the view
+     * using it.
+     */
+    @Nullable
+    private View createViewFromCustomInflater(@NotNull String name, @NotNull AttributeSet attrs) {
+        if (mCustomInflater == null) {
+            Context context = getContext();
+            context = getBaseContext(context);
+            if (context instanceof BridgeContext) {
+                BridgeContext bc = (BridgeContext) context;
+                Class<?> inflaterClass = findCustomInflater(bc, mLayoutlibCallback);
+
+                if (inflaterClass != null) {
+                    try {
+                        Constructor<?> constructor =  inflaterClass.getDeclaredConstructor();
+                        constructor.setAccessible(true);
+                        Object inflater = constructor.newInstance();
+                        Method method = getCreateViewMethod(inflaterClass);
+                        Context finalContext = context;
+                        mCustomInflater = (viewName, attributeSet) -> {
+                            try {
+                                return (View) method.invoke(inflater, null, viewName, finalContext,
+                                        attributeSet,
+                                        false,
+                                        false /*readAndroidTheme*/, // No need after L
+                                        true /*readAppTheme*/,
+                                        true /*wrapContext*/);
+                            } catch (IllegalAccessException | InvocationTargetException e) {
+                                assert false : "Call to createView failed";
+                            }
+                            return null;
+                        };
+                    } catch (InvocationTargetException | IllegalAccessException |
+                            NoSuchMethodException | InstantiationException ignore) {
+                    }
+                }
+            }
+
+            if (mCustomInflater == null) {
+                // There is no custom inflater. We'll create a nop custom inflater to avoid the
+                // penalty of trying to instantiate again
+                mCustomInflater = (s, attributeSet) -> null;
+            }
+        }
+
+        return mCustomInflater.apply(name, attrs);
+    }
+
     @Override
     public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
             boolean ignoreThemeAttr) {
diff --git a/bridge/src/android/view/IWindowManagerImpl.java b/bridge/src/android/view/IWindowManagerImpl.java
index 82cec66..b1d9151 100644
--- a/bridge/src/android/view/IWindowManagerImpl.java
+++ b/bridge/src/android/view/IWindowManagerImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.app.IAssistDataReceiver;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
@@ -28,8 +29,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
+import android.view.RemoteAnimationAdapter;
 
-import com.android.internal.app.IAssistScreenshotReceiver;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
@@ -76,6 +77,11 @@
     // ---- 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
 
@@ -156,12 +162,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedInputMode() throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
     public boolean inputMethodClientHasFocus(IInputMethodClient arg0) throws RemoteException {
         // TODO Auto-generated method stub
         return false;
@@ -243,6 +243,10 @@
     }
 
     @Override
+    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter adapter) {
+    }
+
+    @Override
     public void prepareAppTransition(int arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
 
@@ -261,7 +265,7 @@
     }
 
     @Override
-    public boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver)
+    public boolean requestAssistScreenshot(IAssistDataReceiver receiver)
             throws RemoteException {
         // TODO Auto-generated method stub
         return false;
@@ -342,7 +346,7 @@
     }
 
     @Override
-    public void setScreenCaptureDisabled(int userId, boolean disabled) {
+    public void refreshScreenCaptureDisabled(int userId) {
         // TODO Auto-generated method stub
     }
 
@@ -383,6 +387,15 @@
     }
 
     @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;
@@ -422,7 +435,8 @@
     }
 
     @Override
-    public void dismissKeyguard(IKeyguardDismissCallback callback) throws RemoteException {
+    public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message)
+            throws RemoteException {
     }
 
     @Override
@@ -469,10 +483,6 @@
     }
 
     @Override
-    public void setDockedStackResizing(boolean resizing) throws RemoteException {
-    }
-
-    @Override
     public void endProlongedAnimations() {
     }
 
@@ -507,7 +517,7 @@
         throws RemoteException {}
 
     @Override
-    public void createInputConsumer(String name, InputChannel inputChannel)
+    public void createInputConsumer(IBinder token, String name, InputChannel inputChannel)
             throws RemoteException {}
 
     @Override
@@ -521,14 +531,6 @@
     }
 
     @Override
-    public void enableSurfaceTrace(ParcelFileDescriptor fd) throws RemoteException {
-    }
-
-    @Override
-    public void disableSurfaceTrace() throws RemoteException {
-    }
-
-    @Override
     public Region getCurrentImeTouchRegion() throws RemoteException {
         return null;
     }
@@ -545,6 +547,23 @@
     }
 
     @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/MenuInflater_Delegate.java b/bridge/src/android/view/MenuInflater_Delegate.java
index 08a97d6..d16d851 100644
--- a/bridge/src/android/view/MenuInflater_Delegate.java
+++ b/bridge/src/android/view/MenuInflater_Delegate.java
@@ -42,7 +42,6 @@
  * ViewInfo}, we check the corresponding view key in the menu item for the view and add it
  */
 public class MenuInflater_Delegate {
-
     @LayoutlibDelegate
     /*package*/ static void registerMenu(MenuInflater thisInflater, MenuItem menuItem,
             AttributeSet attrs) {
@@ -56,10 +55,18 @@
                 return;
             }
         }
-        // This means that Bridge did not take over the instantiation of some object properly.
-        // This is most likely a bug in the LayoutLib code.
-        Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
-                "Action Bar Menu rendering may be incorrect.", null);
+
+        String menuItemName = menuItem != null ? menuItem.getClass().getName() : null;
+        if (menuItemName == null ||
+                !menuItemName.startsWith("android.support.") ||
+                !menuItemName.startsWith("androidx.")) {
+            // This means that Bridge did not take over the instantiation of some object properly.
+            // This is most likely a bug in the LayoutLib code.
+            // We suppress this error for AppCompat menus since we do not support them in the menu
+            // editor yet.
+            Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
+                    "Action Bar Menu rendering may be incorrect.", null);
+        }
 
     }
 
diff --git a/bridge/src/android/view/RectShadowPainter.java b/bridge/src/android/view/RectShadowPainter.java
index 5665d4f..88771a7 100644
--- a/bridge/src/android/view/RectShadowPainter.java
+++ b/bridge/src/android/view/RectShadowPainter.java
@@ -19,8 +19,10 @@
 import com.android.layoutlib.bridge.impl.GcSnapshot;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 
+import android.graphics.BaseCanvas_Delegate;
 import android.graphics.Canvas;
 import android.graphics.Canvas_Delegate;
+import android.graphics.Color;
 import android.graphics.LinearGradient;
 import android.graphics.Outline;
 import android.graphics.Paint;
@@ -46,7 +48,8 @@
     private static final int END_COLOR = ResourceHelper.getColor("#03000000");
     private static final float PERPENDICULAR_ANGLE = 90f;
 
-    public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas) {
+    public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas,
+            float alpha) {
         Rect outline = new Rect();
         if (!viewOutline.getRect(outline)) {
             assert false : "Outline is not a rect shadow";
@@ -74,9 +77,16 @@
             edgePaint.setAntiAlias(false);
             float outerArcRadius = radius + shadowSize;
             int[] colors = {START_COLOR, START_COLOR, END_COLOR};
+            if (alpha != 1f) {
+                // Correct colors using the given component alpha
+                for (int i = 0; i < colors.length; i++) {
+                    colors[i] = Color.argb((int) (Color.alpha(colors[i]) * alpha), Color.red(colors[i]),
+                            Color.green(colors[i]), Color.blue(colors[i]));
+                }
+            }
             cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
                     new float[]{0f, radius / outerArcRadius, 1f}, TileMode.CLAMP));
-            edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, START_COLOR, END_COLOR,
+            edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, colors[0], colors[2],
                     TileMode.CLAMP));
             Path path = new Path();
             path.setFillType(FillType.EVEN_ODD);
@@ -184,7 +194,8 @@
     /**
      * Differs from {@link RectF#isEmpty()} as this first converts the rect to int and then checks.
      * <p/>
-     * This is required because {@link Canvas_Delegate#native_drawRect(long, float, float, float,
+     * This is required because {@link BaseCanvas_Delegate#native_drawRect(long, float, float,
+     * float,
      * float, long)} casts the co-ordinates to int and we want to ensure that it doesn't end up
      * drawing empty rectangles, which results in IllegalArgumentException.
      */
diff --git a/bridge/src/android/view/ShadowPainter.java b/bridge/src/android/view/ShadowPainter.java
index f09fffd..788c6c3 100644
--- a/bridge/src/android/view/ShadowPainter.java
+++ b/bridge/src/android/view/ShadowPainter.java
@@ -41,14 +41,16 @@
      * @param source the source image
      * @param shadowSize the size of the shadow, normally {@link #SHADOW_SIZE or {@link
      * #SMALL_SHADOW_SIZE}}
+     * @param alpha alpha value to apply to the shadow
      *
      * @return an image with the shadow painted in or the source image if shadowSize <= 1
      */
     @NonNull
-    public static BufferedImage createDropShadow(BufferedImage source, int shadowSize) {
+    public static BufferedImage createDropShadow(BufferedImage source, int shadowSize, float
+            alpha) {
         shadowSize /= 2; // make shadow size have the same meaning as in the other shadow paint methods in this class
 
-        return createDropShadow(source, shadowSize, 0.7f, 0);
+        return createDropShadow(source, shadowSize, 0.7f * alpha, 0);
     }
 
     /**
diff --git a/bridge/src/android/view/ViewGroup_Delegate.java b/bridge/src/android/view/ViewGroup_Delegate.java
index 4b760a7..10a4f31 100644
--- a/bridge/src/android/view/ViewGroup_Delegate.java
+++ b/bridge/src/android/view/ViewGroup_Delegate.java
@@ -67,31 +67,33 @@
             Outline outline) {
         float elevation = getElevation(child, parent);
         if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
-            RectShadowPainter.paintShadow(outline, elevation, canvas);
+            RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha());
             return;
         }
         BufferedImage shadow = null;
         if (outline.mPath != null) {
-            shadow = getPathShadow(outline, canvas, elevation);
+            shadow = getPathShadow(outline, canvas, elevation, child.getAlpha());
         }
         if (shadow == null) {
             return;
         }
         Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
                 Density.getEnum(canvas.getDensity()));
+        canvas.save();
         Rect clipBounds = canvas.getClipBounds();
         Rect newBounds = new Rect(clipBounds);
         newBounds.inset((int)-elevation, (int)-elevation);
         canvas.clipRect(newBounds, Op.REPLACE);
         canvas.drawBitmap(bitmap, 0, 0, null);
-        canvas.clipRect(clipBounds, Op.REPLACE);
+        canvas.restore();
     }
 
     private static float getElevation(View child, ViewGroup parent) {
         return child.getZ() - parent.getZ();
     }
 
-    private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation) {
+    private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation,
+            float alpha) {
         Rect clipBounds = canvas.getClipBounds();
         if (clipBounds.isEmpty()) {
           return null;
@@ -101,7 +103,7 @@
         Graphics2D graphics = image.createGraphics();
         graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
         graphics.dispose();
-        return ShadowPainter.createDropShadow(image, (int) elevation);
+        return ShadowPainter.createDropShadow(image, (int) elevation, alpha);
     }
 
     // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
@@ -144,11 +146,11 @@
                         canvas.concat(transformToApply.getMatrix());
                         canvas.translate(transX, transY);
                     }
-                    if (!childHasIdentityMatrix) {
-                        canvas.translate(-transX, -transY);
-                        canvas.concat(child.getMatrix());
-                        canvas.translate(transX, transY);
-                    }
+                }
+                if (!childHasIdentityMatrix) {
+                    canvas.translate(-transX, -transY);
+                    canvas.concat(child.getMatrix());
+                    canvas.translate(transX, transY);
                 }
 
             }
diff --git a/bridge/src/android/view/View_Delegate.java b/bridge/src/android/view/View_Delegate.java
index 408ec54..5d39e4c 100644
--- a/bridge/src/android/view/View_Delegate.java
+++ b/bridge/src/android/view/View_Delegate.java
@@ -16,10 +16,13 @@
 
 package android.view;
 
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.Context;
+import android.graphics.Canvas;
 import android.os.IBinder;
 
 /**
@@ -44,4 +47,50 @@
         }
         return null;
     }
+
+    @LayoutlibDelegate
+    /*package*/ static void draw(View thisView, Canvas canvas) {
+        try {
+            // This code is run within a catch to prevent misbehaving components from breaking
+            // all the layout.
+            thisView.draw_Original(canvas);
+        } catch (Throwable t) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View draw failed", t, null);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean draw(
+            View thisView, Canvas canvas, ViewGroup parent, long drawingTime) {
+        try {
+            // This code is run within a catch to prevent misbehaving components from breaking
+            // all the layout.
+            return thisView.draw_Original(canvas, parent, drawingTime);
+        } catch (Throwable t) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View draw failed", t, null);
+        }
+        return false;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void measure(View thisView, int widthMeasureSpec, int heightMeasureSpec) {
+        try {
+            // This code is run within a catch to prevent misbehaving components from breaking
+            // all the layout.
+            thisView.measure_Original(widthMeasureSpec, heightMeasureSpec);
+        } catch (Throwable t) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View measure failed", t, null);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void layout(View thisView, int l, int t, int r, int b) {
+        try {
+            // This code is run within a catch to prevent misbehaving components from breaking
+            // all the layout.
+            thisView.layout_Original(l, t, r, b);
+        } catch (Throwable th) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "View layout failed", th, null);
+        }
+    }
 }
diff --git a/bridge/src/android/view/accessibility/AccessibilityManager.java b/bridge/src/android/view/accessibility/AccessibilityManager.java
index 11cb046..84b4064 100644
--- a/bridge/src/android/view/accessibility/AccessibilityManager.java
+++ b/bridge/src/android/view/accessibility/AccessibilityManager.java
@@ -17,6 +17,7 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -24,6 +25,7 @@
 import android.os.Handler;
 import android.view.IWindow;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent.EventType;
 
 import java.util.Collections;
 import java.util.List;
@@ -90,6 +92,60 @@
         public void onHighTextContrastStateChanged(boolean enabled);
     }
 
+    /**
+     * Policy to inject behavior into the accessibility manager.
+     *
+     * @hide
+     */
+    public interface AccessibilityPolicy {
+        /**
+         * Checks whether accessibility is enabled.
+         *
+         * @param accessibilityEnabled Whether the accessibility layer is enabled.
+         * @return whether accessibility is enabled.
+         */
+        boolean isEnabled(boolean accessibilityEnabled);
+
+        /**
+         * Notifies the policy for an accessibility event.
+         *
+         * @param event The event.
+         * @param accessibilityEnabled Whether the accessibility layer is enabled.
+         * @param relevantEventTypes The events relevant events.
+         * @return The event to dispatch or null.
+         */
+        @Nullable AccessibilityEvent onAccessibilityEvent(@NonNull AccessibilityEvent event,
+                boolean accessibilityEnabled, @EventType int relevantEventTypes);
+
+        /**
+         * Gets the list of relevant events.
+         *
+         * @param relevantEventTypes The relevant events.
+         * @return The relevant events to report.
+         */
+        @EventType int getRelevantEventTypes(@EventType int relevantEventTypes);
+
+        /**
+         * Gets the list of installed services to report.
+         *
+         * @param installedService The installed services.
+         * @return The services to report.
+         */
+        @NonNull List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+                @Nullable List<AccessibilityServiceInfo> installedService);
+
+        /**
+         * Gets the list of enabled accessibility services.
+         *
+         * @param feedbackTypeFlags The feedback type to query for.
+         * @param enabledService The enabled services.
+         * @return The services to report.
+         */
+        @Nullable List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+                @FeedbackType int feedbackTypeFlags,
+                @Nullable List<AccessibilityServiceInfo> enabledService);
+    }
+
     private final IAccessibilityManagerClient.Stub mClient =
             new IAccessibilityManagerClient.Stub() {
                 public void setState(int state) {
@@ -159,6 +215,18 @@
     }
 
     /**
+     * Returns whether there are observers registered for this event type. If
+     * this method returns false you shuold not generate events of this type
+     * to conserve resources.
+     *
+     * @param type The event type.
+     * @return Whether the event is being observed.
+     */
+    public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+        return false;
+    }
+
+    /**
      * Requests interruption of the accessibility feedback from all accessibility services.
      */
     public void interrupt() {
diff --git a/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java b/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
index 7c98847..b2a183b 100644
--- a/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
+++ b/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
@@ -16,10 +16,10 @@
 
 package android.view.inputmethod;
 
-import com.android.layoutlib.bridge.android.BridgeIInputMethodManager;
+import com.android.internal.view.IInputMethodManager;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.content.Context;
 import android.os.Looper;
 
 
@@ -39,8 +39,8 @@
         synchronized (InputMethodManager.class) {
             InputMethodManager imm = InputMethodManager.peekInstance();
             if (imm == null) {
-                imm = new InputMethodManager(
-                        new BridgeIInputMethodManager(), Looper.getMainLooper());
+                imm = new InputMethodManager(ReflectionUtils.createProxy(IInputMethodManager.class),
+                        Looper.getMainLooper());
                 InputMethodManager.sInstance = imm;
             }
             return imm;
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 93fd005..5dca8e7 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -56,6 +56,7 @@
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.WeakHashMap;
 import java.util.concurrent.locks.ReentrantLock;
 
 import libcore.io.MemoryMappedFile_Delegate;
@@ -104,10 +105,9 @@
     private final static DynamicIdMap sDynamicIds = new DynamicIdMap(DYNAMIC_ID_SEED_START);
 
     private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
-            new HashMap<>();
+            new WeakHashMap<>();
     private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
-
-            new HashMap<>();
+            new WeakHashMap<>();
 
     private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache = new HashMap<>();
     private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
@@ -355,6 +355,8 @@
         // dispose of the default typeface.
         Typeface_Delegate.resetDefaults();
         Typeface.sDynamicTypefaceCache.evictAll();
+        sProject9PatchCache.clear();
+        sProjectBitmapCache.clear();
 
         return true;
     }
@@ -397,7 +399,7 @@
             // get the real cause of the exception.
             Throwable t2 = t;
             while (t2.getCause() != null) {
-                t2 = t.getCause();
+                t2 = t2.getCause();
             }
             return new BridgeRenderSession(null,
                     ERROR_UNKNOWN.createResult(t2.getMessage(), t));
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 55636eb..b33344c 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -22,6 +22,8 @@
 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.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;
@@ -40,7 +42,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.app.SystemServiceRegistry_Accessor;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -86,7 +87,6 @@
 import android.view.BridgeInflater;
 import android.view.Display;
 import android.view.DisplayAdjustments;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -101,6 +101,7 @@
 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;
@@ -119,6 +120,9 @@
     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));
@@ -610,45 +614,35 @@
 
     @Override
     public Object getSystemService(String service) {
-        if (LAYOUT_INFLATER_SERVICE.equals(service)) {
-            return mBridgeInflater;
+        switch (service) {
+            case LAYOUT_INFLATER_SERVICE:
+                return mBridgeInflater;
+
+            case TEXT_SERVICES_MANAGER_SERVICE:
+                // we need to return a valid service to avoid NPE
+                return TextServicesManager.getInstance();
+
+            case WINDOW_SERVICE:
+                return mWindowManager;
+
+            case POWER_SERVICE:
+                return new PowerManager(this, new BridgePowerManager(), new Handler());
+
+            case DISPLAY_SERVICE:
+                return mDisplayManager;
+
+            case ACCESSIBILITY_SERVICE:
+                return AccessibilityManager.getInstance(this);
+
+            case INPUT_METHOD_SERVICE:  // needed by SearchView
+            case AUTOFILL_MANAGER_SERVICE:
+            case AUDIO_SERVICE:
+            case TEXT_CLASSIFICATION_SERVICE:
+                return null;
+            default:
+                assert false : "Unsupported Service: " + service;
         }
 
-        if (TEXT_SERVICES_MANAGER_SERVICE.equals(service)) {
-            // we need to return a valid service to avoid NPE
-            return TextServicesManager.getInstance();
-        }
-
-        if (WINDOW_SERVICE.equals(service)) {
-            return mWindowManager;
-        }
-
-        // needed by SearchView
-        if (INPUT_METHOD_SERVICE.equals(service)) {
-            return null;
-        }
-
-        if (POWER_SERVICE.equals(service)) {
-            return new PowerManager(this, new BridgePowerManager(), new Handler());
-        }
-
-        if (DISPLAY_SERVICE.equals(service)) {
-            return mDisplayManager;
-        }
-
-        if (ACCESSIBILITY_SERVICE.equals(service)) {
-            return AccessibilityManager.getInstance(this);
-        }
-
-        if (AUTOFILL_MANAGER_SERVICE.equals(service)) {
-            return null;
-        }
-
-        if (AUDIO_SERVICE.equals(service)) {
-            return null;
-        }
-
-        assert false : "Unsupported Service: " + service;
         return null;
     }
 
@@ -657,13 +651,13 @@
         return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass);
     }
 
-    @Override
-    public final BridgeTypedArray obtainStyledAttributes(int[] attrs) {
-        return obtainStyledAttributes(0, attrs);
-    }
 
-    @Override
-    public final BridgeTypedArray obtainStyledAttributes(int resId, int[] attrs)
+    /**
+     * Same as Context#obtainStyledAttributes. We do not override the base method to give the
+     * original Context the chance to override the theme when needed.
+     */
+    @Nullable
+    public final BridgeTypedArray internalObtainStyledAttributes(int resId, int[] attrs)
             throws Resources.NotFoundException {
         StyleResourceValue style = null;
         // get the StyleResourceValue based on the resId;
@@ -715,18 +709,24 @@
         return typeArrayAndPropertiesPair.getFirst();
     }
 
-    @Override
-    public final BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) {
-        return obtainStyledAttributes(set, attrs, 0, 0);
-    }
-
-    @Override
-    public BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
+    /**
+     * Same as Context#obtainStyledAttributes. We do not override the base method to give the
+     * original Context the chance to override the theme when needed.
+     */
+    @Nullable
+    public BridgeTypedArray internalObtainStyledAttributes(@Nullable AttributeSet set, int[] attrs,
             int defStyleAttr, int defStyleRes) {
 
         PropertiesMap defaultPropMap = null;
         boolean isPlatformFile = true;
 
+        // 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;
+
         // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
         if (set instanceof BridgeXmlBlockParser) {
             BridgeXmlBlockParser parser;
@@ -739,6 +739,8 @@
                 defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap());
             }
 
+            resolver = parser::getNamespace;
+            currentFileNamespace = ResourceNamespace.fromBoolean(parser.isPlatformFile());
         } else if (set instanceof BridgeLayoutParamsMapAttributes) {
             // this is only for temp layout params generated dynamically, so this is never
             // platform content.
@@ -807,15 +809,11 @@
                         }
                         defaultPropMap.put("style", new Property(defStyleName, item.getValue()));
                     }
-                } else {
-                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
-                            String.format(
-                                    "Failed to find style '%s' in current theme",
-                                    defStyleAttribute.getFirst()),
-                            null);
                 }
             }
-        } else if (defStyleRes != 0) {
+        }
+
+        if (defStyleValues == null && defStyleRes != 0) {
             StyleResourceValue item = getStyleByDynamicId(defStyleRes);
             if (item != null) {
                 defStyleValues = item;
@@ -968,10 +966,19 @@
                     ta.bridgeSetValue(index, attrName, frameworkAttr, attributeHolder.resourceId,
                             defaultValue);
                 } else {
-                    // there is a value in the XML, but we need to resolve it in case it's
+                    // There is a value in the XML, but we need to resolve it in case it's
                     // referencing another resource or a theme value.
-                    ta.bridgeSetValue(index, attrName, frameworkAttr, attributeHolder.resourceId,
-                            mRenderResources.resolveValue(null, attrName, value, isPlatformFile));
+                    ResourceValue dummy =
+                            new ResourceValue(
+                                    currentFileNamespace,
+                                    null,
+                                    attrName,
+                                    value);
+                    dummy.setNamespaceLookup(resolver);
+
+                    ta.bridgeSetValue(
+                            index, attrName, frameworkAttr, attributeHolder.resourceId,
+                            mRenderResources.resolveResValue(dummy));
                 }
             }
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
deleted file mode 100644
index 4805ed1..0000000
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.android;
-
-import com.android.internal.inputmethod.IInputContentUriToken;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
-import com.android.internal.view.InputBindResult;
-
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.text.style.SuggestionSpan;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-
-import java.util.List;
-
-/**
- * Basic implementation of IInputMethodManager that does nothing.
- *
- */
-public class BridgeIInputMethodManager implements IInputMethodManager {
-
-    @Override
-    public void addClient(IInputMethodClient arg0, IInputContext arg1, int arg2, int arg3)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void finishInput(IInputMethodClient arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public InputMethodSubtype getCurrentInputMethodSubtype() throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public List<InputMethodInfo> getEnabledInputMethodList() throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String arg0,
-            boolean arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public List<InputMethodInfo> getInputMethodList() throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public InputMethodSubtype getLastInputMethodSubtype() throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public List getShortcutInputMethodsAndSubtypes() throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void hideMySoftInput(IBinder arg0, int arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public boolean hideSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean notifySuggestionPicked(SuggestionSpan arg0, String arg1, int arg2)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void registerSuggestionSpansForNotification(SuggestionSpan[] arg0)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void removeClient(IInputMethodClient arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void setAdditionalInputMethodSubtypes(String arg0, InputMethodSubtype[] arg1)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public boolean setCurrentInputMethodSubtype(InputMethodSubtype arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void setImeWindowStatus(IBinder arg0, IBinder arg1, int arg2, int arg3)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setInputMethod(IBinder arg0, String arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void setInputMethodAndSubtype(IBinder arg0, String arg1, InputMethodSubtype arg2)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public boolean setInputMethodEnabled(String arg0, boolean arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient arg0, String arg1)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void showInputMethodPickerFromClient(IInputMethodClient arg0,
-            int arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void showMySoftInput(IBinder arg0, int arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public boolean showSoftInput(IInputMethodClient arg0, int arg1, ResultReceiver arg2)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean switchToLastInputMethod(IBinder arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-    public boolean shouldOfferSwitchingToNextInputMethod(IBinder arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-        return false;
-    }
-
-    @Override
-     public int getInputMethodWindowVisibleHeight() throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    @Override
-    public void notifyUserAction(int sequenceNumber) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException {
-        // TODO Auto-generated method stub
-
-    }
-
-    @Override
-    public void clearLastInputMethodWindowForTransition(IBinder arg0) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public InputBindResult startInputOrWindowGainedFocus(
-            /* @InputMethodClient.StartInputReason */ int startInputReason,
-            IInputMethodClient client, IBinder windowToken, int controlFlags,
-            /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
-            int windowFlags, EditorInfo attribute, IInputContext inputContext,
-            /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public IBinder asBinder() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public IInputContentUriToken createInputContentUriToken(IBinder token, Uri contentUri,
-            String packageName) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void reportFullscreenMode(IBinder token, boolean fullscreen) {
-        // TODO Auto-generated method stub
-    }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
index f5912e7..3ddf93a 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeLayoutParamsMapAttributes.java
@@ -56,6 +56,11 @@
     }
 
     @Override
+    public String getAttributeNamespace(int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public String getAttributeName(int index) {
         throw new UnsupportedOperationException();
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 98937ef..73a9a62 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -26,12 +25,11 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ChangedPackages;
-import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
@@ -52,10 +50,11 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
+
 import java.util.List;
 
 /**
@@ -112,6 +111,11 @@
     }
 
     @Override
+    public Intent getCarLaunchIntentForPackage(String packageName) {
+        return null;
+    }
+
+    @Override
     public int[] getPackageGids(String packageName) throws NameNotFoundException {
         return new int[0];
     }
@@ -430,6 +434,11 @@
     }
 
     @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) {
+        return null;
+    }
+
+    @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
         return null;
     }
@@ -611,16 +620,6 @@
     }
 
     @Override
-    public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
-            String installerPackageName) {
-    }
-
-    @Override
-    public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags,
-            String installerPackageName) {
-    }
-
-    @Override
     public int installExistingPackage(String packageName) throws NameNotFoundException {
         return 0;
     }
@@ -835,8 +834,8 @@
     }
 
     @Override
-    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
-            int userId) {
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) {
         return new String[]{};
     }
 
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index ed428ec..b87ca3b 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -117,16 +117,6 @@
     }
 
     @Override
-    public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void setTemporaryScreenBrightnessSettingOverride(int arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
     public void setStayOnSetting(int arg0) throws RemoteException {
         // pass for now.
     }
@@ -176,4 +166,9 @@
     public int getLastShutdownReason() {
         return PowerManager.SHUTDOWN_REASON_UNKNOWN;
     }
+
+    @Override
+    public void setDozeAfterScreenOff(boolean mode) throws RemoteException {
+        // pass for now.
+    }
 }
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
deleted file mode 100644
index ffbe7c4..0000000
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.internal.os.IResultReceiver;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.util.MergedConfiguration;
-import android.view.DragEvent;
-import android.view.IWindow;
-
-/**
- * Implementation of {@link IWindow} to pass to the AttachInfo.
- */
-public final class BridgeWindow implements IWindow {
-
-    @Override
-    public void dispatchAppVisibility(boolean arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void dispatchGetNewSurface() throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
-            throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6,
-            boolean b, MergedConfiguration mergedConfig, Rect rect7, boolean b2, boolean b3, int i0)
-            throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void moved(int arg0, int arg1) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-            boolean sync) {
-        // pass for now.
-    }
-
-    @Override
-    public void dispatchWallpaperCommand(String action, int x, int y,
-            int z, Bundle extras, boolean sync) {
-        // pass for now.
-    }
-
-    @Override
-    public void closeSystemDialogs(String reason) {
-        // pass for now.
-    }
-
-    @Override
-    public void dispatchDragEvent(DragEvent event) {
-        // pass for now.
-    }
-
-    @Override
-    public void updatePointerIcon(float x, float y) {
-        // pass for now
-    }
-
-    @Override
-    public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
-            int localValue, int localChanges) {
-        // pass for now.
-    }
-
-    @Override
-    public void dispatchWindowShown() {
-    }
-
-    @Override
-    public void requestAppKeyboardShortcuts(
-            IResultReceiver receiver, int deviceId) throws RemoteException {
-    }
-
-    @Override
-    public void dispatchPointerCaptureChanged(boolean hasCapture) {
-    }
-
-    @Override
-    public IBinder asBinder() {
-        // pass for now.
-        return null;
-    }
-
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
deleted file mode 100644
index 2c88394..0000000
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 android.content.ClipData;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.MergedConfiguration;
-import android.view.IWindow;
-import android.view.IWindowId;
-import android.view.IWindowSession;
-import android.view.InputChannel;
-import android.view.Surface;
-import android.view.SurfaceView;
-import android.view.WindowManager.LayoutParams;
-
-/**
- * Implementation of {@link IWindowSession} so that mSession is not null in
- * the {@link SurfaceView}.
- */
-public final class BridgeWindowSession implements IWindowSession {
-
-    @Override
-    public int add(IWindow arg0, int seq, LayoutParams arg1, int arg2, Rect arg3, Rect arg4,
-            InputChannel outInputchannel)
-            throws RemoteException {
-        // pass for now.
-        return 0;
-    }
-
-    @Override
-    public int addToDisplay(IWindow arg0, int seq, LayoutParams arg1, int arg2, int displayId,
-                            Rect arg3, Rect arg4, Rect arg5, InputChannel outInputchannel)
-            throws RemoteException {
-        // pass for now.
-        return 0;
-    }
-
-    @Override
-    public int addWithoutInputChannel(IWindow arg0, int seq, LayoutParams arg1, int arg2,
-                                      Rect arg3, Rect arg4)
-            throws RemoteException {
-        // pass for now.
-        return 0;
-    }
-
-    @Override
-    public int addToDisplayWithoutInputChannel(IWindow arg0, int seq, LayoutParams arg1, int arg2,
-                                               int displayId, Rect arg3, Rect arg4)
-            throws RemoteException {
-        // pass for now.
-        return 0;
-    }
-
-    @Override
-    public void finishDrawing(IWindow arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public boolean getInTouchMode() throws RemoteException {
-        // pass for now.
-        return false;
-    }
-
-    @Override
-    public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
-        // pass for now.
-        return false;
-    }
-
-    @Override
-    public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
-            int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
-            Rect rect6, Rect rect7, MergedConfiguration mergedConfig, Surface surface)
-            throws RemoteException {
-        // pass for now.
-        return 0;
-    }
-
-    @Override
-    public boolean outOfMemory(IWindow window) throws RemoteException {
-        return false;
-    }
-
-    @Override
-    public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
-        // pass for now.
-    }
-
-    @Override
-    public void remove(IWindow arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void setInTouchMode(boolean arg0) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
-        // pass for now.
-    }
-
-    @Override
-    public void setInsets(IWindow window, int touchable, Rect contentInsets,
-            Rect visibleInsets, Region touchableRegion) {
-        // pass for now.
-    }
-
-    @Override
-    public IBinder prepareDrag(IWindow window, int flags,
-            int thumbnailWidth, int thumbnailHeight, Surface outSurface)
-            throws RemoteException {
-        // pass for now
-        return null;
-    }
-
-    @Override
-    public boolean performDrag(IWindow window, IBinder dragToken,
-            int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
-            ClipData data)
-            throws RemoteException {
-        // pass for now
-        return false;
-    }
-
-    @Override
-    public boolean startMovingTask(IWindow window, float startX, float startY)
-            throws RemoteException {
-        // pass for now
-        return false;
-    }
-
-    @Override
-    public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
-        // pass for now
-    }
-
-    @Override
-    public void cancelDragAndDrop(IBinder dragToken) throws RemoteException {
-        // pass for now
-    }
-
-    @Override
-    public void dragRecipientEntered(IWindow window) throws RemoteException {
-        // pass for now
-    }
-
-    @Override
-    public void dragRecipientExited(IWindow window) throws RemoteException {
-        // pass for now
-    }
-
-    @Override
-    public void setWallpaperPosition(IBinder window, float x, float y,
-        float xStep, float yStep) {
-        // pass for now.
-    }
-
-    @Override
-    public void wallpaperOffsetsComplete(IBinder window) {
-        // pass for now.
-    }
-
-    @Override
-    public void setWallpaperDisplayOffset(IBinder windowToken, int x, int y) {
-        // pass for now.
-    }
-
-    @Override
-    public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
-            int z, Bundle extras, boolean sync) {
-        // pass for now.
-        return null;
-    }
-
-    @Override
-    public void wallpaperCommandComplete(IBinder window, Bundle result) {
-        // pass for now.
-    }
-
-    @Override
-    public IBinder asBinder() {
-        // pass for now.
-        return null;
-    }
-
-    @Override
-    public void onRectangleOnScreenRequested(IBinder window, Rect rectangle) {
-        // pass for now.
-    }
-
-    @Override
-    public IWindowId getWindowId(IBinder window) throws RemoteException {
-        // pass for now.
-        return null;
-    }
-
-    @Override
-    public void pokeDrawLock(IBinder window) {
-        // pass for now.
-    }
-
-    @Override
-    public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
-        // pass for now.
-    }
-
-    @Override
-    public void updatePointerIcon(IWindow window) {
-        // pass for now.
-    }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index f18cd48..d50117e 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -159,7 +159,7 @@
 
     @Override
     public String getNamespace(String prefix) {
-        throw new RuntimeException("getNamespace() not supported");
+        return mParser.getNamespace(prefix);
     }
 
     @Override
diff --git a/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java b/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java
index fbb12d5..cd971ee 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/NopAttributeSet.java
@@ -28,6 +28,11 @@
     }
 
     @Override
+    public String getAttributeNamespace(int index) {
+        return null;
+    }
+
+    @Override
     public String getAttributeName(int index) {
         return null;
     }
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 aa873a6..aedcc48 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
@@ -31,14 +31,22 @@
  * Utility class for working with the design support lib.
  */
 public class DesignLibUtil {
-
-    private static final String PKG_PREFIX = "android.support.design.widget.";
-    public static final String CN_COORDINATOR_LAYOUT = PKG_PREFIX + "CoordinatorLayout";
-    public static final String CN_APPBAR_LAYOUT = PKG_PREFIX + "AppBarLayout";
-    public static final String CN_COLLAPSING_TOOLBAR_LAYOUT =
-            PKG_PREFIX + "CollapsingToolbarLayout";
-    public static final String CN_TOOLBAR = "android.support.v7.widget.Toolbar";
-    public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
+    public static final String[] CN_COORDINATOR_LAYOUT = {
+            "android.support.design.widget.CoordinatorLayout",
+            "androidx.widget.CoordinatorLayout"
+    };
+    public static final String[] CN_APPBAR_LAYOUT = {
+            "android.support.design.widget.AppBarLayout",
+            "androidx.design.widget.AppBarLayout"
+    };
+    public static final String[] CN_COLLAPSING_TOOLBAR_LAYOUT = {
+            "android.support.design.widget.CollapsingToolbarLayout",
+            "androidx.design.widget.CollapsingToolbarLayout"
+    };
+    public static final String[] CN_TOOLBAR = {
+            "android.support.v7.widget.Toolbar",
+            "androidx.widget.Toolbar"
+    };
 
     /**
      * Tries to set the title of a view. This is used to set the title in a
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 40d3811..4b9f674 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
@@ -33,7 +33,10 @@
 
 public class DrawerLayoutUtil {
 
-    public static final String CN_DRAWER_LAYOUT = "android.support.v4.widget.DrawerLayout";
+    public static final String[] CN_DRAWER_LAYOUT = {
+            "android.support.v4.widget.DrawerLayout",
+            "androidx.widget.DrawerLayout"
+    };
 
     public static void openDrawer(View drawerLayout, @Nullable String drawerGravity) {
         int gravity = -1;
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 730ac13..138d914 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
@@ -32,18 +32,43 @@
  */
 public class FragmentTabHostUtil {
 
-    public static final String CN_FRAGMENT_TAB_HOST = "android.support.v4.app.FragmentTabHost";
+    public static final String[] CN_FRAGMENT_TAB_HOST = {
+            "android.support.v4.app.FragmentTabHost",
+            "androidx.app.FragmentTabHost"
+    };
+
+    private static final String[] CN_FRAGMENT_MANAGER = {
+            "android.support.v4.app.FragmentManager",
+            "androidx.app.FragmentManager"
+    };
 
     /**
      * Calls the setup method for the FragmentTabHost tabHost
      */
     public static void setup(TabHost tabHost, Context context) {
+        Class<?> fragmentManager = null;
+
+        for (int i = CN_FRAGMENT_MANAGER.length - 1; i >= 0; i--) {
+            String className = CN_FRAGMENT_MANAGER[i];
+            try {
+                fragmentManager = Class.forName(className, true,
+                        tabHost.getClass().getClassLoader());
+                break;
+            } catch (ClassNotFoundException ignore) {
+            }
+        }
+
+        if (fragmentManager == null) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Unable to find FragmentManager.", null);
+            return;
+        }
+
         try {
             invoke(getMethod(tabHost.getClass(), "setup", Context.class,
-                    Class.forName("android.support.v4.app.FragmentManager", true,
-                            tabHost.getClass().getClassLoader()), int.class), tabHost, context, null,
+                    fragmentManager, int.class), tabHost, context, null,
                     android.R.id.tabcontent);
-        } catch (ReflectionException | ClassNotFoundException e) {
+        } catch (ReflectionException e) {
             Throwable cause = getCause(e);
             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
                     "Error occurred while trying to setup FragmentTabHost.", cause, null);
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 c6e034f..bf9a7e5 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -18,6 +18,7 @@
 
 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;
@@ -34,17 +35,15 @@
 import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
 
 /**
- * Utility class for working with android.support.v7.widget.RecyclerView
+ * Utility class for working with android.support.v7.widget.RecyclerView and
+ * androidx.widget.RecyclerView
  */
 public class RecyclerViewUtil {
+    public static final String[] CN_RECYCLER_VIEW = {
+            "android.support.v7.widget.RecyclerView",
+            "androidx.widget.RecyclerView"
+    };
 
-    private static final String RV_PKG_PREFIX = "android.support.v7.widget.";
-    public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView";
-    private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager";
-    private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter";
-
-    // LinearLayoutManager related constants.
-    private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager";
     private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class};
 
     /**
@@ -56,11 +55,15 @@
      */
     public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
             @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) {
+        String recyclerViewClassName = recyclerView.getClass().getName();
+        String adapterClassName = recyclerViewClassName + "$Adapter";
+        String layoutMgrClassName = recyclerViewClassName + "$LayoutManager";
+
         try {
-            setLayoutManager(recyclerView, context, layoutlibCallback);
-            Object adapter = createAdapter(layoutlibCallback);
+            setLayoutManager(recyclerView, layoutMgrClassName, context, layoutlibCallback);
+            Object adapter = createAdapter(layoutlibCallback, adapterClassName);
             if (adapter != null) {
-                setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter");
+                setProperty(recyclerView, adapterClassName, adapter, "setAdapter");
                 setProperty(adapter, int.class, adapterLayout, "setLayoutId");
 
                 if (itemCount != -1) {
@@ -74,13 +77,17 @@
         }
     }
 
-    private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
+    private static void setLayoutManager(@NonNull View recyclerView,
+            @NonNull String layoutMgrClassName, @NonNull BridgeContext context,
             @NonNull LayoutlibCallback callback) throws ReflectionException {
         if (getLayoutManager(recyclerView) == null) {
+            String linearLayoutMgrClassManager =
+                    recyclerView.getClass().getPackage().getName() + ".LinearLayoutManager";
             // Only set the layout manager if not already set by the recycler view.
-            Object layoutManager = createLayoutManager(context, callback);
+            Object layoutManager =
+                    createLayoutManager(context, linearLayoutMgrClassManager, callback);
             if (layoutManager != null) {
-                setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
+                setProperty(recyclerView, layoutMgrClassName, layoutManager, "setLayoutManager");
             }
         }
     }
@@ -88,10 +95,10 @@
     /** Creates a LinearLayoutManager using the provided context. */
     @Nullable
     private static Object createLayoutManager(@NonNull Context context,
-            @NonNull LayoutlibCallback callback)
+            @NonNull String linearLayoutMgrClassName, @NonNull LayoutlibCallback callback)
             throws ReflectionException {
         try {
-            return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE,
+            return callback.loadView(linearLayoutMgrClassName, LLM_CONSTRUCTOR_SIGNATURE,
                     new Object[]{context});
         } catch (Exception e) {
             throw new ReflectionException(e);
@@ -104,22 +111,22 @@
     }
 
     @Nullable
-    private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback)
-            throws ReflectionException {
+    private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback,
+            @NonNull String layoutMgrClassName) throws ReflectionException {
         Boolean ideSupport =
                 layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
         if (ideSupport != Boolean.TRUE) {
             return null;
         }
         try {
-            return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]);
+            return layoutlibCallback.loadClass(layoutMgrClassName, new Class[0], new Object[0]);
         } catch (Exception e) {
             throw new ReflectionException(e);
         }
     }
 
     private static void setProperty(@NonNull Object object, @NonNull String propertyClassName,
-      @NonNull Object propertyValue, @NonNull String propertySetter)
+            @NonNull Object propertyValue, @NonNull String propertySetter)
             throws ReflectionException {
         Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName);
         setProperty(object, propertyClass, propertyValue, propertySetter);
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 6ad9efc..fda4693 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -52,12 +52,10 @@
  * the API being stable.
  */
 public class SupportPreferencesUtil {
-    private static final String PREFERENCE_PKG = "android.support.v7.preference";
-    private static final String PREFERENCE_MANAGER = PREFERENCE_PKG + ".PreferenceManager";
-    private static final String PREFERENCE_GROUP = PREFERENCE_PKG + ".PreferenceGroup";
-    private static final String PREFERENCE_GROUP_ADAPTER =
-      PREFERENCE_PKG + ".PreferenceGroupAdapter";
-    private static final String PREFERENCE_INFLATER = PREFERENCE_PKG + ".PreferenceInflater";
+    private static final String[] PREFERENCES_PKG_NAMES = {
+            "android.support.v7.preference",
+            "androidx.preference"
+    };
 
     private SupportPreferencesUtil() {
     }
@@ -79,21 +77,27 @@
 
     @NonNull
     private static Object createPreferenceGroupAdapter(@NonNull LayoutlibCallback callback,
-            @NonNull Object preferenceScreen) throws ReflectionException {
-        Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+            @NonNull String preferenceGroupClassName,
+            @NonNull String preferenceGroupAdapterClassName, @NonNull Object preferenceScreen)
+            throws ReflectionException {
+        Class<?> preferenceGroupClass =
+                getClassInstance(preferenceScreen, preferenceGroupClassName);
 
-        return instantiateClass(callback, PREFERENCE_GROUP_ADAPTER,
+        return instantiateClass(callback, preferenceGroupAdapterClassName,
                 new Class[]{preferenceGroupClass}, new Object[]{preferenceScreen});
     }
 
     @NonNull
     private static Object createInflatedPreference(@NonNull LayoutlibCallback callback,
-      @NonNull Context context, @NonNull XmlPullParser parser, @NonNull Object preferenceScreen,
-      @NonNull Object preferenceManager) throws ReflectionException {
-        Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
-        Object preferenceInflater = instantiateClass(callback, PREFERENCE_INFLATER,
-          new Class[]{Context.class, preferenceManager.getClass()},
-          new Object[]{context, preferenceManager});
+            @NonNull String preferenceGroupClassName, @NonNull String preferenceInflaterClassName,
+            @NonNull Context context, @NonNull XmlPullParser parser,
+            @NonNull Object preferenceScreen, @NonNull Object preferenceManager)
+            throws ReflectionException {
+        Class<?> preferenceGroupClass =
+                getClassInstance(preferenceScreen, preferenceGroupClassName);
+        Object preferenceInflater = instantiateClass(callback, preferenceInflaterClassName,
+                new Class[]{Context.class, preferenceManager.getClass()},
+                new Object[]{context, preferenceManager});
         Object inflatedPreference =
                 invoke(getAccessibleMethod(preferenceInflater.getClass(), "inflate",
                         XmlPullParser.class, preferenceGroupClass), preferenceInflater, parser,
@@ -169,8 +173,7 @@
 
             // Get the type of the preference layout and bind it to a newly created view holder
             Integer type = (Integer) invoke(getItemViewType, preferenceGroupAdapter, i);
-            Object viewHolder =
-                    invoke(onCreateViewHolder, preferenceGroupAdapter, listView, type);
+            Object viewHolder = invoke(onCreateViewHolder, preferenceGroupAdapter, listView, type);
             if (viewHolder == null) {
                 continue;
             }
@@ -179,8 +182,7 @@
 
             try {
                 // Get the view from the view holder and add it to our layout
-                View itemView =
-                        (View) viewHolder.getClass().getField("itemView").get(viewHolder);
+                View itemView = (View) viewHolder.getClass().getField("itemView").get(viewHolder);
 
                 int arrayPosition = id.intValue() - 1; // IDs are 1 based
                 if (arrayPosition >= 0 && arrayPosition < viewCookie.size()) {
@@ -201,6 +203,24 @@
     @Nullable
     public static View inflatePreference(@NonNull BridgeContext bridgeContext,
             @NonNull XmlPullParser parser, @Nullable ViewGroup root) {
+        String preferencePackageName = null;
+        String preferenceManagerClassName = null;
+        // Find the correct package for the classes
+        for (int i = PREFERENCES_PKG_NAMES.length - 1; i >= 0; i--) {
+            preferencePackageName = PREFERENCES_PKG_NAMES[i];
+            preferenceManagerClassName = preferencePackageName + ".PreferenceManager";
+            try {
+                bridgeContext.getLayoutlibCallback().findClass(preferenceManagerClassName);
+                break;
+            } catch (ClassNotFoundException ignore) {
+            }
+        }
+
+        assert preferencePackageName != null;
+        String preferenceGroupClassName = preferencePackageName + ".PreferenceGroup";
+        String preferenceGroupAdapterClassName = preferencePackageName + ".PreferenceGroupAdapter";
+        String preferenceInflaterClassName = preferencePackageName + ".PreferenceInflater";
+
         try {
             LayoutlibCallback callback = bridgeContext.getLayoutlibCallback();
 
@@ -211,9 +231,8 @@
             }
 
             // Create PreferenceManager
-            Object preferenceManager =
-                    instantiateClass(callback, PREFERENCE_MANAGER, new Class[]{Context.class},
-                            new Object[]{context});
+            Object preferenceManager = instantiateClass(callback, preferenceManagerClassName,
+                    new Class[]{Context.class}, new Object[]{context});
 
             // From this moment on, we can assume that we found the support library and that
             // nothing should fail
@@ -251,13 +270,14 @@
             }
 
             // Create the PreferenceInflater
-            Object inflatedPreference =
-              createInflatedPreference(callback, context, parser, preferenceScreen,
-                preferenceManager);
+            Object inflatedPreference = createInflatedPreference(callback, preferenceGroupClassName,
+                    preferenceInflaterClassName, context, parser, preferenceScreen,
+                    preferenceManager);
 
             // Setup the RecyclerView (set adapter and layout manager)
             Object preferenceGroupAdapter =
-                    createPreferenceGroupAdapter(callback, inflatedPreference);
+                    createPreferenceGroupAdapter(callback, preferenceGroupClassName,
+                            preferenceGroupAdapterClassName, inflatedPreference);
 
             // Instead of just setting the group adapter as adapter for a RecyclerView, we manually
             // get all the items and add them to a LinearLayout. This allows us to set the view
@@ -266,8 +286,8 @@
                     preferenceGroupAdapter);
 
             ScrollView scrollView = new ScrollView(context);
-            scrollView.setLayoutParams(
-              new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+            scrollView.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,
+                    LayoutParams.WRAP_CONTENT));
             scrollView.addView(listView);
 
             if (root != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index cdcf0ea..919d57c 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -26,6 +26,7 @@
 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;
@@ -33,11 +34,18 @@
 import android.graphics.drawable.Drawable;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.View;
 import android.widget.FrameLayout;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 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;
 
 
 /**
@@ -47,9 +55,12 @@
 public class AppCompatActionBar extends BridgeActionBar {
 
     private Object mWindowDecorActionBar;
-    private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
-    // This is used on v23.1.1 and later.
-    private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar";
+    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
+    };
+
     private Class<?> mWindowActionBarClass;
 
     /**
@@ -77,19 +88,23 @@
             Object[] constructorArgs = {getDecorContent()};
             LayoutlibCallback callback = params.getLayoutlibCallback();
 
-            // Check if the old action bar class is present.
-            String actionBarClass = WINDOW_ACTION_BAR_CLASS;
-            try {
-                callback.findClass(actionBarClass);
-            } catch (ClassNotFoundException expected) {
-                // Failed to find the old class, use the newer one.
-                actionBarClass = WINDOW_ACTION_BAR_CLASS_NEW;
+            // Find the correct WindowActionBar class
+            String actionBarClass = null;
+            for  (int i = WINDOW_ACTION_BAR_CLASS_NAMES.length - 1; i >= 0; i--) {
+                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();
+            inflateMenus();
             setupActionBar();
         } catch (Exception e) {
             Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
@@ -165,6 +180,51 @@
         }
     }
 
+    private void inflateMenus() {
+        List<String> menuNames = getCallBack().getMenuIdNames();
+        if (menuNames.isEmpty()) {
+            return;
+        }
+
+        if (menuNames.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,
+                    "Support Toolbar does not currently support multiple menus in the preview.",
+                    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);
+        }
+        if (id < 1) {
+            return;
+        }
+        // Get toolbar decorator
+        Object mDecorToolbar = getFieldValue(mWindowDecorActionBar, "mDecorToolbar");
+        if (mDecorToolbar == null) {
+            return;
+        }
+
+        Class<?> mDecorToolbarClass = mDecorToolbar.getClass();
+        Context themedContext = (Context)invoke(
+                getMethod(mWindowActionBarClass, "getThemedContext"),
+                mWindowDecorActionBar);
+        MenuInflater inflater = new MenuInflater(themedContext);
+        Menu menuBuilder = (Menu)invoke(getMethod(mDecorToolbarClass, "getMenu"), mDecorToolbar);
+        inflater.inflate(id, menuBuilder);
+
+        // Set the actual menu
+        invoke(findMethod(mDecorToolbarClass, "setMenu"), mDecorToolbar, menuBuilder, null);
+    }
+
     @Override
     public void createMenuPopup() {
         // it's hard to add menus to appcompat's actionbar, since it'll use a lot of reflection.
@@ -181,13 +241,53 @@
         return null;
     }
 
+    /**
+     * Same as getMethod but doesn't require the parameterTypes. This allows us to call methods
+     * without having to get all the types for the parameters when we do not need them
+     */
     @Nullable
-    private static Object invoke(Method method, Object owner, Object... args) {
+    private static Method findMethod(@Nullable Class<?> owner, @NotNull String name) {
+        if (owner == null) {
+            return null;
+        }
+        for (Method method : owner.getMethods()) {
+            if (name.equals(method.getName())) {
+                return method;
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private static Object getFieldValue(@Nullable Object instance, @NotNull String name) {
+        if (instance == null) {
+            return null;
+        }
+
+        Class<?> instanceClass = instance.getClass();
+        try {
+            Field field = instanceClass.getDeclaredField(name);
+            boolean accesible = field.isAccessible();
+            if (!accesible) {
+                field.setAccessible(true);
+            }
+            try {
+                return field.get(instance);
+            } finally {
+                field.setAccessible(accesible);
+            }
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Nullable
+    private static Object invoke(@Nullable Method method, Object owner, Object... args) {
         try {
             return method == null ? null : method.invoke(owner, args);
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();
-        } catch (IllegalAccessException e) {
+        } catch (InvocationTargetException | IllegalAccessException e) {
             e.printStackTrace();
         }
         return null;
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 7f8d992..569d7b6 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -74,8 +74,8 @@
     }
 
     public static String getTime(int platformVersion) {
-        if (isGreaterOrEqual(platformVersion, N)) {
-            return "7:00";
+        if (isGreaterOrEqual(platformVersion, O)) {
+            return "8:00";
         }
         if (platformVersion < GINGERBREAD) {
             return "2:20";
@@ -101,6 +101,9 @@
         if (platformVersion < N) {
             return "6:00";
         }
+        if (platformVersion < O) {
+            return "7:00";
+        }
         // Should never happen.
         return "4:04";
     }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 2e31980..7168b54 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -724,7 +724,7 @@
         DesignLibUtil.setTitle(collapsingToolbar, title);
     }
 
-    private View findChildView(View view, String className) {
+    private View findChildView(View view, String[] className) {
         if (!(view instanceof ViewGroup)) {
             return null;
         }
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index c3b9aed..16f92f3 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -49,6 +49,7 @@
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.Typeface_Accessor;
+import android.graphics.Typeface_Delegate;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -159,31 +160,9 @@
         } catch (NumberFormatException ignored) {
         }
 
-        XmlPullParser parser = null;
-        // first check if the value is a file (xml most likely)
-        Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
-                RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
-        if (psiParserSupport != null && psiParserSupport) {
-            parser = context.getLayoutlibCallback().getXmlFileParser(value);
-        }
-        if (parser == null) {
-            File f = new File(value);
-            if (f.isFile()) {
-                // let the framework inflate the color from the XML file, by
-                // providing an XmlPullParser
-                try {
-                    parser = ParserFactory.create(f);
-                } catch (XmlPullParserException | FileNotFoundException e) {
-                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                            "Failed to parse file " + value, e, null /*data*/);
-                }
-            }
-        }
-
-        if (parser != null) {
-            try {
-                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
-                        parser, context, resValue.isFramework());
+        try {
+            BridgeXmlBlockParser blockParser = getXmlBlockParser(context, resValue);
+            if (blockParser != null) {
                 try {
                     // Advance the parser to the first element so we can detect if it's a
                     // color list or a gradient color
@@ -214,18 +193,18 @@
                 } finally {
                     blockParser.ensurePopped();
                 }
-            } catch (XmlPullParserException e) {
-                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        "Failed to configure parser for " + value, e, null /*data*/);
-                // we'll return null below.
-            } catch (Exception e) {
-                // this is an error and not warning since the file existence is
-                // checked before attempting to parse it.
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
-                        "Failed to parse file " + value, e, null /*data*/);
-
-                return null;
             }
+        } catch (XmlPullParserException e) {
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Failed to configure parser for " + value, e, null /*data*/);
+            // we'll return null below.
+        } catch (Exception e) {
+            // this is an error and not warning since the file existence is
+            // checked before attempting to parse it.
+            Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+                    "Failed to parse file " + value, e, null /*data*/);
+
+            return null;
         }
 
         return null;
@@ -409,59 +388,8 @@
             return null;
         }
 
-        // Check if this is an asset that we've already loaded dynamically
-        Typeface typeface = Typeface.findFromCache(context.getAssets(), fontName);
-        if (typeface != null) {
-            return typeface;
-        }
 
-        String lowerCaseValue = fontName.toLowerCase();
-        if (lowerCaseValue.endsWith(".xml")) {
-            // create a block parser for the file
-            Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
-                    RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
-            XmlPullParser parser = null;
-            if (psiParserSupport != null && psiParserSupport) {
-                parser = context.getLayoutlibCallback().getXmlFileParser(fontName);
-            }
-            else {
-                File f = new File(fontName);
-                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 " + fontName,
-                                e, null /*data*/);
-                    }
-                }
-            }
-
-            if (parser != null) {
-                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
-                        parser, context, isFramework);
-                try {
-                    FontResourcesParser.FamilyResourceEntry entry =
-                            FontResourcesParser.parse(blockParser, context.getResources());
-                    typeface = Typeface.createFromResources(entry, context.getAssets(),
-                            fontName);
-                } catch (XmlPullParserException | IOException e) {
-                    Bridge.getLog().error(null, "Failed to parse file " + fontName,
-                            e, null /*data*/);
-                } finally {
-                    blockParser.ensurePopped();
-                }
-            } else {
-                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                        String.format("File %s does not exist (or is not a file)", fontName),
-                        null /*data*/);
-            }
-        } else {
-            typeface = Typeface.createFromResources(context.getAssets(), fontName, 0);
-        }
-
-        return typeface;
+        return Typeface_Delegate.createFromDisk(context, fontName, isFramework);
     }
 
     /**
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
index b89718f..64d100a 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -19,8 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 
 /**
  * Utility to convert checked Reflection exceptions to unchecked exceptions.
@@ -74,6 +76,24 @@
         return false;
     }
 
+    /**
+     * Check if the object is an instance of any of the class named in {@code className}. This
+     * doesn't work for interfaces.
+     */
+    public static boolean isInstanceOf(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;
+                }
+            }
+            superClass = superClass.getSuperclass();
+        }
+        return false;
+    }
+
     @NonNull
     public static Throwable getCause(@NonNull Throwable throwable) {
         Throwable cause = throwable.getCause();
@@ -99,6 +119,34 @@
         throw new RuntimeException("invalid object/classname combination.");
     }
 
+    public static <T> T createProxy(Class<T> interfaze) {
+        ClassLoader loader = interfaze.getClassLoader();
+        return (T) Proxy.newProxyInstance(loader, new Class[]{interfaze}, new InvocationHandler() {
+            public Object invoke(Object proxy, Method m, Object[] args) {
+                final Class<?> returnType = m.getReturnType();
+                if (returnType == boolean.class) {
+                    return false;
+                } else if (returnType == int.class) {
+                    return 0;
+                } else if (returnType == long.class) {
+                    return 0L;
+                } else if (returnType == short.class) {
+                    return 0;
+                } else if (returnType == char.class) {
+                    return 0;
+                } else if (returnType == byte.class) {
+                    return 0;
+                } else if (returnType == float.class) {
+                    return 0f;
+                } else if (returnType == double.class) {
+                    return 0.0;
+                } else {
+                    return null;
+                }
+            }
+        });
+    }
+
     /**
      * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
      * introduced in 1.7 and we are still on 1.6
diff --git a/bridge/tests/Android.mk b/bridge/tests/Android.mk
index 1b65eee..e51d75d 100644
--- a/bridge/tests/Android.mk
+++ b/bridge/tests/Android.mk
@@ -32,7 +32,8 @@
 			sdk-common \
 			junit-host \
 			guavalib \
-			mockito-host
+			mockito-host \
+			objenesis-host
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
diff --git a/bridge/tests/res/testApp/MyApplication/build.gradle b/bridge/tests/res/testApp/MyApplication/build.gradle
index 4781660..c9058b5 100644
--- a/bridge/tests/res/testApp/MyApplication/build.gradle
+++ b/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -3,7 +3,7 @@
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.2.3'
+        classpath 'com.android.tools.build:gradle:3.0.0-beta5'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -19,12 +19,12 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 25
-    buildToolsVersion '25.0.0'
+    compileSdkVersion 26
+    buildToolsVersion '26.0.2'
     defaultConfig {
         applicationId 'com.android.layoutlib.test.myapplication'
         minSdkVersion 21
-        targetSdkVersion 25
+        targetSdkVersion 26
         versionCode 1
         versionName '1.0'
     }
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
new file mode 100644
index 0000000..a734a21
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/R.class
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/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class
index f73528a..ea2e30d 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/classes/debug/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/MyActivity.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
index 5bb04fc..be984f0 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/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
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
index b87f193..d279a3e 100644
--- 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
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/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
index e2968d4..2b98809 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/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
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
index ff699d1..a73fcca 100644
--- 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
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/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
index a3931b8..6f6bd54 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/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
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
index e293677..93c5977 100644
--- 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
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/classes/debug/com/android/layoutlib/test/myapplication/R$font.class
new file mode 100644
index 0000000..a3e9926
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$font.class
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
index d6268bf..e580ef5 100644
--- 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
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/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
index 08b98fb..4f1a852 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/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class
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
index f9be1ca..9bdc44f 100644
--- 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
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
index 6874b49..11e0686 100644
--- 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
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/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
index a4205a8..331ea4a 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/classes/debug/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/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
index 4fb3b61..3b47ac9 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/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
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
index dba67fd..e91c774 100644
--- 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
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/classes/debug/com/android/layoutlib/test/myapplication/ThemableWidget.class
new file mode 100644
index 0000000..8d96ac1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ThemableWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/asset.png b/bridge/tests/res/testApp/MyApplication/golden/asset.png
new file mode 100644
index 0000000..f69b128
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
new file mode 100644
index 0000000..27bc3d8
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
new file mode 100644
index 0000000..ca51e21
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
new file mode 100644
index 0000000..780f8e5
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/remote_component_load.png b/bridge/tests/res/testApp/MyApplication/golden/remote_component_load.png
new file mode 100644
index 0000000..2920b7d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/remote_component_load.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/remote_component_load_fail.png b/bridge/tests/res/testApp/MyApplication/golden/remote_component_load_fail.png
new file mode 100644
index 0000000..0ed85d1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/remote_component_load_fail.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
new file mode 100644
index 0000000..62fb869
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.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 4f3ed60..8212d04 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/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
index 7bbae09..eddb5e6 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_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
new file mode 100644
index 0000000..8bb1677
--- /dev/null
+++ 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_multi_line_of_path_data.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png
index 5fc6052..ebc8a11 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.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
new file mode 100644
index 0000000..2a3f3dd
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.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
new file mode 100644
index 0000000..bf358fa
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.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 3b51ffe..565238f 100644
--- a/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
+++ b/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/assets/asset.png b/bridge/tests/res/testApp/MyApplication/src/main/assets/asset.png
new file mode 100644
index 0000000..0327e13
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/assets/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ThemableWidget.java b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ThemableWidget.java
new file mode 100644
index 0000000..10189b7
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ThemableWidget.java
@@ -0,0 +1,40 @@
+package com.android.layoutlib.test.myapplication;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.widget.FrameLayout;
+
+public class ThemableWidget extends FrameLayout {
+    public ThemableWidget(Context context) {
+        super(context);
+
+        init();
+    }
+
+    public ThemableWidget(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        init();
+    }
+
+    public ThemableWidget(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        init();
+    }
+
+    public ThemableWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        init();
+    }
+
+    private void init() {
+        ContextThemeWrapper context = new ContextThemeWrapper(getContext(), getContext().getTheme());
+        context.setTheme(R.style.ThemableWidgetStyle);
+
+        LayoutInflater.from(context).inflate(R.layout.themable_widget_layout, this);
+    }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/AssetView.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/AssetView.java
new file mode 100644
index 0000000..9e43d8f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/AssetView.java
@@ -0,0 +1,35 @@
+package com.android.layoutlib.test.myapplication.widgets;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AssetView extends View {
+    public AssetView(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public AssetView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public AssetView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init(context);
+    }
+
+    private void init(Context context) {
+        try {
+            InputStream istr = context.getAssets().open("asset.png");
+            setBackground(Drawable.createFromStream(istr, null));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/color/complex_gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/color/complex_gradient.xml
new file mode 100644
index 0000000..1578192
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/color/complex_gradient.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gradient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:endX="200"
+    android:endY="200"
+    android:startX="100"
+    android:startY="100"
+    android:type="linear">
+    <item
+        android:color="#123"
+        android:offset="0.0" />
+    <item
+        android:color="#AAA"
+        android:offset="0.5" />
+    <item
+        android:color="#00F"
+        android:offset="1.0" />
+</gradient>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/color/complex_radial_gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/color/complex_radial_gradient.xml
new file mode 100644
index 0000000..ce6a3de
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/color/complex_radial_gradient.xml
@@ -0,0 +1,30 @@
+<?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.
+  -->
+
+<gradient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:centerX="50"
+    android:centerY="50"
+    android:gradientRadius="20"
+    android:type="radial">
+    <item
+        android:color="#123"
+        android:offset="0.0" />
+    <item
+        android:color="#00F"
+        android:offset="1.0" />
+</gradient>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.xml
index fc0afa6..371d4fe 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.xml
@@ -16,9 +16,9 @@
 
 
 <gradient xmlns:android="http://schemas.android.com/apk/res/android"
-          android:startX="10"
-          android:startY="10"
-          android:endX="50"
-          android:endY="50"
+          android:startX="-20"
+          android:startY="-20"
+          android:endX="-10"
+          android:endY="-10"
           android:startColor="#ffff0000"
           android:endColor="#ff00ff00" />
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml
index 8f862c8..cc9c449 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@android:color/red" />
+    <background android:drawable="@color/red" />
     <foreground android:drawable="@drawable/headset" />
 </adaptive-icon>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml
index c5b0f01..3afb7b6 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_line_of_path_data.xml
@@ -1,13 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="16dp"
-        android:viewportHeight="16"
+        android:viewportHeight="20"
         android:viewportWidth="16"
         android:width="16dp">
 
     <group
         android:translateX="-2.000000"
-        android:translateY="-14.000000">
+        android:translateY="-12.000000">
         <group
             android:translateX="2.000000"
             android:translateY="14.000000">
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
index 0998b25..6f4a350 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
@@ -1,22 +1,60 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:height="76dp"
-        android:width="76dp"
-        android:viewportHeight="48"
-        android:viewportWidth="48"
+        android:height="50dp"
+        android:width="50dp"
+        android:viewportHeight="100"
+        android:viewportWidth="100"
         android:alpha="0.6">
 
     <group
+        android:name="clipBackground">
+        <!--
+        Any content outside this box, should not be there as it will be outside the viewport
+        -->
+        <path
+            android:pathData="M0,0h100v100H0z"
+            android:fillColor="#301BB0D3"/>
+
+        <!--
+        So this box shouldn't be visible
+        -->
+        <path
+            android:pathData="M0,-10h10v10H0z"
+            android:strokeWidth="1"
+            android:strokeColor="#000"
+            android:fillColor="#F00"/>
+
+        <!--
+        This one should be partially visible
+        -->
+        <path
+            android:pathData="M80,-10h10v20h-10z"
+            android:strokeWidth="1"
+            android:strokeColor="#000"
+            android:fillColor="#FF0"/>
+
+    </group>
+
+    <group
         android:name="root"
-        android:translateX="24.0"
-        android:translateY="24.0">
+        android:translateX="50.0"
+        android:translateY="50.0"
+        android:scaleX="1.2"
+        android:scaleY="1.2">
+
+        <path
+            android:strokeWidth="2"
+            android:strokeColor="#FFFF00"
+            android:pathData="M-10,-10 m20,0 m0,20 m-20,0 m0,-20z"
+            android:fillColor="#FEBEBE"
+        />
+
         <!--
             This is the same as the material indeterminate progressbar which involves drawing
             several cubic segments
         -->
         <path
-            android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -30 a 10,10 0 1,1 0,38 a 10,10 0 1,1 0,-38"
             android:strokeColor="#00FF00"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
@@ -25,13 +63,13 @@
             android:trimPathStart="0.3" />
         <!-- Same figure with reversed end and start -->
         <path
-            android:pathData="M0, 0 m 0, -12 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38"
+            android:pathData="M0, 0 m 0, -12 a 10,10 0 1,1 0,38 a 10,10 0 1,1 0,-38"
             android:strokeColor="#FFFF00"
             android:strokeLineCap="square"
             android:strokeLineJoin="miter"
             android:strokeWidth="1"
             android:trimPathEnd="0.3"
-            android:trimPathStart="0.8" />
+            android:trimPathStart="0.5" />
 
         <!--
             Draw a few partial quadratic segments
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/radial_gradient.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/radial_gradient.xml
new file mode 100644
index 0000000..3ab9d72
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/radial_gradient.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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 xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportHeight="100"
+        android:viewportWidth="100">
+    <path
+        android:pathData="m100,0 0,100 -100,0 0,-100z"
+        android:strokeColor="#F00000"
+        android:strokeWidth="1"
+        android:fillColor="@color/complex_radial_gradient">
+    </path>
+</vector>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/shadow.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/shadow.xml
new file mode 100644
index 0000000..67824fd
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/shadow.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="300dp"
+        android:height="300dp"
+        android:viewportHeight="200"
+        android:viewportWidth="200">
+    <path
+        android:pathData="m200,0 0,200 -200,0 0,-200z"
+        android:strokeColor="#F00000"
+        android:strokeWidth="1"
+        android:fillColor="@color/complex_gradient">
+    </path>
+</vector>
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 59dbbec..c083a6f 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
@@ -94,4 +94,50 @@
             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: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:alpha="0.5"/>
+
+    </RelativeLayout>
+
+    <!-- Test translation -->
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:translationX="5dp"
+            android:translationY="20dp"
+            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" />
+
+    </RelativeLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/themable_widget_layout.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/themable_widget_layout.xml
new file mode 100644
index 0000000..507bba6
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/themable_widget_layout.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:background="#00F"
+    android:layout_width="50dp"/>
+<!-- Intentionally omitting height since it will be added via themeing -->
\ No newline at end of file
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 5f58d39..1059d2e 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
@@ -17,7 +17,7 @@
         <!-- theme ref in android NS. value = @string/candidates_style = <u>candidates</u> -->
         <item>?android:attr/candidatesTextStyleSpans</item>
         <item>@android:string/unknownName</item>  <!-- value = Unknown -->
-        <item>?EC</item>
+        <item>\?EC</item>
     </string-array>
 
     <!-- resources that the above array can refer to -->
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/colors.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/colors.xml
new file mode 100644
index 0000000..4754690
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="red">#F00</color>
+</resources>
\ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index debe33b..f598fda 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -5,4 +5,8 @@
         <item name="myattr">@integer/ten</item>
     </style>
 
+    <style name="ThemableWidgetStyle">
+        <item name="android:layout_height">150dp</item>
+    </style>
+
 </resources>
diff --git a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java b/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
index d20fb14..aa51773 100644
--- a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
+++ b/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.util.Arrays;
+
 import junit.framework.TestCase;
 
 /**
@@ -28,7 +30,7 @@
 
         assertTrue(m1.isIdentity());
 
-        m1.setValues(new float[] { 1,0,0, 0,1,0, 0,0,1 });
+        m1.setValues(new float[]{1, 0, 0, 0, 1, 0, 0, 0, 1});
         assertTrue(m1.isIdentity());
     }
 
@@ -41,8 +43,22 @@
         m1.getValues(v1);
         m2.getValues(v2);
 
-        for (int i = 0 ; i < 9; i++) {
+        for (int i = 0; i < 9; i++) {
             assertEquals(v1[i], v2[i]);
         }
     }
+
+    public void testInvert() {
+        Matrix m1 = new Matrix();
+        Matrix inverse = new Matrix();
+        m1.setValues(new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
+        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}));
+    }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
new file mode 100644
index 0000000..96146dc
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.SessionParams;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.layoutlib.bridge.impl.RenderActionTestUtil;
+import com.android.layoutlib.bridge.intensive.RenderTestBase;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
+import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
+import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+
+import org.junit.Test;
+
+import android.R.attr;
+import android.R.style;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.util.DisplayMetrics;
+import android.view.ContextThemeWrapper;
+
+import static org.junit.Assert.assertTrue;
+
+public class BridgeContextTest extends RenderTestBase {
+    @Test
+    public void basic() throws ClassNotFoundException {
+        // Setup
+        // Create the layout pull parser for our resources (empty.xml can not be part of the test
+        // app as it won't compile).
+        LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material", false)
+                .build();
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+        BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                params.getAssets(), params.getLayoutlibCallback(), configuration,
+                params.getTargetSdkVersion(), params.isRtlSupported());
+
+        context.initResources();
+        BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
+        try {
+            Context themeContext = new ContextThemeWrapper(context, style.Theme_Material);
+            // First we try to get the style from the ?attr/editTextStyle fallback value.
+            // We pass an invalid value to defStyleRes
+            TypedArray array = themeContext.obtainStyledAttributes(null,
+                            new int[]{attr.clickable}, attr.editTextStyle, Integer.MAX_VALUE);
+            assertTrue(array.getBoolean(0, false));
+            // Now, we try to get it directly from the Widget.EditText. We pass an invalid value
+            // to defStyleAttr so it fails and falls back to the defStyleRes
+            array = themeContext.obtainStyledAttributes(null,
+                    new int[]{attr.clickable}, Integer.MAX_VALUE,
+                    style.Widget_EditText);
+            assertTrue(array.getBoolean(0, false));
+
+        } finally {
+            RenderActionTestUtil.setBridgeContext(oldContext);
+            context.disposeResources();
+        }
+
+        // This message is expected when asking for an invalid defStyleAttr
+        sRenderMessages.removeIf(msg ->
+                msg.startsWith("Failed to find the style corresponding to the id"));
+    }
+
+    @Test
+    public void checkNoErrorWhenUsingDefaults() throws ClassNotFoundException {
+        // Setup
+        // Create the layout pull parser for our resources (empty.xml can not be part of the test
+        // app as it won't compile).
+        LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material", false)
+                .build();
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+        BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                params.getAssets(), params.getLayoutlibCallback(), configuration,
+                params.getTargetSdkVersion(), params.isRtlSupported());
+
+        context.initResources();
+        BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
+        try {
+            Context themeContext = new ContextThemeWrapper(context, style.Theme_Material);
+            // First we try to get the style from the ?attr/editTextStyle fallback value.
+            // We pass an invalid value to defStyleRes
+            themeContext.obtainStyledAttributes(null,
+                    new int[]{attr.clickable}, 0, style.Widget_EditText);
+            themeContext.obtainStyledAttributes(null,
+                    new int[]{attr.clickable}, attr.editTextStyle, 0);
+        } finally {
+            RenderActionTestUtil.setBridgeContext(oldContext);
+            context.disposeResources();
+        }
+
+
+    }
+}
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 e98c789..1360334 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -18,6 +18,7 @@
 
 import com.android.layoutlib.bridge.BridgeRenderSessionTest;
 import com.android.layoutlib.bridge.TestDelegates;
+import com.android.layoutlib.bridge.android.BridgeContextTest;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest;
 import com.android.layoutlib.bridge.impl.LayoutParserWrapperTest;
 import com.android.layoutlib.bridge.impl.ResourceHelperTest;
@@ -36,8 +37,8 @@
 @SuiteClasses({
         RenderTests.class, LayoutParserWrapperTest.class,
         BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
-        Matrix_DelegateTest.class, TestDelegates.class, PerformanceTests.class,
-        BridgeRenderSessionTest.class, ResourceHelperTest.class
+        Matrix_DelegateTest.class, TestDelegates.class,
+        BridgeRenderSessionTest.class, ResourceHelperTest.class, BridgeContextTest.class
 })
 public class Main {
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java
index 230e116..4d7438f 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/PerformanceTests.java
@@ -43,7 +43,7 @@
     private void render(@NonNull String layoutFileName)
             throws ClassNotFoundException, FileNotFoundException {
         SessionParams params = createSessionParams(layoutFileName, ConfigGenerator.NEXUS_5);
-        render(params, 250);
+        render(sBridge, params, 250);
     }
 
     @Test
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java
index 087478f..989d146 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderResult.java
@@ -28,7 +28,7 @@
 import java.util.Collections;
 import java.util.List;
 
-class RenderResult {
+public class RenderResult {
     private final List<ViewInfo> mRootViews;
     private final List<ViewInfo> mSystemViews;
     private final Result mRenderResult;
@@ -51,7 +51,7 @@
     }
 
     @Nullable
-    Result getResult() {
+    public Result getResult() {
         return mRenderResult;
     }
 
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 1fc7015..56d1d65 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -21,11 +21,9 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
-import com.android.ide.common.resources.FrameworkResources;
-import com.android.ide.common.resources.ResourceItem;
-import com.android.ide.common.resources.ResourceRepository;
-import com.android.ide.common.resources.ResourceResolver;
-import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.deprecated.FrameworkResources;
+import com.android.ide.common.resources.deprecated.ResourceItem;
+import com.android.ide.common.resources.deprecated.ResourceRepository;
 import com.android.io.FolderWrapper;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
@@ -35,6 +33,7 @@
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
 import com.android.layoutlib.bridge.intensive.util.ImageUtils;
 import com.android.layoutlib.bridge.intensive.util.ModuleClassLoader;
+import com.android.layoutlib.bridge.intensive.util.SessionParamsBuilder;
 import com.android.layoutlib.bridge.intensive.util.TestAssetRepository;
 import com.android.layoutlib.bridge.intensive.util.TestUtils;
 import com.android.tools.layoutlib.java.System_Delegate;
@@ -50,6 +49,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -59,6 +59,7 @@
 import java.util.concurrent.TimeUnit;
 
 import com.google.android.collect.Lists;
+import com.google.common.collect.ImmutableMap;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -91,12 +92,14 @@
     private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
     private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
 
-    private static final String PLATFORM_DIR;
+    protected static final String PLATFORM_DIR;
     private static final String TEST_RES_DIR;
     /** Location of the app to test inside {@link #TEST_RES_DIR} */
-    private static final String APP_TEST_DIR = "testApp/MyApplication";
+    protected static final String APP_TEST_DIR = "testApp/MyApplication";
     /** Location of the app's res dir inside {@link #TEST_RES_DIR} */
     private static final String APP_TEST_RES = APP_TEST_DIR + "/src/main/res";
+    /** 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/";
     protected static Bridge sBridge;
@@ -133,9 +136,8 @@
             }
         }
     };
-    // Default class loader with access to the app classes
-    protected ClassLoader mDefaultClassLoader =
-            new ModuleClassLoader(APP_CLASSES_LOCATION, getClass().getClassLoader());
+
+    protected ClassLoader mDefaultClassLoader;
 
     private static String getPlatformDir() {
         String platformDir = System.getProperty(PLATFORM_DIR_PROPERTY);
@@ -163,6 +165,18 @@
         if (currentDir.getName().equalsIgnoreCase("bridge")) {
             currentDir = currentDir.getParentFile();
         }
+
+        // Find frameworks/layoutlib
+        while (currentDir != null && !"layoutlib".equals(currentDir.getName())) {
+            currentDir = currentDir.getParentFile();
+        }
+
+        if (currentDir == null ||
+                currentDir.getParentFile() == null ||
+                !"frameworks".equals(currentDir.getParentFile().getName())) {
+            return null;
+        }
+
         // Test if currentDir is  platform/frameworks/layoutlib. That is, root should be
         // workingDir/../../ (2 levels up)
         for (int i = 0; i < 2; i++) {
@@ -186,7 +200,8 @@
             return null;
         }
         File[] hosts = host.listFiles(path -> path.isDirectory() &&
-                (path.getName().startsWith("linux-") || path.getName().startsWith("darwin-")));
+                (path.getName().startsWith("linux-") ||
+                        path.getName().startsWith("darwin-")));
         assert hosts != null;
         for (File hostOut : hosts) {
             String platformDir = getPlatformDirFromHostOut(hostOut);
@@ -194,6 +209,7 @@
                 return platformDir;
             }
         }
+
         return null;
     }
 
@@ -321,12 +337,14 @@
     }
 
     @NonNull
-    protected static RenderResult render(SessionParams params, long frameTimeNanos) {
+    protected static RenderResult render(com.android.ide.common.rendering.api.Bridge bridge,
+            SessionParams params,
+            long frameTimeNanos) {
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
         System_Delegate.setBootTimeNanos(TimeUnit.MILLISECONDS.toNanos(871732800000L));
         System_Delegate.setNanosTime(TimeUnit.MILLISECONDS.toNanos(871732800000L));
-        RenderSession session = sBridge.createSession(params);
+        RenderSession session = bridge.createSession(params);
 
         try {
             if (frameTimeNanos != -1) {
@@ -353,6 +371,18 @@
     }
 
     /**
+     * Compares the golden image with the passed image
+     */
+    protected static void verify(@NonNull String goldenImageName, @NonNull BufferedImage image) {
+        try {
+            String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenImageName;
+            ImageUtils.requireSimilar(goldenImagePath, image);
+        } catch (IOException e) {
+            getLogger().error(e, e.getMessage());
+        }
+    }
+
+    /**
      * Create a new rendering session and test that rendering the given layout doesn't throw any
      * exceptions and matches the provided image.
      * <p>
@@ -362,14 +392,9 @@
     @Nullable
     protected static RenderResult renderAndVerify(SessionParams params, String goldenFileName,
             long frameTimeNanos) throws ClassNotFoundException {
-        RenderResult result = RenderTestBase.render(params, frameTimeNanos);
-        try {
-            String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenFileName;
-            assertNotNull(result.getImage());
-            ImageUtils.requireSimilar(goldenImagePath, result.getImage());
-        } catch (IOException e) {
-            getLogger().error(e, e.getMessage());
-        }
+        RenderResult result = RenderTestBase.render(sBridge, params, frameTimeNanos);
+        assertNotNull(result.getImage());
+        verify(goldenFileName, result.getImage());
 
         return result;
     }
@@ -384,7 +409,7 @@
         return RenderTestBase.renderAndVerify(params, goldenFileName, -1);
     }
 
-    private static LayoutLog getLayoutLog() {
+    protected static LayoutLog getLayoutLog() {
         if (sLayoutLibLog == null) {
             sLayoutLibLog = new LayoutLog() {
                 @Override
@@ -394,9 +419,8 @@
                 }
 
                 @Override
-                public void fidelityWarning(@Nullable String tag, String message,
-                        Throwable throwable, Object data) {
-
+                public void fidelityWarning(String tag, String message, Throwable throwable,
+                        Object viewCookie, Object data) {
                     System.out.println("FidelityWarning " + tag + ": " + message);
                     if (throwable != null) {
                         throwable.printStackTrace();
@@ -480,6 +504,8 @@
 
     @Before
     public void beforeTestCase() {
+        // Default class loader with access to the app classes
+        mDefaultClassLoader = new ModuleClassLoader(APP_CLASSES_LOCATION, getClass().getClassLoader());
         sRenderMessages.clear();
     }
 
@@ -520,28 +546,28 @@
         layoutLibCallback.initResources();
         // TODO: Set up action bar handler properly to test menu rendering.
         // Create session params.
-        return getSessionParams(parser, deviceConfig, layoutLibCallback, "AppTheme", true,
-                RenderingMode.NORMAL, 22);
+        return getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(deviceConfig)
+                .setCallback(layoutLibCallback)
+                .build();
     }
 
     /**
-     * Uses Theme.Material and Target sdk version as 22.
+     * Returns a pre-configured {@link SessionParamsBuilder} for target API 22, Normal rendering
+     * mode, AppTheme as theme and Nexus 5.
      */
-    protected SessionParams getSessionParams(LayoutPullParser layoutParser,
-            ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback,
-            String themeName, boolean isProjectTheme, RenderingMode renderingMode,
-            @SuppressWarnings("SameParameterValue") int targetSdk) {
-        FolderConfiguration config = configGenerator.getFolderConfig();
-        ResourceResolver resourceResolver =
-                ResourceResolver.create(sProjectResources.getConfiguredResources(config),
-                        sFrameworkRepo.getConfiguredResources(config), themeName, isProjectTheme);
-
-        SessionParams sessionParams =
-                new SessionParams(layoutParser, renderingMode, null /*used for caching*/,
-                        configGenerator.getHardwareConfig(), resourceResolver, layoutLibCallback, 0,
-                        targetSdk, getLayoutLog());
-        sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true);
-        sessionParams.setAssetRepository(new TestAssetRepository());
-        return sessionParams;
+    @NonNull
+    protected SessionParamsBuilder getSessionParamsBuilder() {
+        return new SessionParamsBuilder()
+                .setLayoutLog(getLayoutLog())
+                .setFrameworkResources(sFrameworkRepo)
+                .setConfigGenerator(ConfigGenerator.NEXUS_5)
+                .setProjectResources(sProjectResources)
+                .setTheme("AppTheme", true)
+                .setRenderingMode(RenderingMode.NORMAL)
+                .setTargetSdk(22)
+                .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 b09184b..3b4851f 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.intensive;
 
 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.SessionParams;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
@@ -36,11 +37,13 @@
 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.content.res.AssetManager;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -50,8 +53,10 @@
 import android.util.DisplayMetrics;
 import android.util.StateSet;
 import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
 
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -64,7 +69,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -72,6 +76,11 @@
  */
 public class RenderTests extends RenderTestBase {
 
+    @After
+    public void afterTestCase() {
+        com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
+    }
+
     @Test
     public void testActivity() throws ClassNotFoundException, FileNotFoundException {
         renderAndVerify("activity.xml", "activity.png");
@@ -99,9 +108,11 @@
                 "        android:background=\"#FF0000\"\n" +
                 "        android:id=\"@+id/text1\"/>\n" +
                 "</RelativeLayout>");
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.NoTitleBar", false,
-                RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.NoTitleBar", false)
+                .build();
 
         renderAndVerify(params, "simple_activity-old-theme.png");
     }
@@ -113,21 +124,28 @@
         layoutLibCallback.initResources();
 
         LayoutPullParser parser = createParserFromPath("four_corners.xml");
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar.TranslucentDecor", false,
-                RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.TranslucentDecor", false)
+                .build();
         renderAndVerify(params, "four_corners_translucent.png");
 
         parser = createParserFromPath("four_corners.xml");
-        params = getSessionParams(parser, ConfigGenerator.NEXUS_5_LAND,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar.TranslucentDecor", false,
-                RenderingMode.NORMAL, 22);
+        params = getSessionParamsBuilder()
+                .setConfigGenerator(ConfigGenerator.NEXUS_5_LAND)
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.TranslucentDecor", false)
+                .build();
         renderAndVerify(params, "four_corners_translucent_land.png");
 
         parser = createParserFromPath("four_corners.xml");
-        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
-                RenderingMode.NORMAL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(parser)
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.Light.NoActionBar", false)
+                    .build();
         renderAndVerify(params, "four_corners.png");
     }
 
@@ -175,25 +193,34 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "simple_activity_noactionbar.png");
 
         parser = LayoutPullParser.createFromString(simpleActivity);
-        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.Light", false,
-                RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(parser)
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.Light", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
 
         renderAndVerify(params, "simple_activity.png");
 
         // This also tests that a theme with "NoActionBar" DOES HAVE an action bar when we are
         // displaying menus.
         parser = LayoutPullParser.createFromString(simpleActivity);
-        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
-                RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(parser)
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.Light.NoActionBar", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
         params.setFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG, "menu");
         renderAndVerify(params, "simple_activity.png");
     }
@@ -222,11 +249,13 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar", false,
-                RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar", false)
+                .build();
 
-        render(params, -1);
+        render(sBridge, params, -1);
 
         assertTrue((Boolean)field.get(null));
         field.set(null, false);
@@ -248,9 +277,13 @@
                 .setDensity(Density.XHIGH)
                 .setNavigation(Navigation.NONAV);
 
-        SessionParams params = getSessionParams(parser, customConfigGenerator,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(customConfigGenerator)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "expand_vert_layout.png");
 
@@ -260,9 +293,13 @@
                 .setDensity(Density.XHIGH)
                 .setNavigation(Navigation.NONAV);
         parser = createParserFromPath("expand_horz_layout.xml");
-        params = getSessionParams(parser, customConfigGenerator,
-                layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
-                RenderingMode.H_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(parser)
+                    .setConfigGenerator(customConfigGenerator)
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+                    .setRenderingMode(RenderingMode.H_SCROLL)
+                    .build();
 
         renderAndVerify(params, "expand_horz_layout.png");
     }
@@ -287,16 +324,22 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
 
         parser = LayoutPullParser.createFromString(layout);
-        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(parser)
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
         renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3));
     }
 
@@ -323,9 +366,12 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "vector_drawable.png", TimeUnit.SECONDS.toNanos(2));
     }
@@ -356,9 +402,12 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "vector_drawable_91383.png", TimeUnit.SECONDS.toNanos(2));
     }
@@ -386,14 +435,91 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "vector_drawable_multi_line_of_path_data.png",
                 TimeUnit.SECONDS.toNanos(2));
     }
 
+    /**
+     * Tests that the gradients are correctly transformed using the viewport of the vector drawable.
+     * <p/>
+     * If a vector drawable is 50x50 and the gradient has startX=25 and startY=25, the gradient
+     * will start in the middle of the box.
+     * <p/>
+     * http://b/65495452
+     */
+    @Test
+    public void testVectorDrawableGradient() 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/shadow\" />\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)
+                .build();
+
+        renderAndVerify(params, "vector_drawable_gradient.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
+    /**
+     * Tests that the radial gradients are correctly transformed using the viewport of the vector
+     * drawable.
+     * <p/>
+     * http://b/66168608
+     */
+    @Test
+    public void testVectorDrawableRadialGradient() 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/radial_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)
+                .build();
+
+        renderAndVerify(params, "vector_drawable_radial_gradient.png",
+                TimeUnit.SECONDS.toNanos(2));
+    }
+
     /** Test activity.xml */
     @Test
     public void testScrollingAndMeasure() throws ClassNotFoundException, FileNotFoundException {
@@ -404,9 +530,12 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
         params.setForceNoDecor();
         params.setExtendedViewInfoMode(true);
 
@@ -434,9 +563,12 @@
         // Do a full render pass
         parser = createParserFromPath("scrolled.xml");
 
-        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(parser)
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
         params.setForceNoDecor();
         params.setExtendedViewInfoMode(true);
 
@@ -456,8 +588,11 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
-                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setConfigGenerator(ConfigGenerator.NEXUS_4)
+                .setCallback(layoutLibCallback)
+                .build();
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
@@ -495,8 +630,11 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
-                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setConfigGenerator(ConfigGenerator.NEXUS_4)
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .build();
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
@@ -544,36 +682,45 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
-                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "adaptive_icon.png");
 
         layoutLibCallback.setAdaptiveIconMaskPath(
                 "M50 0C77.6 0 100 22.4 100 50C100 77.6 77.6 100 50 100C22.4 100 0 77.6 0 50C0 " +
                         "22.4 22.4 0 50 0Z");
-        params =
-                getSessionParams(LayoutPullParser.createFromString(layout), ConfigGenerator.NEXUS_5,
-                        layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                        RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(LayoutPullParser.createFromString(layout))
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
         renderAndVerify(params, "adaptive_icon_circle.png");
 
         layoutLibCallback.setAdaptiveIconMaskPath(
                 "M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58," +
                         " 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z");
-        params =
-                getSessionParams(LayoutPullParser.createFromString(layout), ConfigGenerator.NEXUS_5,
-                        layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                        RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(LayoutPullParser.createFromString(layout))
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
         renderAndVerify(params, "adaptive_icon_rounded_corners.png");
 
         layoutLibCallback.setAdaptiveIconMaskPath(
                 "M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z");
-        params =
-                getSessionParams(LayoutPullParser.createFromString(layout), ConfigGenerator.NEXUS_5,
-                        layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
-                        RenderingMode.V_SCROLL, 22);
+        params = getSessionParamsBuilder()
+                    .setParser(LayoutPullParser.createFromString(layout))
+                    .setCallback(layoutLibCallback)
+                    .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                    .setRenderingMode(RenderingMode.V_SCROLL)
+                    .build();
         renderAndVerify(params, "adaptive_icon_squircle.png");
     }
 
@@ -587,8 +734,11 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
-                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setConfigGenerator(ConfigGenerator.NEXUS_4)
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .build();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
 
@@ -633,8 +783,12 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
-                layoutLibCallback, "Theme.Material", false, RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setConfigGenerator(ConfigGenerator.NEXUS_4)
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material", false)
+                .build();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
 
@@ -647,8 +801,13 @@
 
         try {
             ColorStateList stateList = ResourceHelper.getColorStateList(
-                    new ResourceValue(ResourceUrl.create(null, ResourceType.COLOR, "test_list"),
-                            tmpColorList.getAbsolutePath()), mContext, null);
+                    new ResourceValue(
+                            ResourceNamespace.RES_AUTO,
+                            ResourceType.COLOR,
+                            "test_list",
+                            tmpColorList.getAbsolutePath()),
+                    mContext,
+                    null);
             assertNotNull(stateList);
             assertEquals(Color.parseColor("#ffffffff"), stateList.getColorForState(
                     StateSet.get(StateSet.VIEW_STATE_PRESSED),
@@ -664,8 +823,13 @@
             Resources.Theme theme = mContext.getResources().newTheme();
             theme.applyStyle(R.style.ThemeOverlay_Material_Light, true);
             stateList = ResourceHelper.getColorStateList(
-                    new ResourceValue(ResourceUrl.create(null, ResourceType.COLOR, "test_list"),
-                            tmpColorList.getAbsolutePath()), mContext, theme);
+                    new ResourceValue(
+                            ResourceNamespace.RES_AUTO,
+                            ResourceType.COLOR,
+                            "test_list",
+                            tmpColorList.getAbsolutePath()),
+                    mContext,
+                    theme);
             assertNotNull(stateList);
             assertEquals(Color.parseColor("#ff000000"), stateList.getColorForState(
                     StateSet.get(StateSet.VIEW_STATE_PRESSED),
@@ -697,8 +861,11 @@
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
-                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setConfigGenerator(ConfigGenerator.NEXUS_4)
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .build();
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
@@ -757,9 +924,225 @@
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
         layoutLibCallback.initResources();
 
-        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, layoutLibCallback,
-                "Theme.Material.NoActionBar.Fullscreen", false, RenderingMode.V_SCROLL, 22);
+        SessionParams params = getSessionParamsBuilder()
+                .setParser(parser)
+                .setCallback(layoutLibCallback)
+                .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+                .setRenderingMode(RenderingMode.V_SCROLL)
+                .build();
 
         renderAndVerify(params, "ninepatch_background.png");
     }
+
+    @Test
+    public void testAssetManager() throws Exception {
+        String layout =
+                "<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.AssetView\n" +
+                        "             android:layout_height=\"wrap_content\"\n" +
+                        "             android:layout_width=\"wrap_content\" />\n" +
+                        "</LinearLayout>\n";
+        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)
+                .build();
+
+        renderAndVerify(params, "asset.png");
+    }
+
+    /**
+     * Tests that calling setTheme in a ContextThemeWrapper actually applies the theme
+     *
+     * http://b/66902070
+     */
+    @Test
+    public void testContextThemeWrapper() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<com.android.layoutlib.test.myapplication.ThemableWidget " +
+                        "xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+                        "              android:layout_width=\"wrap_content\"\n" +
+                        "              android:layout_height=\"wrap_content\" />\n");
+        // 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)
+                .build();
+
+        renderAndVerify(params, "context_theme_wrapper.png", TimeUnit.SECONDS.toNanos(2));
+    }
+
+    /**
+     * Tests that a crashing view does not prevent others from working. This is meant to prevent
+     * crashes in framework views since custom views are already handled by Android Studio by
+     * rewriting the byte code.
+     */
+    @Test
+    public void testCrashes() throws ClassNotFoundException {
+        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" +
+                    "<com.android.layoutlib.bridge.test.widgets.HookWidget " +
+                    "              android:layout_width=\"100dp\"\n" +
+                    "              android:layout_height=\"200dp\" />\n" +
+                    "<LinearLayout " +
+                    "              android:background=\"#CBBAF0\"\n" +
+                    "              android:layout_width=\"100dp\"\n" +
+                    "              android:layout_height=\"200dp\" />\n" +
+                "</LinearLayout>";
+        {
+            com.android.layoutlib.bridge.test.widgets.HookWidget.setOnPreDrawHook(() -> {
+                throw new NullPointerException();
+            });
+
+            // 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)
+                    .build();
+
+            renderAndVerify(params, "ondraw_crash.png", TimeUnit.SECONDS.toNanos(2));
+        }
+
+        com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
+
+        {
+            com.android.layoutlib.bridge.test.widgets.HookWidget.setOnPreMeasure(() -> {
+                throw new NullPointerException();
+            });
+
+            LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+            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)
+                    .build();
+
+            renderAndVerify(params, "onmeasure_crash.png", TimeUnit.SECONDS.toNanos(2));
+        }
+
+        // We expect the view error messages. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("View draw failed"));
+        sRenderMessages.removeIf(message -> message.equals("View measure failed"));
+    }
+
+    /**
+     * Paints the borders of the given {@link ViewInfo} and its children to the passed
+     * {@link Graphics2D} context.
+     * The method will used the given parentLeft and parentTop as the given vInfo coordinates.
+     * The depth is used to calculate different colors for the borders depending on the hierarchy
+     * depth.
+     */
+    private void paintBorders(@NonNull Graphics2D g, int parentLeft, int parentTop, int depth,
+            @NonNull ViewInfo vInfo) {
+        int leftMargin = Math.max(0, vInfo.getLeftMargin());
+        int topMargin = Math.max(0, vInfo.getTopMargin());
+        int x = parentLeft + vInfo.getLeft() + leftMargin;
+        int y = parentTop + vInfo.getTop() + topMargin;
+        int w = vInfo.getRight() - vInfo.getLeft();
+        int h = vInfo.getBottom() - vInfo.getTop();
+        g.setXORMode(java.awt.Color.decode(Integer.toString(depth * 50000)));
+        g.drawRect(x, y, w, h);
+
+        for (ViewInfo child : vInfo.getChildren()) {
+            paintBorders(g, x, y, depth + 1, child);
+        }
+    }
+
+    @Test
+    public void testViewBoundariesReporting() throws Exception {
+        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:background=\"@drawable/ninepatch\"\n" +
+                        "              android:layout_margin=\"20dp\"\n" +
+                        "              android:orientation=\"vertical\">\n" + "\n" +
+                        "    <TextView\n" +
+                        "        android:layout_width=\"150dp\"\n" +
+                        "        android:layout_height=\"50dp\"\n" +
+                        "        android:background=\"#FF0\"/>\n" +
+                        "    <TextView\n" +
+                        "        android:layout_width=\"150dp\"\n" +
+                        "        android:layout_height=\"50dp\"\n" +
+                        "        android:background=\"#F00\"/>\n" +
+                        "    <LinearLayout\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:paddingLeft=\"10dp\">\n" +
+                        "        <TextView\n" +
+                        "            android:layout_width=\"150dp\"\n" +
+                        "            android:layout_height=\"50dp\"\n" +
+                        "            android:background=\"#00F\"/>\n" +
+                        "    </LinearLayout>\n" +
+                        "    <LinearLayout\n" +
+                        "        android:layout_width=\"wrap_content\"\n" +
+                        "        android:layout_height=\"wrap_content\"\n" +
+                        "        android:layout_marginLeft=\"30dp\"\n" +
+                        "        android:layout_marginTop=\"15dp\">\n" +
+                        "        <TextView\n" +
+                        "            android:layout_width=\"150dp\"\n" +
+                        "            android:layout_height=\"50dp\"\n" +
+                        "            android:background=\"#F0F\"/>\n" +
+                        "    </LinearLayout>\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)
+                .build();
+        params.setForceNoDecor();
+
+        RenderResult result = RenderTestBase.render(sBridge, params, -1);
+        BufferedImage image = result.getImage();
+        assertNotNull(image);
+        Graphics2D g = (Graphics2D) image.getGraphics();
+        g.setStroke(new BasicStroke(8));
+        for (ViewInfo vInfo : result.getSystemViews()) {
+            paintBorders(g, 0, 0, 0, vInfo);
+        }
+
+        RenderTestBase.verify("view_boundaries.png", image);
+    }
 }
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
index 34fc726..684cf12 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
@@ -163,8 +163,7 @@
     private Navigation mNavigation = Navigation.NONAV;
 
     public FolderConfiguration getFolderConfig() {
-        FolderConfiguration config = new FolderConfiguration();
-        config.createDefault();
+        FolderConfiguration config = FolderConfiguration.createDefault();
         config.setDensityQualifier(new DensityQualifier(mDensity));
         config.setNavigationMethodQualifier(new NavigationMethodQualifier(mNavigation));
         if (mScreenWidth > mScreenHeight) {
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 45facf5..4866051 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
@@ -86,8 +86,6 @@
 
     public static void assertImageSimilar(String relativePath, BufferedImage goldenImage,
             BufferedImage image, double maxPercentDifferent) throws IOException {
-        assertEquals("Only TYPE_INT_ARGB image types are supported",  TYPE_INT_ARGB, image.getType());
-
         if (goldenImage.getType() != TYPE_INT_ARGB) {
             BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
                     TYPE_INT_ARGB);
@@ -165,7 +163,6 @@
                     "vs" + image.getWidth() + "x" + image.getHeight();
         }
 
-        assertEquals(TYPE_INT_ARGB, image.getType());
         if (error != null) {
             // Expected on the left
             // Golden on the right
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
new file mode 100644
index 0000000..1c7857c
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
@@ -0,0 +1,175 @@
+/*
+ * 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.intensive.util;
+
+import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AssetRepository;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.ResourceNamespace;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.deprecated.ResourceRepository;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
+import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.resources.ResourceType;
+
+import android.annotation.NonNull;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Builder to help setting up {@link SessionParams} objects.
+ */
+public class SessionParamsBuilder {
+
+    private LayoutPullParser mLayoutParser;
+    private RenderingMode mRenderingMode = RenderingMode.NORMAL;
+    private Object mProjectKey = null;
+    private ConfigGenerator mConfigGenerator = ConfigGenerator.NEXUS_5;
+    private ResourceRepository mFrameworkResources;
+    private ResourceRepository mProjectResources;
+    private String mThemeName;
+    private boolean isProjectTheme;
+    private LayoutlibCallback mLayoutlibCallback;
+    private int mTargetSdk;
+    private int mMinSdk = 0;
+    private LayoutLog mLayoutLog;
+    private Map<SessionParams.Key, Object> mFlags = new HashMap<>();
+    private AssetRepository mAssetRepository = null;
+
+    @NonNull
+    public SessionParamsBuilder setParser(@NonNull LayoutPullParser layoutParser) {
+        mLayoutParser = layoutParser;
+
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setRenderingMode(@NonNull RenderingMode renderingMode) {
+        mRenderingMode = renderingMode;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setConfigGenerator(@NonNull ConfigGenerator configGenerator) {
+        mConfigGenerator = configGenerator;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setProjectResources(@NonNull ResourceRepository resources) {
+        mProjectResources = resources;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setFrameworkResources(@NonNull ResourceRepository resources) {
+        mFrameworkResources = resources;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setTheme(@NonNull String themeName, boolean isProjectTheme) {
+        mThemeName = themeName;
+        this.isProjectTheme = isProjectTheme;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setTheme(@NonNull String themeName) {
+        boolean isProjectTheme;
+        if (themeName.startsWith(SdkConstants.PREFIX_ANDROID)) {
+            themeName = themeName.substring(SdkConstants.PREFIX_ANDROID.length());
+            isProjectTheme = false;
+        } else {
+            isProjectTheme = true;
+        }
+        return setTheme(themeName, isProjectTheme);
+    }
+
+    @NonNull
+    public SessionParamsBuilder setCallback(@NonNull LayoutlibCallback callback) {
+        mLayoutlibCallback = callback;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setTargetSdk(int targetSdk) {
+        mTargetSdk = targetSdk;
+        return this;
+    }
+
+    @SuppressWarnings("unused")
+    @NonNull
+    public SessionParamsBuilder setMinSdk(int minSdk) {
+        mMinSdk = minSdk;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setLayoutLog(@NonNull LayoutLog layoutLog) {
+        mLayoutLog = layoutLog;
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setFlag(@NonNull SessionParams.Key flag, Object value) {
+        mFlags.put(flag, value);
+        return this;
+    }
+
+    @NonNull
+    public SessionParamsBuilder setAssetRepository(@NonNull AssetRepository repository) {
+        mAssetRepository = repository;
+        return this;
+    }
+
+    @NonNull
+    public SessionParams build() {
+        assert mFrameworkResources != null;
+        assert mProjectResources != null;
+        assert mThemeName != null;
+        assert mLayoutLog != null;
+        assert mLayoutlibCallback != null;
+
+        FolderConfiguration config = mConfigGenerator.getFolderConfig();
+        ResourceResolver resourceResolver = ResourceResolver.create(
+                ImmutableMap.of(
+                        ResourceNamespace.ANDROID, mFrameworkResources.getConfiguredResources(config),
+                        ResourceNamespace.TODO, mProjectResources.getConfiguredResources(config)),
+                new ResourceReference(
+                        ResourceNamespace.fromBoolean(!isProjectTheme),
+                        ResourceType.STYLE,
+                        mThemeName));
+
+        SessionParams params = new SessionParams(mLayoutParser, mRenderingMode, mProjectKey /* for
+        caching */, mConfigGenerator.getHardwareConfig(), resourceResolver, mLayoutlibCallback,
+                mMinSdk, mTargetSdk, mLayoutLog);
+
+        mFlags.forEach(params::setFlag);
+        params.setAssetRepository(mAssetRepository);
+
+        return params;
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java
index 0856ac9..54af92d 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/TestAssetRepository.java
@@ -18,6 +18,8 @@
 
 import com.android.ide.common.rendering.api.AssetRepository;
 
+import android.annotation.NonNull;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -28,6 +30,12 @@
  * {@link AssetRepository} used for render tests.
  */
 public class TestAssetRepository extends AssetRepository {
+    private final String mAssetPath;
+
+    public TestAssetRepository(@NonNull String assetPath) {
+        mAssetPath = assetPath;
+    }
+
     private static InputStream open(String path) throws FileNotFoundException {
         File asset = new File(path);
         if (asset.isFile()) {
@@ -44,7 +52,7 @@
 
     @Override
     public InputStream openAsset(String path, int mode) throws IOException {
-        return open(path);
+        return open(mAssetPath + path);
     }
 
     @Override
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/HookWidget.java b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/HookWidget.java
new file mode 100644
index 0000000..7efb9b8
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/HookWidget.java
@@ -0,0 +1,113 @@
+/*
+ * 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.test.widgets;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+@SuppressWarnings("unused") // Used from RenderTests
+public class HookWidget extends View {
+    private static final Runnable NOP_RUNNABLE = () -> {};
+    private static Runnable sOnPreDrawHook = NOP_RUNNABLE;
+    private static Runnable sOnPreMeasure = NOP_RUNNABLE;
+    private static Runnable sOnPreLayout = NOP_RUNNABLE;
+    private static Runnable sOnPostDrawHook = NOP_RUNNABLE;
+    private static Runnable sOnPostMeasure = NOP_RUNNABLE;
+    private static Runnable sOnPostLayout = NOP_RUNNABLE;
+
+    public static void setOnPreDrawHook(Runnable runnable) {
+        sOnPreDrawHook = runnable;
+    }
+
+    public static void setOnPreMeasure(Runnable runnable) {
+        sOnPreMeasure = runnable;
+    }
+
+    public static void setOnPreLayout(Runnable runnable) {
+        sOnPreLayout = runnable;
+    }
+
+    public static void setOnPostDrawHook(Runnable onPostDrawHook) {
+        sOnPostDrawHook = onPostDrawHook;
+    }
+
+    public static void setOnPostMeasure(Runnable onPostMeasure) {
+        sOnPostMeasure = onPostMeasure;
+    }
+
+    public static void setOnPostLayout(Runnable onPostLayout) {
+        sOnPostLayout = onPostLayout;
+    }
+
+    public static void reset() {
+        sOnPreDrawHook = NOP_RUNNABLE;
+        sOnPreMeasure = NOP_RUNNABLE;
+        sOnPreLayout = NOP_RUNNABLE;
+        sOnPostDrawHook = NOP_RUNNABLE;
+        sOnPostMeasure = NOP_RUNNABLE;
+        sOnPostLayout = NOP_RUNNABLE;
+    }
+
+    public HookWidget(Context context) {
+        super(context);
+    }
+
+    public HookWidget(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HookWidget(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public HookWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        sOnPreDrawHook.run();
+
+        super.onDraw(canvas);
+        Paint paint = new Paint();
+        paint.setColor(Color.BLUE);
+        Rect rect = new Rect();
+        getDrawingRect(rect);
+        canvas.drawRect(rect, paint);
+
+        sOnPostDrawHook.run();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        sOnPreMeasure.run();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        sOnPostMeasure.run();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        sOnPreLayout.run();
+        super.onLayout(changed, left, top, right, bottom);
+        sOnPostLayout.run();
+    }
+}
diff --git a/common/src/com/android/tools/layoutlib/annotations/NotNull.java b/common/src/com/android/tools/layoutlib/annotations/NotNull.java
new file mode 100644
index 0000000..4dcb24b
--- /dev/null
+++ b/common/src/com/android/tools/layoutlib/annotations/NotNull.java
@@ -0,0 +1,34 @@
+/*
+ * 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.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can not be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * not be null.
+ * <p/>
+ * When decorating a method, this denotes the method can not return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface NotNull {
+}
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index f595803..f57e1e6 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -132,6 +132,7 @@
         "android.content.res.Resources#getDimensionPixelOffset",
         "android.content.res.Resources#getDimensionPixelSize",
         "android.content.res.Resources#getDrawable",
+        "android.content.res.Resources#getFloat",
         "android.content.res.Resources#getFont",
         "android.content.res.Resources#getIdentifier",
         "android.content.res.Resources#getIntArray",
@@ -148,6 +149,7 @@
         "android.content.res.Resources#getText",
         "android.content.res.Resources#getTextArray",
         "android.content.res.Resources#getValue",
+        "android.content.res.Resources#getValueForDensity",
         "android.content.res.Resources#getXml",
         "android.content.res.Resources#loadXmlResourceParser",
         "android.content.res.Resources#obtainAttributes",
@@ -157,6 +159,7 @@
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "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#getAssignedPackageIdentifiers",
@@ -169,13 +172,15 @@
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.drawable.AdaptiveIconDrawable#<init>",
         "android.graphics.FontFamily#addFont",
-        "android.graphics.Typeface#getSystemFontConfigLocation",
-        "android.graphics.Typeface#makeFamilyFromParsed",
+        "android.graphics.Typeface#buildSystemFallback",
+        "android.graphics.Typeface#create",
+        "android.graphics.Typeface#createFontFamily",
+        "android.os.Binder#getNativeBBinderHolder",
+        "android.os.Binder#getNativeFinalizer",
         "android.os.Handler#sendMessageAtTime",
         "android.os.HandlerThread#run",
         "android.preference.Preference#getView",
         "android.text.format.DateFormat#is24HourFormat",
-        "android.text.Hyphenator#getSystemHyphenatorLocation",
         "android.util.Xml#newPullParser",
         "android.view.Choreographer#getInstance",
         "android.view.Choreographer#getRefreshRate",
@@ -185,6 +190,9 @@
         "android.view.HandlerActionQueue#postDelayed",
         "android.view.LayoutInflater#rInflate",
         "android.view.LayoutInflater#parseInclude",
+        "android.view.View#draw",
+        "android.view.View#layout",
+        "android.view.View#measure",
         "android.view.View#getWindowToken",
         "android.view.View#isInEditMode",
         "android.view.ViewRootImpl#isInTouchMode",
@@ -272,7 +280,7 @@
         "android.graphics.drawable.VectorDrawable",
         "android.os.SystemClock",
         "android.os.SystemProperties",
-        "android.text.AndroidBidi",
+        "android.text.MeasuredParagraph",
         "android.text.StaticLayout",
         "android.util.PathParser",
         "android.view.Display",
@@ -293,6 +301,7 @@
             "android.view.SurfaceView",                        "android.view._Original_SurfaceView",
             "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
             "android.webkit.WebView",                          "android.webkit._Original_WebView",
+            "android.graphics.ImageDecoder",                   "android.graphics._Original_ImageDecoder",
         };
 
     /**
diff --git a/remote/client/remote client.iml b/remote/client/remote client.iml
new file mode 100644
index 0000000..051a2c1
--- /dev/null
+++ b/remote/client/remote client.iml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../../../../libcore/xml/src/main/java" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module" module-name="remote common" />
+    <orderEntry type="module" module-name="common" />
+  </component>
+</module>
\ No newline at end of file
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
new file mode 100644
index 0000000..99143ad
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/RemoteBridgeClient.java
@@ -0,0 +1,148 @@
+/*
+ * 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;
+
+import com.android.ide.common.rendering.api.Bridge;
+import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layout.remote.api.RemoteBridge;
+import com.android.layout.remote.api.RemoteDrawableParams;
+import com.android.layout.remote.api.RemoteSessionParams;
+import com.android.layoutlib.bridge.remote.client.adapters.RemoteDrawableParamsAdapter;
+import com.android.layoutlib.bridge.remote.client.adapters.RemoteLayoutLogAdapter;
+import com.android.layoutlib.bridge.remote.client.adapters.RemoteRenderSessionAdapter;
+import com.android.layoutlib.bridge.remote.client.adapters.RemoteSessionParamsAdapter;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.File;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Map;
+
+public class RemoteBridgeClient extends Bridge {
+    private final RemoteBridge mDelegate;
+
+    private RemoteBridgeClient(@NotNull RemoteBridge delegate) {
+        mDelegate = delegate;
+    }
+
+    @NotNull
+    public static RemoteBridgeClient getRemoteBridge(int registryPort) throws RemoteException,
+            NotBoundException {
+        Registry registry = LocateRegistry.getRegistry(registryPort);
+        RemoteBridge remoteBridge = (RemoteBridge) registry.lookup(RemoteBridge.class.getName());
+
+        return new RemoteBridgeClient(remoteBridge);
+    }
+
+    @Override
+    public int getApiLevel() {
+        try {
+            return mDelegate.getApiLevel();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+
+        }
+    }
+
+    @Override
+    public int getRevision() {
+        try {
+            return mDelegate.getRevision();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean supports(int feature) {
+        try {
+            return mDelegate.supports(feature);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean init(Map<String, String> platformProperties, File fontLocation,
+            Map<String, Map<String, Integer>> enumValueMap, LayoutLog log) {
+        try {
+            return mDelegate.init(platformProperties, fontLocation, enumValueMap,
+                    RemoteLayoutLogAdapter.create(log));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean dispose() {
+        try {
+            return mDelegate.dispose();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public RenderSession createSession(SessionParams params) {
+        try {
+            RemoteSessionParams remoteParams = RemoteSessionParamsAdapter.create(params);
+
+            return new RemoteRenderSessionAdapter(mDelegate.createSession(remoteParams));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Result renderDrawable(DrawableParams params) {
+        try {
+            return mDelegate.renderDrawable(RemoteDrawableParamsAdapter.create(params));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void clearCaches(Object projectKey) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Result getViewParent(Object viewObject) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Result getViewIndex(Object viewObject) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isRtl(String locale) {
+        try {
+            return mDelegate.isRtl(locale);
+        } 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
new file mode 100644
index 0000000..e1cfe15
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteActionBarCallbackAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ActionBarCallback;
+import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.layout.remote.api.RemoteActionBarCallback;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.List;
+
+class RemoteActionBarCallbackAdapter implements RemoteActionBarCallback {
+    private final ActionBarCallback mDelegate;
+
+    private RemoteActionBarCallbackAdapter(@NotNull ActionBarCallback delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteActionBarCallback create(@NotNull ActionBarCallback delegate)
+            throws RemoteException {
+        return (RemoteActionBarCallback) UnicastRemoteObject.exportObject(
+                new RemoteActionBarCallbackAdapter(delegate), 0);
+    }
+
+    @Override
+    public List<String> getMenuIdNames() {
+        return mDelegate.getMenuIdNames();
+    }
+
+    @Override
+    public boolean getSplitActionBarWhenNarrow() {
+        return mDelegate.getSplitActionBarWhenNarrow();
+    }
+
+    @Override
+    public int getNavigationMode() {
+        return mDelegate.getNavigationMode();
+    }
+
+    @Override
+    public String getSubTitle() {
+        return mDelegate.getSubTitle();
+    }
+
+    @Override
+    public HomeButtonStyle getHomeButtonStyle() {
+        return mDelegate.getHomeButtonStyle();
+    }
+
+    @Override
+    public boolean isOverflowPopupNeeded() {
+        return mDelegate.isOverflowPopupNeeded();
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteAssetRepositoryAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteAssetRepositoryAdapter.java
new file mode 100644
index 0000000..4def740
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteAssetRepositoryAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.AssetRepository;
+import com.android.layout.remote.api.RemoteAssetRepository;
+import com.android.layout.remote.util.RemoteInputStream;
+import com.android.layout.remote.util.RemoteInputStreamAdapter;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteAssetRepositoryAdapter implements RemoteAssetRepository {
+    private final AssetRepository mDelegate;
+
+    private RemoteAssetRepositoryAdapter(@NotNull AssetRepository delegate) {
+        mDelegate = delegate;
+    }
+
+    static RemoteAssetRepository create(@NotNull AssetRepository delegate) throws RemoteException {
+        return (RemoteAssetRepository) UnicastRemoteObject.exportObject(
+                new RemoteAssetRepositoryAdapter(delegate), 0);
+    }
+
+    @Override
+    public RemoteInputStream openAsset(String path, int mode) throws IOException, RemoteException {
+        return RemoteInputStreamAdapter.create(mDelegate.openAsset(path, mode));
+    }
+
+    @Override
+    public RemoteInputStream openNonAsset(int cookie, String path, int mode)
+            throws IOException, RemoteException {
+        return RemoteInputStreamAdapter.create(mDelegate.openNonAsset(cookie, path, mode));
+    }
+
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteDrawableParamsAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteDrawableParamsAdapter.java
new file mode 100644
index 0000000..d2c2cd8
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteDrawableParamsAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.DrawableParams;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.layout.remote.api.RemoteDrawableParams;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteDrawableParamsAdapter extends RemoteRenderParamsAdapter implements
+        RemoteDrawableParams {
+    private final DrawableParams mDelegate;
+
+    private RemoteDrawableParamsAdapter(@NotNull DrawableParams drawableParams) {
+        super(drawableParams);
+        mDelegate = drawableParams;
+    }
+
+    @NotNull
+    public static RemoteDrawableParams create(@NotNull DrawableParams drawableParams)
+            throws RemoteException {
+        return (RemoteDrawableParams) UnicastRemoteObject.exportObject(
+                new RemoteDrawableParamsAdapter(drawableParams), 0);
+    }
+
+    @NotNull
+    @Override
+    public ResourceValue getDrawable() throws RemoteException {
+        return mDelegate.getDrawable();
+    }
+}
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
new file mode 100644
index 0000000..451d93e
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteILayoutPullParserAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.ILayoutPullParser;
+import com.android.layout.remote.api.RemoteILayoutPullParser;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+class RemoteILayoutPullParserAdapter extends RemoteXmlPullParserAdapter
+        implements RemoteILayoutPullParser {
+    private RemoteILayoutPullParserAdapter(@NotNull ILayoutPullParser delegate) {
+        super(delegate);
+    }
+
+    public static RemoteILayoutPullParser create(@NotNull ILayoutPullParser delegate)
+            throws RemoteException {
+        return (RemoteILayoutPullParser) UnicastRemoteObject.exportObject(
+                new RemoteILayoutPullParserAdapter(delegate), 0);
+    }
+
+    @Override
+    public Object getViewCookie() throws RemoteException {
+        return ((ILayoutPullParser) mDelegate).getViewCookie();
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java
new file mode 100644
index 0000000..c5dbfab
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutLogAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.LayoutLog;
+import com.android.layout.remote.api.RemoteLayoutLog;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.Serializable;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteLayoutLogAdapter implements RemoteLayoutLog {
+    private final LayoutLog mLog;
+
+    private RemoteLayoutLogAdapter(@NotNull LayoutLog log) {
+        mLog = log;
+    }
+
+    public static RemoteLayoutLog create(@NotNull LayoutLog log) throws RemoteException {
+        return (RemoteLayoutLog) UnicastRemoteObject.exportObject(new RemoteLayoutLogAdapter(log),
+                0);
+    }
+
+    @Override
+    public void warning(String tag, String message, Serializable data) {
+        mLog.warning(tag, message, null);
+    }
+
+    @Override
+    public void fidelityWarning(String tag, String message, Throwable throwable, Object viewCookie,
+            Object data) {
+        mLog.fidelityWarning(tag, message, throwable, viewCookie, data);
+    }
+
+    @Override
+    public void error(String tag, String message, Serializable data) {
+        mLog.error(tag, message, null);
+    }
+
+    @Override
+    public void error(String tag, String message, Throwable throwable, Serializable data) {
+        mLog.error(tag, message, throwable, null);
+    }
+}
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
new file mode 100644
index 0000000..b9b1a00
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -0,0 +1,159 @@
+/*
+ * 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.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.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams.Key;
+import com.android.layout.remote.api.RemoteActionBarCallback;
+import com.android.layout.remote.api.RemoteILayoutPullParser;
+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;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteLayoutlibCallbackAdapter implements RemoteLayoutlibCallback {
+    private final LayoutlibCallback mDelegate;
+
+    private RemoteLayoutlibCallbackAdapter(@NotNull LayoutlibCallback delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteLayoutlibCallback create(@NotNull LayoutlibCallback delegate)
+            throws RemoteException {
+        return (RemoteLayoutlibCallback) UnicastRemoteObject.exportObject(
+                new RemoteLayoutlibCallbackAdapter(delegate), 0);
+    }
+
+    @Override
+    public boolean supports(int ideFeature) {
+        return mDelegate.supports(ideFeature);
+    }
+
+    @Override
+    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+            throws Exception {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    @Override
+    public String getNamespace() {
+        return mDelegate.getNamespace();
+    }
+
+    @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) {
+        return mDelegate.resolveResourceId(id);
+    }
+
+    @Override
+    public Integer getResourceId(ResourceType type, String name) {
+        return mDelegate.getResourceId(type, name);
+    }
+
+    @Override
+    public RemoteILayoutPullParser getParser(ResourceValue layoutResource) {
+        try {
+            return RemoteILayoutPullParserAdapter.create(mDelegate.getParser(layoutResource));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+            ResourceReference itemRef, int fullPosition, int positionPerType,
+            int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
+            ViewAttribute viewAttribute, Object defaultValue) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    @Override
+    public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
+            Object viewObject) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    @Override
+    public RemoteActionBarCallback getActionBarCallback() {
+        try {
+            return RemoteActionBarCallbackAdapter.create(mDelegate.getActionBarCallback());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public <T> T getFlag(Key<T> key) {
+        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) {
+        try {
+            Class<?> clazz = mDelegate.findClass(name);
+            URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
+            if (url != null) {
+                return Paths.get(url.toURI());
+            }
+        } catch (ClassNotFoundException ignore) {
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+
+        return null;
+    }
+
+    @Override
+    public RemoteXmlPullParser getXmlFileParser(String fileName) {
+        try {
+            return RemoteXmlPullParserAdapter.create(mDelegate.getXmlFileParser(fileName));
+        } 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
new file mode 100644
index 0000000..d1f0411
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteParserFactoryAdapter.java
@@ -0,0 +1,51 @@
+/*
+ * 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
new file mode 100644
index 0000000..a3950d7
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderParamsAdapter.java
@@ -0,0 +1,161 @@
+/*
+ * 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.IImageFactory;
+import com.android.ide.common.rendering.api.RenderParams;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.Key;
+import com.android.layout.remote.api.RemoteAssetRepository;
+import com.android.layout.remote.api.RemoteHardwareConfig;
+import com.android.layout.remote.api.RemoteLayoutLog;
+import com.android.layout.remote.api.RemoteLayoutlibCallback;
+import com.android.layout.remote.api.RemoteRenderParams;
+import com.android.layout.remote.api.RemoteRenderResources;
+import com.android.layout.remote.api.RemoteSessionParams;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteRenderParamsAdapter implements RemoteRenderParams {
+    private final RenderParams mDelegate;
+
+    protected RemoteRenderParamsAdapter(@NotNull RenderParams params) {
+        mDelegate = params;
+    }
+
+    public static RemoteSessionParams create(@NotNull SessionParams params) throws RemoteException {
+        return (RemoteSessionParams) UnicastRemoteObject.exportObject(
+                new RemoteRenderParamsAdapter(params), 0);
+    }
+
+    @Nullable
+    @Override
+    public String getProjectKey() {
+        Object projectKey = mDelegate.getProjectKey();
+        // We can not transfer a random object so let's send just a string
+        return projectKey != null ? projectKey.toString() : null;
+    }
+
+    @Override
+    public RemoteHardwareConfig getRemoteHardwareConfig() {
+        return new RemoteHardwareConfig(mDelegate.getHardwareConfig());
+    }
+
+    @Override
+    public int getMinSdkVersion() {
+        return mDelegate.getMinSdkVersion();
+    }
+
+    @Override
+    public int getTargetSdkVersion() {
+        return mDelegate.getTargetSdkVersion();
+    }
+
+    @Override
+    public RemoteRenderResources getRemoteResources() {
+        try {
+            return RemoteRenderResourcesAdapter.create(mDelegate.getResources());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public RemoteAssetRepository getAssets() {
+        try {
+            return RemoteAssetRepositoryAdapter.create(mDelegate.getAssets());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public RemoteLayoutlibCallback getRemoteLayoutlibCallback() {
+        try {
+            return RemoteLayoutlibCallbackAdapter.create(mDelegate.getLayoutlibCallback());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public RemoteLayoutLog getLog() {
+        try {
+            return RemoteLayoutLogAdapter.create(mDelegate.getLog());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isBgColorOverridden() {
+        return mDelegate.isBgColorOverridden();
+    }
+
+    @Override
+    public int getOverrideBgColor() {
+        return mDelegate.getOverrideBgColor();
+    }
+
+    @Override
+    public long getTimeout() {
+        return mDelegate.getTimeout();
+    }
+
+    @Override
+    public IImageFactory getImageFactory() {
+        return mDelegate.getImageFactory();
+    }
+
+    @Override
+    public String getAppIcon() {
+        return mDelegate.getAppIcon();
+    }
+
+    @Override
+    public String getAppLabel() {
+        return mDelegate.getAppLabel();
+    }
+
+    @Override
+    public String getLocale() {
+        return mDelegate.getLocale();
+    }
+
+    @Override
+    public String getActivityName() {
+        return mDelegate.getActivityName();
+    }
+
+    @Override
+    public boolean isForceNoDecor() {
+        return mDelegate.isForceNoDecor();
+    }
+
+    @Override
+    public boolean isRtlSupported() {
+        return mDelegate.isRtlSupported();
+    }
+
+    @Override
+    public <T> T getFlag(Key<T> key) {
+        return mDelegate.getFlag(key);
+    }
+}
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
new file mode 100644
index 0000000..9ae58a9
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderResourcesAdapter.java
@@ -0,0 +1,124 @@
+/*
+ * 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.RenderResources;
+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.layout.remote.api.RemoteRenderResources;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.List;
+
+public class RemoteRenderResourcesAdapter implements RemoteRenderResources {
+    private final RenderResources mDelegate;
+
+    private RemoteRenderResourcesAdapter(@NotNull RenderResources delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteRenderResources create(@NotNull RenderResources resources)
+            throws RemoteException {
+        return (RemoteRenderResources) UnicastRemoteObject.exportObject(
+                new RemoteRenderResourcesAdapter(resources), 0);
+    }
+
+    @Override
+    public StyleResourceValue getDefaultTheme() {
+        return mDelegate.getDefaultTheme();
+    }
+
+    @Override
+    public void applyStyle(StyleResourceValue theme, boolean useAsPrimary) {
+        mDelegate.applyStyle(theme, useAsPrimary);
+    }
+
+    @Override
+    public void clearStyles() {
+        mDelegate.clearStyles();
+    }
+
+    @Override
+    public List<StyleResourceValue> getAllThemes() {
+        return mDelegate.getAllThemes();
+    }
+
+    @Override
+    public StyleResourceValue getTheme(String name, boolean frameworkTheme) {
+        return mDelegate.getTheme(name, frameworkTheme);
+    }
+
+    @Override
+    public boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme) {
+        return mDelegate.themeIsParentOf(parentTheme, childTheme);
+    }
+
+    @Override
+    public ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName) {
+        return mDelegate.getFrameworkResource(resourceType, resourceName);
+    }
+
+    @Override
+    public ResourceValue getProjectResource(ResourceType resourceType, String resourceName) {
+        return mDelegate.getProjectResource(resourceType, resourceName);
+    }
+
+    @Override
+    public ResourceValue findItemInTheme(ResourceReference attr) {
+        return mDelegate.findItemInTheme(attr);
+    }
+
+    @Override
+    public ResourceValue findItemInStyle(StyleResourceValue style, ResourceReference attr) {
+        return mDelegate.findItemInStyle(style, attr);
+    }
+
+    @Override
+    public ResourceValue resolveValue(ResourceValue value) {
+        return mDelegate.resolveResValue(value);
+    }
+
+    @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);
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderSessionAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderSessionAdapter.java
new file mode 100644
index 0000000..c2c892e
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteRenderSessionAdapter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.layout.remote.api.RemoteRenderSession;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.awt.image.BufferedImage;
+import java.rmi.RemoteException;
+import java.util.Collections;
+import java.util.List;
+
+public class RemoteRenderSessionAdapter extends RenderSession {
+    private final RemoteRenderSession mDelegate;
+
+    public RemoteRenderSessionAdapter(@NotNull RemoteRenderSession delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public Result getResult() {
+        try {
+            return mDelegate.getResult();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public BufferedImage getImage() {
+        try {
+            return mDelegate.getSerializableImage().getBufferedImage();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setSystemTimeNanos(long nanos) {
+        try {
+            mDelegate.setSystemTimeNanos(nanos);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setSystemBootTimeNanos(long nanos) {
+        try {
+            mDelegate.setSystemBootTimeNanos(nanos);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setElapsedFrameTimeNanos(long nanos) {
+        try {
+            mDelegate.setElapsedFrameTimeNanos(nanos);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Result render(long timeout, boolean forceMeasure) {
+        try {
+            return mDelegate.render(timeout, forceMeasure);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public List<ViewInfo> getRootViews() {
+        // TODO
+        return Collections.emptyList();
+    }
+
+    @Override
+    public List<ViewInfo> getSystemRootViews() {
+        // TODO
+        return Collections.emptyList();
+    }
+
+    @Override
+    public void dispose() {
+        try {
+            mDelegate.dispose();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteSessionParamsAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteSessionParamsAdapter.java
new file mode 100644
index 0000000..e6ed871
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteSessionParamsAdapter.java
@@ -0,0 +1,80 @@
+/*
+ * 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.AdapterBinding;
+import com.android.ide.common.rendering.api.IImageFactory;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.Key;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.layout.remote.api.RemoteAssetRepository;
+import com.android.layout.remote.api.RemoteHardwareConfig;
+import com.android.layout.remote.api.RemoteILayoutPullParser;
+import com.android.layout.remote.api.RemoteLayoutLog;
+import com.android.layout.remote.api.RemoteLayoutlibCallback;
+import com.android.layout.remote.api.RemoteRenderResources;
+import com.android.layout.remote.api.RemoteSessionParams;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.Map;
+
+public class RemoteSessionParamsAdapter extends RemoteRenderParamsAdapter implements RemoteSessionParams {
+    private final SessionParams mDelegate;
+
+    private RemoteSessionParamsAdapter(@NotNull SessionParams params) {
+        super(params);
+        mDelegate = params;
+    }
+
+    public static RemoteSessionParams create(@NotNull SessionParams params) throws RemoteException {
+        return (RemoteSessionParams) UnicastRemoteObject.exportObject(
+                new RemoteSessionParamsAdapter(params), 0);
+    }
+
+    @Override
+    public RenderingMode getRenderingMode() {
+        return mDelegate.getRenderingMode();
+    }
+
+    @Override
+    public boolean isLayoutOnly() {
+        return mDelegate.isLayoutOnly();
+    }
+
+    @Override
+    public Map<ResourceReference, AdapterBinding> getAdapterBindings() {
+        return mDelegate.getAdapterBindings();
+    }
+
+    @Override
+    public boolean getExtendedViewInfoMode() {
+        return mDelegate.getExtendedViewInfoMode();
+    }
+
+    @Override
+    public int getSimulatedPlatformVersion() {
+        return mDelegate.getSimulatedPlatformVersion();
+    }
+
+    @Override
+    public RemoteILayoutPullParser getLayoutDescription() throws RemoteException {
+        return RemoteILayoutPullParserAdapter.create(mDelegate.getLayoutDescription());
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteXmlPullParserAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteXmlPullParserAdapter.java
new file mode 100644
index 0000000..e646bbc
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteXmlPullParserAdapter.java
@@ -0,0 +1,234 @@
+/*
+ * 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.layout.remote.api.RemoteXmlPullParser;
+import com.android.layout.remote.util.RemoteInputStream;
+import com.android.layout.remote.util.StreamUtil;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteXmlPullParserAdapter implements RemoteXmlPullParser {
+    protected XmlPullParser mDelegate;
+
+    protected RemoteXmlPullParserAdapter(@NotNull XmlPullParser delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteXmlPullParser create(@NotNull XmlPullParser delegate)
+            throws RemoteException {
+        return (RemoteXmlPullParser) UnicastRemoteObject.exportObject(
+                new RemoteXmlPullParserAdapter(delegate), 0);
+    }
+
+    @Override
+    public void setFeature(String name, boolean state)
+            throws XmlPullParserException, RemoteException {
+        mDelegate.setFeature(name, state);
+    }
+
+    @Override
+    public boolean getFeature(String name) throws RemoteException {
+        return mDelegate.getFeature(name);
+    }
+
+    @Override
+    public void setProperty(String name, Object value)
+            throws XmlPullParserException, RemoteException {
+        mDelegate.setProperty(name, value);
+    }
+
+    @Override
+    public Object getProperty(String name) throws RemoteException {
+        return mDelegate.getProperty(name);
+    }
+
+    @Override
+    public void setInput(Reader in) throws XmlPullParserException, RemoteException {
+        mDelegate.setInput(in);
+    }
+
+    @Override
+    public void setInput(RemoteInputStream inputStream, String inputEncoding)
+            throws XmlPullParserException, RemoteException {
+        mDelegate.setInput(StreamUtil.getInputStream(inputStream), inputEncoding);
+    }
+
+    @Override
+    public String getInputEncoding() throws RemoteException {
+        return mDelegate.getInputEncoding();
+    }
+
+    @Override
+    public void defineEntityReplacementText(String entityName, String replacementText)
+            throws XmlPullParserException {
+
+    }
+
+    @Override
+    public int getNamespaceCount(int depth) throws XmlPullParserException, RemoteException {
+        return mDelegate.getNamespaceCount(depth);
+    }
+
+    @Override
+    public String getNamespacePrefix(int pos) throws XmlPullParserException, RemoteException {
+        return mDelegate.getNamespacePrefix(pos);
+    }
+
+    @Override
+    public String getNamespaceUri(int pos) throws XmlPullParserException, RemoteException {
+        return mDelegate.getNamespaceUri(pos);
+    }
+
+    @Override
+    public String getNamespace(String prefix) throws RemoteException {
+        return mDelegate.getNamespace(prefix);
+    }
+
+    @Override
+    public int getDepth() throws RemoteException {
+        return mDelegate.getDepth();
+    }
+
+    @Override
+    public String getPositionDescription() throws RemoteException {
+        return mDelegate.getPositionDescription();
+    }
+
+    @Override
+    public int getLineNumber() throws RemoteException {
+        return mDelegate.getLineNumber();
+    }
+
+    @Override
+    public int getColumnNumber() throws RemoteException {
+        return mDelegate.getColumnNumber();
+    }
+
+    @Override
+    public boolean isWhitespace() throws XmlPullParserException, RemoteException {
+        return mDelegate.isWhitespace();
+    }
+
+    @Override
+    public String getText() throws RemoteException {
+        return mDelegate.getText();
+    }
+
+    @Override
+    public char[] getTextCharacters(int[] holderForStartAndLength) throws RemoteException {
+        return mDelegate.getTextCharacters(holderForStartAndLength);
+    }
+
+    @Override
+    public String getNamespace() throws RemoteException {
+        return mDelegate.getNamespace();
+    }
+
+    @Override
+    public String getName() throws RemoteException {
+        return mDelegate.getName();
+    }
+
+    @Override
+    public String getPrefix() throws RemoteException {
+        return mDelegate.getPrefix();
+    }
+
+    @Override
+    public boolean isEmptyElementTag() throws XmlPullParserException, RemoteException {
+        return mDelegate.isEmptyElementTag();
+    }
+
+    @Override
+    public int getAttributeCount() throws RemoteException {
+        return mDelegate.getAttributeCount();
+    }
+
+    @Override
+    public String getAttributeNamespace(int index) throws RemoteException {
+        return mDelegate.getAttributeNamespace(index);
+    }
+
+    @Override
+    public String getAttributeName(int index) throws RemoteException {
+        return mDelegate.getAttributeName(index);
+    }
+
+    @Override
+    public String getAttributePrefix(int index) throws RemoteException {
+        return mDelegate.getAttributePrefix(index);
+    }
+
+    @Override
+    public String getAttributeType(int index) throws RemoteException {
+        return mDelegate.getAttributeType(index);
+    }
+
+    @Override
+    public boolean isAttributeDefault(int index) throws RemoteException {
+        return mDelegate.isAttributeDefault(index);
+    }
+
+    @Override
+    public String getAttributeValue(int index) throws RemoteException {
+        return mDelegate.getAttributeValue(index);
+    }
+
+    @Override
+    public String getAttributeValue(String namespace, String name) throws RemoteException {
+        return mDelegate.getAttributeValue(namespace, name);
+    }
+
+    @Override
+    public int getEventType() throws XmlPullParserException, RemoteException {
+        return mDelegate.getEventType();
+    }
+
+    @Override
+    public int next() throws XmlPullParserException, IOException, RemoteException {
+        return mDelegate.next();
+    }
+
+    @Override
+    public int nextToken() throws XmlPullParserException, IOException, RemoteException {
+        return mDelegate.nextToken();
+    }
+
+    @Override
+    public void require(int type, String namespace, String name)
+            throws XmlPullParserException, IOException, RemoteException {
+        mDelegate.require(type, namespace, name);
+    }
+
+    @Override
+    public String nextText() throws XmlPullParserException, IOException, RemoteException {
+        return mDelegate.nextText();
+    }
+
+    @Override
+    public int nextTag() throws XmlPullParserException, IOException, RemoteException {
+        return mDelegate.nextTag();
+    }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/package-info.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/package-info.java
new file mode 100644
index 0000000..6edf00d
--- /dev/null
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Package containing all the client side adapters. These adapters have the mission of receiving
+ * remote calls and translating them into the local API.
+ */
+package com.android.layoutlib.bridge.remote.client.adapters;
\ No newline at end of file
diff --git a/remote/common/remote common.iml b/remote/common/remote common.iml
new file mode 100644
index 0000000..e56c17c
--- /dev/null
+++ b/remote/common/remote common.iml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../../../../libcore/xml/src/main/java" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
+    <orderEntry type="module" module-name="common" />
+  </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
new file mode 100644
index 0000000..153a575
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteActionBarCallback.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") throws RemoteException;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ActionBarCallback;
+import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.List;
+
+/**
+ * Remote version of the {@link ActionBarCallback} class
+ */
+public interface RemoteActionBarCallback extends Remote {
+
+    List<String> getMenuIdNames() 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/RemoteAssetRepository.java b/remote/common/src/com/android/layout/remote/api/RemoteAssetRepository.java
new file mode 100644
index 0000000..5b4d412
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteAssetRepository.java
@@ -0,0 +1,36 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.AssetRepository;
+import com.android.layout.remote.util.RemoteInputStream;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.IOException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link AssetRepository} class
+ */
+public interface RemoteAssetRepository extends Remote {
+    @NotNull
+    RemoteInputStream openAsset(String path, int mode) throws IOException;
+
+    @NotNull
+    RemoteInputStream openNonAsset(int cookie, String path, int mode) throws IOException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteBridge.java b/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
new file mode 100644
index 0000000..8188198
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteBridge.java
@@ -0,0 +1,122 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.Bridge;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.io.File;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.Map;
+
+/**
+ * Interface that defines the operations available in the remote bridge. This is a remote version of
+ * the local {@link Bridge} API. Most of the methods are mapped 1:1 with the {@link Bridge} API
+ * unless there is a need for a method to be adapted for remote use.
+ */
+public interface RemoteBridge extends Remote {
+    /**
+     * Returns the API level of the layout library.
+     * <p>
+     * While no methods will ever be removed, some may become deprecated, and some new ones will
+     * appear. <p>All Layout libraries based on {@link Bridge} return at minimum an API level of 5.
+     */
+    int getApiLevel() throws RemoteException;
+
+    /**
+     * Returns the revision of the library inside a given (layoutlib) API level. The true revision
+     * number of the library is {@link #getApiLevel()}.{@link #getRevision()}
+     */
+    @SuppressWarnings("JavaDoc")
+    // javadoc pointing to itself.
+    int getRevision() throws RemoteException;
+
+    /**
+     * Returns true if the layout library supports the given feature.
+     *
+     * @see com.android.ide.common.rendering.api.Features
+     */
+    boolean supports(int feature) throws RemoteException;
+
+    /**
+     * Initializes the Bridge object.
+     *
+     * @param platformProperties The build properties for the platform.
+     * @param fontLocation the location of the fonts.
+     * @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.
+     *
+     * @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;
+
+    /**
+     * Prepares the layoutlib to be unloaded.
+     */
+    boolean dispose() throws RemoteException;
+
+    /**
+     * Starts a layout session by inflating and rendering it. The method returns a {@link
+     * RenderSession} on which further actions can be taken.
+     *
+     * @return a new {@link RenderSession} object that contains the result of the scene creation and
+     * first rendering.
+     */
+    @NotNull
+    RemoteRenderSession createSession(@NotNull RemoteSessionParams params) throws RemoteException;
+
+    /**
+     * Renders a Drawable. If the rendering is successful, the result image is accessible through
+     * {@link Result#getData()}. It is of type {@link BufferedImage}
+     *
+     * @param params the rendering parameters.
+     *
+     * @return the result of the action.
+     */
+    @NotNull
+    Result renderDrawable(@NotNull RemoteDrawableParams params) throws RemoteException;
+
+    /**
+     * Clears the resource cache for a specific project.
+     * <p>This cache contains bitmaps and nine patches that are loaded from the disk and reused
+     * until this method is called.
+     * <p>The cache is not configuration dependent and should only be cleared when a
+     * resource changes (at this time only bitmaps and 9 patches go into the cache).
+     * <p>
+     * The project key provided must be similar to the one passed in {@link RenderParams}.
+     *
+     * @param projectKey the key for the project.
+     */
+    void clearCaches(String projectKey) throws RemoteException;
+
+    /**
+     * Returns true if the character orientation of the locale is right to left.
+     *
+     * @param locale The locale formatted as language-region
+     *
+     * @return true if the locale is right to left.
+     */
+    boolean isRtl(String locale) throws RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteDrawableParams.java b/remote/common/src/com/android/layout/remote/api/RemoteDrawableParams.java
new file mode 100644
index 0000000..9ef1e9e
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteDrawableParams.java
@@ -0,0 +1,31 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link DrawableParams} class
+ */
+public interface RemoteDrawableParams extends RemoteRenderParams {
+    @NotNull
+    ResourceValue getDrawable() throws RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteHardwareConfig.java b/remote/common/src/com/android/layout/remote/api/RemoteHardwareConfig.java
new file mode 100644
index 0000000..191cca7
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteHardwareConfig.java
@@ -0,0 +1,71 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.HardwareConfig;
+import com.android.resources.Density;
+import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
+import com.android.resources.ScreenSize;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.Serializable;
+
+/**
+ * Remote version of the {@link HardwareConfig} class
+ */
+// TODO: Just make HardwareConfig serializable
+public class RemoteHardwareConfig implements Serializable {
+    private int mScreenWidth;
+    private int mScreenHeight;
+    private Density mDensity;
+    private float mXdpi;
+    private float mYdpi;
+    private ScreenSize mScreenSize;
+    private ScreenOrientation mOrientation;
+    private ScreenRound mScreenRoundness;
+    private boolean mHasSoftwareButtons;
+
+    public RemoteHardwareConfig() {
+    }
+
+    public RemoteHardwareConfig(@NotNull HardwareConfig config) {
+        this(config.getScreenWidth(), config.getScreenHeight(), config.getDensity(),
+                config.getXdpi(), config.getYdpi(), config.getScreenSize(), config.getOrientation(),
+                config.getScreenRoundness(), config.hasSoftwareButtons());
+    }
+
+    private RemoteHardwareConfig(int screenWidth, int screenHeight, Density density, float xdpi,
+            float ydpi, ScreenSize screenSize, ScreenOrientation orientation,
+            ScreenRound screenRoundness, boolean hasSoftwareButtons) {
+        mScreenWidth = screenWidth;
+        mScreenHeight = screenHeight;
+        mDensity = density;
+        mXdpi = xdpi;
+        mYdpi = ydpi;
+        mScreenSize = screenSize;
+        mOrientation = orientation;
+        mScreenRoundness = screenRoundness;
+        mHasSoftwareButtons = hasSoftwareButtons;
+    }
+
+    @NotNull
+    public HardwareConfig getHardwareConfig() {
+        return new HardwareConfig(mScreenWidth, mScreenHeight, mDensity, mXdpi, mYdpi, mScreenSize,
+                mOrientation, mScreenRoundness, mHasSoftwareButtons);
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java b/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java
new file mode 100644
index 0000000..585535b
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteILayoutPullParser.java
@@ -0,0 +1,28 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.ILayoutPullParser;
+
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link ILayoutPullParser} interface
+ */
+public interface RemoteILayoutPullParser extends RemoteXmlPullParser {
+    Object getViewCookie() throws RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
new file mode 100644
index 0000000..c3b5c61
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
@@ -0,0 +1,72 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+
+import java.io.Serializable;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link LayoutLog} class
+ */
+public interface RemoteLayoutLog extends Remote {
+    /**
+     * Logs a warning.
+     *
+     * @param tag a tag describing the type of the warning
+     * @param message the message of the warning
+     * @param data an optional data bundle that the client can use to improve the warning display.
+     */
+    void warning(String tag, String message, Serializable data) throws RemoteException;
+
+    /**
+     * Logs a fidelity warning.
+     * <p>
+     * This type of warning indicates that the render will not be the same as the rendering on a
+     * device due to limitation of the Java rendering API.
+     *
+     * @param tag a tag describing the type of the warning
+     * @param message the message of the warning
+     * @param throwable an optional Throwable that triggered the warning
+     * @param viewCookie optional cookie of the view associated to this error
+     * @param data an optional data bundle that the client can use to improve the warning display.
+     */
+    void fidelityWarning(String tag, String message, Throwable throwable, Object viewCookie,
+            Object data) throws RemoteException;
+
+    /**
+     * Logs an error.
+     *
+     * @param tag a tag describing the type of the error
+     * @param message the message of the error
+     * @param data an optional data bundle that the client can use to improve the error display.
+     */
+    void error(String tag, String message, Serializable data) throws RemoteException;
+
+    /**
+     * Logs an error, and the {@link Throwable} that triggered it.
+     *
+     * @param tag a tag describing the type of the error
+     * @param message the message of the error
+     * @param throwable the Throwable that triggered the error
+     * @param data an optional data bundle that the client can use to improve the error display.
+     */
+    void error(String tag, String message, Throwable throwable, Serializable data)
+            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
new file mode 100644
index 0000000..0f315ca
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
@@ -0,0 +1,83 @@
+/*
+ * 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.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.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 java.nio.file.Path;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link LayoutlibCallback} class
+ */
+public interface RemoteLayoutlibCallback extends Remote {
+    boolean supports(int ideFeature) throws RemoteException;
+
+    Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+            throws Exception, RemoteException;
+
+    String getNamespace() throws RemoteException;
+
+    RemoteResolveResult resolveResourceId(int id) throws RemoteException;
+
+    String resolveResourceId(int[] id) throws RemoteException;
+
+    Integer getResourceId(ResourceType type, String name) throws RemoteException;
+
+    RemoteILayoutPullParser getParser(ResourceValue layoutResource) throws RemoteException;
+
+    Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+            ResourceReference itemRef, int fullPosition, int positionPerType,
+            int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
+            ViewAttribute viewAttribute, Object defaultValue) throws RemoteException;
+
+    AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
+            Object viewObject) throws RemoteException;
+
+    RemoteActionBarCallback getActionBarCallback() throws RemoteException;
+
+    <T> T getFlag(Key<T> key) throws RemoteException;
+
+    RemoteParserFactory getParserFactory() throws RemoteException;
+
+    Path findClassPath(String name) throws RemoteException;
+
+    RemoteXmlPullParser getXmlFileParser(String fileName) throws RemoteException;
+
+    class RemoteResolveResult implements Serializable {
+        private ResourceType type;
+        private String value;
+
+        public RemoteResolveResult(ResourceType type, String value) {
+            this.type = type;
+            this.value = value;
+        }
+
+        public Pair<ResourceType, String> asPair() {
+            return Pair.of(type, value);
+        }
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java b/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java
new file mode 100644
index 0000000..31a35b2
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteParserFactory.java
@@ -0,0 +1,30 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.ParserFactory;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link ParserFactory} class
+ */
+public interface RemoteParserFactory extends Remote {
+    RemoteXmlPullParser createParser(@Nullable String debugName) throws RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java b/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
new file mode 100644
index 0000000..da2bd8e
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteRenderParams.java
@@ -0,0 +1,65 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.IImageFactory;
+import com.android.ide.common.rendering.api.SessionParams.Key;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteRenderParams extends Remote {
+    @Nullable
+    String getProjectKey() throws RemoteException;
+
+    RemoteHardwareConfig getRemoteHardwareConfig() throws RemoteException;
+
+    int getMinSdkVersion() throws RemoteException;
+
+    int getTargetSdkVersion() throws RemoteException;
+
+    RemoteRenderResources getRemoteResources() throws RemoteException;
+
+    RemoteAssetRepository getAssets() throws RemoteException;
+
+    RemoteLayoutlibCallback getRemoteLayoutlibCallback() throws RemoteException;
+
+    RemoteLayoutLog getLog() throws RemoteException;
+
+    boolean isBgColorOverridden() throws RemoteException;
+
+    int getOverrideBgColor() throws RemoteException;
+
+    long getTimeout() throws RemoteException;
+
+    IImageFactory getImageFactory() throws RemoteException;
+
+    String getAppIcon() throws RemoteException;
+
+    String getAppLabel() throws RemoteException;
+
+    String getLocale() throws RemoteException;
+
+    String getActivityName() throws RemoteException;
+
+    boolean isForceNoDecor() throws RemoteException;
+
+    boolean isRtlSupported() throws RemoteException;
+
+    <T> T getFlag(Key<T> key) 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
new file mode 100644
index 0000000..f54d44d
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteRenderResources.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") throws RemoteException;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.RenderResources;
+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 java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.List;
+
+/**
+ * Remote version of the {@link RenderResources} class
+ */
+public interface RemoteRenderResources extends Remote {
+    StyleResourceValue getDefaultTheme() throws RemoteException;
+
+    void applyStyle(StyleResourceValue theme, boolean useAsPrimary) throws RemoteException;
+
+    void clearStyles() throws RemoteException;
+
+    List<StyleResourceValue> getAllThemes() throws RemoteException;
+
+
+    StyleResourceValue getTheme(String name, boolean frameworkTheme) throws RemoteException;
+
+
+    boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme)
+            throws RemoteException;
+
+    ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName)
+            throws RemoteException;
+
+    ResourceValue getProjectResource(ResourceType resourceType, String resourceName)
+            throws RemoteException;
+
+
+    ResourceValue findItemInTheme(ResourceReference attr) 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;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteRenderSession.java b/remote/common/src/com/android/layout/remote/api/RemoteRenderSession.java
new file mode 100644
index 0000000..648186f
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteRenderSession.java
@@ -0,0 +1,48 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.layout.remote.util.SerializableImage;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+
+/**
+ * Remote version of the {@link RenderSession} class
+ */
+public interface RemoteRenderSession extends Remote {
+    @NotNull
+    Result getResult() throws RemoteException;
+
+    @NotNull
+    SerializableImage getSerializableImage() throws RemoteException;
+
+    void setSystemTimeNanos(long nanos) throws RemoteException;
+
+    void setSystemBootTimeNanos(long nanos) throws RemoteException;
+
+    void setElapsedFrameTimeNanos(long nanos) throws RemoteException;
+
+    void dispose() throws RemoteException;
+
+    @NotNull
+    Result render(long timeout, boolean forceMeasure) throws RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteSessionParams.java b/remote/common/src/com/android/layout/remote/api/RemoteSessionParams.java
new file mode 100644
index 0000000..52a2a09
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteSessionParams.java
@@ -0,0 +1,45 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.IImageFactory;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.Key;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.Map;
+
+/**
+ * Remote version of the {@link SessionParams} class
+ */
+public interface RemoteSessionParams extends RemoteRenderParams {
+    RenderingMode getRenderingMode() throws RemoteException;
+
+    boolean isLayoutOnly() throws RemoteException;
+
+    Map<ResourceReference, AdapterBinding> getAdapterBindings() throws RemoteException;
+
+    boolean getExtendedViewInfoMode() throws RemoteException;
+
+    int getSimulatedPlatformVersion() throws RemoteException;
+
+    RemoteILayoutPullParser getLayoutDescription() throws RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteXmlPullParser.java b/remote/common/src/com/android/layout/remote/api/RemoteXmlPullParser.java
new file mode 100644
index 0000000..e3e05d3
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/RemoteXmlPullParser.java
@@ -0,0 +1,109 @@
+/*
+ * 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.layout.remote.api;
+
+import com.android.layout.remote.util.RemoteInputStream;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * Remote version of the {@link XmlPullParser} interface
+ */
+public interface RemoteXmlPullParser extends Remote {
+    void setFeature(String name, boolean state) throws XmlPullParserException, RemoteException;
+
+    boolean getFeature(String name) throws RemoteException;
+
+    void setProperty(String name, Object value) throws XmlPullParserException, RemoteException;
+
+    Object getProperty(String name) throws RemoteException;
+
+    void setInput(Reader in) throws XmlPullParserException, RemoteException;
+
+    void setInput(RemoteInputStream inputStream, String inputEncoding)
+            throws XmlPullParserException, RemoteException;
+
+    String getInputEncoding() throws RemoteException;
+
+    void defineEntityReplacementText(String entityName, String replacementText)
+            throws XmlPullParserException, RemoteException;
+
+    int getNamespaceCount(int depth) throws XmlPullParserException, RemoteException;
+
+    String getNamespacePrefix(int pos) throws XmlPullParserException, RemoteException;
+
+    String getNamespaceUri(int pos) throws XmlPullParserException, RemoteException;
+
+    String getNamespace(String prefix) throws RemoteException;
+
+    int getDepth() throws RemoteException;
+
+    String getPositionDescription() throws RemoteException;
+
+    int getLineNumber() throws RemoteException;
+
+    int getColumnNumber() throws RemoteException;
+
+    boolean isWhitespace() throws XmlPullParserException, RemoteException;
+
+    String getText() throws RemoteException;
+
+    char[] getTextCharacters(int[] holderForStartAndLength) throws RemoteException;
+
+    String getNamespace() throws RemoteException;
+
+    String getName() throws RemoteException;
+
+    String getPrefix() throws RemoteException;
+
+    boolean isEmptyElementTag() throws XmlPullParserException, RemoteException;
+
+    int getAttributeCount() throws RemoteException;
+
+    String getAttributeNamespace(int index) throws RemoteException;
+
+    String getAttributeName(int index) throws RemoteException;
+
+    String getAttributePrefix(int index) throws RemoteException;
+
+    String getAttributeType(int index) throws RemoteException;
+
+    boolean isAttributeDefault(int index) throws RemoteException;
+
+    String getAttributeValue(int index) throws RemoteException;
+
+    String getAttributeValue(String namespace, String name) throws RemoteException;
+
+    int getEventType() throws XmlPullParserException, RemoteException;
+
+    int next() throws XmlPullParserException, IOException, RemoteException;
+
+    int nextToken() throws XmlPullParserException, IOException, RemoteException;
+
+    void require(int type, String namespace, String name)
+            throws XmlPullParserException, IOException, RemoteException;
+
+    String nextText() throws XmlPullParserException, IOException, RemoteException;
+
+    int nextTag() throws XmlPullParserException, IOException, RemoteException;
+}
diff --git a/remote/common/src/com/android/layout/remote/api/package-info.java b/remote/common/src/com/android/layout/remote/api/package-info.java
new file mode 100644
index 0000000..c7591c6
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/api/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Package containing all the interfaces that define the remote version of the Layoutlib API. This
+ * interface matches the local layout API closely. In some cases where special capabilities to
+ * support the remote calls are needed, the API will be different.
+ */
+package com.android.layout.remote.api;
\ No newline at end of file
diff --git a/remote/common/src/com/android/layout/remote/util/RemoteInputStream.java b/remote/common/src/com/android/layout/remote/util/RemoteInputStream.java
new file mode 100644
index 0000000..f3b1611
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/util/RemoteInputStream.java
@@ -0,0 +1,42 @@
+/*
+ * 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.layout.remote.util;
+
+import java.io.IOException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteInputStream extends Remote {
+    int read() throws IOException;
+
+    byte[] read(int off, int len) throws IOException;
+
+    long skip(long n) throws IOException;
+
+    int available() throws IOException;
+
+    void close() throws IOException;
+
+    void mark(int readlimit) throws RemoteException;
+
+    void reset() throws IOException;
+
+    boolean markSupported() throws RemoteException;
+
+    class EndOfStreamException extends IOException {
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/util/RemoteInputStreamAdapter.java b/remote/common/src/com/android/layout/remote/util/RemoteInputStreamAdapter.java
new file mode 100644
index 0000000..e983202
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/util/RemoteInputStreamAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.layout.remote.util;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteInputStreamAdapter implements RemoteInputStream {
+
+    private InputStream mDelegate;
+
+    private RemoteInputStreamAdapter(@NotNull InputStream delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteInputStream create(@NotNull InputStream is) throws RemoteException {
+        return (RemoteInputStream) UnicastRemoteObject.exportObject(
+                new RemoteInputStreamAdapter(is), 0);
+    }
+
+    @Override
+    public int read() throws IOException {
+        return mDelegate.read();
+    }
+
+    @Override
+    public byte[] read(int off, int len) throws IOException, RemoteException {
+        byte[] buffer = new byte[len];
+        if (mDelegate.read(buffer, off, len) == -1) {
+            throw new EndOfStreamException();
+        }
+        return buffer;
+    }
+
+    @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();
+    }
+
+    @Override
+    public boolean markSupported() {
+        return mDelegate.markSupported();
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/util/SerializableImage.java b/remote/common/src/com/android/layout/remote/util/SerializableImage.java
new file mode 100644
index 0000000..999aae4
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/util/SerializableImage.java
@@ -0,0 +1,30 @@
+/*
+ * 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.layout.remote.util;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.awt.image.BufferedImage;
+import java.io.Serializable;
+
+/**
+ * Serializable BufferedImage that can be sent across VMs
+ */
+public interface SerializableImage extends Serializable {
+    @NotNull
+    BufferedImage getBufferedImage();
+}
diff --git a/remote/common/src/com/android/layout/remote/util/SerializableImageImpl.java b/remote/common/src/com/android/layout/remote/util/SerializableImageImpl.java
new file mode 100644
index 0000000..59c8c3f
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/util/SerializableImageImpl.java
@@ -0,0 +1,55 @@
+/*
+ * 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.layout.remote.util;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Naive implementation of {@link SerializableImage} using {@link ImageIO} and PNG format as
+ * transport.
+ */
+public class SerializableImageImpl implements SerializableImage {
+    @NotNull
+    transient private BufferedImage mBufferedImage;
+
+    public SerializableImageImpl(@NotNull BufferedImage delegate) {
+        mBufferedImage = delegate;
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        ImageIO.write(mBufferedImage, "png", out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        mBufferedImage = ImageIO.read(in);
+    }
+
+    @Override
+    @NotNull
+    public BufferedImage getBufferedImage() {
+        return mBufferedImage;
+    }
+}
diff --git a/remote/common/src/com/android/layout/remote/util/StreamUtil.java b/remote/common/src/com/android/layout/remote/util/StreamUtil.java
new file mode 100644
index 0000000..e05b5de
--- /dev/null
+++ b/remote/common/src/com/android/layout/remote/util/StreamUtil.java
@@ -0,0 +1,96 @@
+/*
+ * 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.layout.remote.util;
+
+import com.android.layout.remote.util.RemoteInputStream.EndOfStreamException;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.rmi.RemoteException;
+
+public class StreamUtil {
+    /**
+     * Returns a local {@link InputStream} from a {@link RemoteInputStream}
+     */
+    @NotNull
+    public static InputStream getInputStream(@NotNull RemoteInputStream is) {
+        return new InputStream() {
+            @Override
+            public int read() throws IOException {
+                return is.read();
+            }
+
+            @SuppressWarnings("NullableProblems")
+            @Override
+            public int read(byte[] b) throws IOException {
+                return read(b, 0, b.length);
+            }
+
+            @SuppressWarnings("NullableProblems")
+            @Override
+            public int read(byte[] b, int off, int len) throws IOException {
+                try {
+                    byte[] read = is.read(off, len);
+                    int actualLength = Math.min(len, read.length);
+                    System.arraycopy(read, 0, b, off, actualLength);
+                    return actualLength;
+                } catch (EndOfStreamException e) {
+                    return -1;
+                }
+            }
+
+            @Override
+            public long skip(long n) throws IOException {
+                return is.skip(n);
+            }
+
+            @Override
+            public int available() throws IOException {
+                return is.available();
+            }
+
+            @Override
+            public void close() throws IOException {
+                is.close();
+            }
+
+            @Override
+            public synchronized void mark(int readlimit) {
+                try {
+                    is.mark(readlimit);
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            @Override
+            public synchronized void reset() throws IOException {
+                is.reset();
+            }
+
+            @Override
+            public boolean markSupported() {
+                try {
+                    return is.markSupported();
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+}
diff --git a/remote/server/remote server.iml b/remote/server/remote server.iml
new file mode 100644
index 0000000..9fc0852
--- /dev/null
+++ b/remote/server/remote server.iml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="bridge" />
+    <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../../../../libcore/xml/src/main/java" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module" module-name="remote common" />
+    <orderEntry type="module" module-name="common" />
+  </component>
+</module>
\ No newline at end of file
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
new file mode 100644
index 0000000..2593afc
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/RemoteBridgeImpl.java
@@ -0,0 +1,163 @@
+/*
+ * 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;
+
+import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.RenderParams;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layout.remote.api.RemoteBridge;
+import com.android.layout.remote.api.RemoteDrawableParams;
+import com.android.layout.remote.api.RemoteLayoutLog;
+import com.android.layout.remote.api.RemoteRenderParams;
+import com.android.layout.remote.api.RemoteRenderSession;
+import com.android.layout.remote.api.RemoteSessionParams;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.remote.server.adapters.RemoteAssetRepositoryAdapter;
+import com.android.layoutlib.bridge.remote.server.adapters.RemoteILayoutPullParserAdapter;
+import com.android.layoutlib.bridge.remote.server.adapters.RemoteLayoutLogAdapter;
+import com.android.layoutlib.bridge.remote.server.adapters.RemoteLayoutlibCallbackAdapter;
+import com.android.layoutlib.bridge.remote.server.adapters.RemoteRenderResourcesAdapter;
+import com.android.layoutlib.bridge.remote.server.adapters.RemoteRenderSessionAdapter;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.File;
+import java.rmi.RemoteException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Remote {@link Bridge} implementation. This class is the remote entry point for the server. Its
+ * responsibility is to receive the remote calls and apply the required transformations to convert
+ * it into a regular call to the {@link Bridge} class.
+ */
+public class RemoteBridgeImpl implements RemoteBridge {
+    private Bridge mBridge = new Bridge();
+
+    /**
+     * The project keys are used as key for some caches. They are usually expected to remain in
+     * memory so WeakReferences are used in the caches.
+     * Because in the remote bridge we do not have a real pointer to the object, we keep the strings
+     * in memory until they are cleared.
+     */
+    private Map<String, String> mCachedProjectKeys = new HashMap<>();
+
+    @Override
+    public int getApiLevel() {
+        return mBridge.getApiLevel();
+    }
+
+    @Override
+    public int getRevision() {
+        return mBridge.getRevision();
+    }
+
+    @Override
+    public boolean supports(int feature) {
+        return mBridge.supports(feature);
+    }
+
+    @Override
+    public boolean init(Map<String, String> platformProperties, File fontLocation,
+            Map<String, Map<String, Integer>> enumValueMap, RemoteLayoutLog log) {
+        return mBridge.init(platformProperties, fontLocation, enumValueMap,
+                log != null ? new RemoteLayoutLogAdapter(log) : null);
+    }
+
+    @Override
+    public boolean dispose() {
+        return mBridge.dispose();
+    }
+
+    private static void setupRenderParams(@NotNull RenderParams params,
+            @NotNull RemoteRenderParams remoteParams) throws RemoteException {
+        params.setAssetRepository(new RemoteAssetRepositoryAdapter(remoteParams.getAssets()));
+        params.setActivityName(remoteParams.getActivityName());
+        params.setAppIcon(remoteParams.getAppIcon());
+        params.setAppLabel(remoteParams.getAppLabel());
+        params.setTimeout(remoteParams.getTimeout());
+        params.setLocale(remoteParams.getLocale());
+        if (remoteParams.isForceNoDecor()) {
+            params.setForceNoDecor();
+        }
+        params.setRtlSupport(remoteParams.isRtlSupported());
+        if (remoteParams.isBgColorOverridden()) {
+            params.setOverrideBgColor(remoteParams.getOverrideBgColor());
+        }
+        params.setImageFactory(remoteParams.getImageFactory());
+        // TODO: Also unpack remote flags and pass them to RenderParams
+    }
+
+    @NotNull
+    @Override
+    public RemoteRenderSession createSession(@NotNull RemoteSessionParams remoteParams) {
+        try {
+            String projectKey = mCachedProjectKeys.putIfAbsent(remoteParams.getProjectKey(),
+                    remoteParams.getProjectKey());
+
+            // Unpack the remote params and convert it into the local SessionParams
+            SessionParams params = new SessionParams(
+                    new RemoteILayoutPullParserAdapter(remoteParams.getLayoutDescription()),
+                    remoteParams.getRenderingMode(), projectKey,
+                    remoteParams.getRemoteHardwareConfig().getHardwareConfig(),
+                    new RemoteRenderResourcesAdapter(remoteParams.getRemoteResources()),
+                    new RemoteLayoutlibCallbackAdapter(remoteParams.getRemoteLayoutlibCallback()),
+                    remoteParams.getMinSdkVersion(), remoteParams.getTargetSdkVersion(),
+                    new RemoteLayoutLogAdapter(remoteParams.getLog()));
+            setupRenderParams(params, remoteParams);
+            return RemoteRenderSessionAdapter.create(mBridge.createSession(params));
+
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @NotNull
+    @Override
+    public Result renderDrawable(@NotNull RemoteDrawableParams remoteParams) {
+        try {
+            String projectKey = mCachedProjectKeys.putIfAbsent(remoteParams.getProjectKey(),
+                    remoteParams.getProjectKey());
+
+            DrawableParams params = new DrawableParams(
+                    remoteParams.getDrawable(),
+                    projectKey,
+                    remoteParams.getRemoteHardwareConfig().getHardwareConfig(),
+                    new RemoteRenderResourcesAdapter(remoteParams.getRemoteResources()),
+                    new RemoteLayoutlibCallbackAdapter(remoteParams.getRemoteLayoutlibCallback()),
+                    remoteParams.getMinSdkVersion(), remoteParams.getTargetSdkVersion(),
+                    new RemoteLayoutLogAdapter(remoteParams.getLog()));
+            setupRenderParams(params, remoteParams);
+            return mBridge.renderDrawable(params);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void clearCaches(String projectKey) {
+        mCachedProjectKeys.remove(projectKey);
+        mBridge.clearCaches(projectKey);
+    }
+
+    @Override
+    public boolean isRtl(String locale) {
+        return mBridge.isRtl(locale);
+    }
+}
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
new file mode 100644
index 0000000..09c27fd
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/ServerMain.java
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+import com.android.layout.remote.api.RemoteBridge;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.rmi.NoSuchObjectException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * Main server class. The main method will start an RMI server for the {@link RemoteBridgeImpl}
+ * class.
+ */
+public class ServerMain {
+    private static final String RUNNING_SERVER_STR = "Server is running on port ";
+    public static int REGISTRY_BASE_PORT = 9000;
+
+    private final int mPort;
+    private final Registry mRegistry;
+
+    private ServerMain(int port, @NotNull Registry registry) {
+        mPort = port;
+        mRegistry = registry;
+    }
+
+    public int getPort() {
+        return mPort;
+    }
+
+    public void stop() {
+        try {
+            UnicastRemoteObject.unexportObject(mRegistry, true);
+        } catch (NoSuchObjectException ignored) {
+        }
+    }
+
+    /**
+     * This will start a new JVM and connect to the new JVM RMI registry.
+     * <p/>
+     * The server will start looking for ports available for the {@link Registry} until a free one
+     * is found. The port number will be returned.
+     * If no ports are available, a {@link RemoteException} will be thrown.
+     * @param basePort port number to start looking for available ports
+     * @param limit number of ports to check. The last port to be checked will be (basePort + limit)
+     */
+    public static ServerMain forkAndStartServer(int basePort, int limit)
+            throws IOException, InterruptedException {
+        // We start a new VM by copying all the parameter that we received in the current VM.
+        // We only remove the agentlib parameter since that could cause a port collision and avoid
+        // the new VM from starting.
+        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
+        List<String> arguments = runtimeMxBean.getInputArguments().stream()
+                .filter(arg -> !arg.contains("-agentlib")) // Filter agentlib to avoid conflicts
+                .collect(Collectors.toList());
+
+        Path javaPath = Paths.get(System.getProperty("java.home"), "bin", "java");
+        String thisClassName = ServerMain.class.getName()
+                .replace('.','/');
+
+        List<String> cmd = new ArrayList<>();
+        cmd.add(javaPath.toString());
+
+        // Inherited arguments
+        cmd.addAll(arguments);
+
+        // Classpath
+        cmd.add("-cp");
+        cmd.add(System.getProperty("java.class.path"));
+
+        // Class name and path
+        cmd.add(thisClassName);
+
+        // ServerMain parameters [basePort. limit]
+        cmd.add(Integer.toString(basePort));
+        cmd.add(Integer.toString(limit));
+
+        Process process = new ProcessBuilder()
+                .command(cmd)
+                .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();
+
+        Runnable killServer = () -> {
+            process.destroyForcibly();
+            outputThread.interrupt();
+            try {
+                outputThread.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);
+
+            if (line.startsWith(RUNNING_SERVER_STR)) {
+                int runningPort = Integer.parseInt(line.substring(RUNNING_SERVER_STR.length()));
+                System.out.println("Running on port " + runningPort);
+
+                // We already know where the server is running so we just need to get the registry
+                // and return our own instance of ServerMain
+                Registry registry = LocateRegistry.getRegistry(runningPort);
+                return new ServerMain(runningPort, registry) {
+                    @Override
+                    public void stop() {
+                        killServer.run();
+                    }
+                };
+            }
+        }
+
+        killServer.run();
+        throw new IOException("Unable to find start string");
+    }
+
+    /**
+     * The server will start looking for ports available for the {@link Registry} until a free one
+     * is found. The port number will be returned.
+     * If no ports are available, a {@link RemoteException} will be thrown.
+     * @param basePort port number to start looking for available ports
+     * @param limit number of ports to check. The last port to be checked will be (basePort + limit)
+     */
+    private static ServerMain startServer(int basePort, int limit) throws RemoteException {
+        RemoteBridgeImpl remoteBridge = new RemoteBridgeImpl();
+        RemoteBridge stub = (RemoteBridge) UnicastRemoteObject.exportObject(remoteBridge, 0);
+
+        RemoteException lastException = null;
+        for (int port = basePort; port <= basePort + limit; port++) {
+            try {
+                Registry registry = LocateRegistry.createRegistry(port);
+                registry.rebind(RemoteBridge.class.getName(), stub);
+                return new ServerMain(port, registry);
+            } catch (RemoteException e) {
+                lastException = e;
+            }
+        }
+
+        if (lastException == null) {
+            lastException = new RemoteException("Unable to start server");
+        }
+
+        throw lastException;
+    }
+
+    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;
+
+        ServerMain server = startServer(basePort, limit);
+        System.out.println(RUNNING_SERVER_STR + server.getPort());
+    }
+}
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
new file mode 100644
index 0000000..b5c040e
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteActionBarCallbackAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.ActionBarCallback;
+import com.android.layout.remote.api.RemoteActionBarCallback;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.util.List;
+
+public class RemoteActionBarCallbackAdapter extends ActionBarCallback {
+    private final RemoteActionBarCallback mDelegate;
+
+    public RemoteActionBarCallbackAdapter(@NotNull RemoteActionBarCallback remote) {
+        mDelegate = remote;
+    }
+
+    @Override
+    public List<String> getMenuIdNames() {
+        try {
+            return mDelegate.getMenuIdNames();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean getSplitActionBarWhenNarrow() {
+        try {
+            return mDelegate.getSplitActionBarWhenNarrow();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getNavigationMode() {
+        try {
+            return mDelegate.getNavigationMode();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getSubTitle() {
+        try {
+            return mDelegate.getSubTitle();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public HomeButtonStyle getHomeButtonStyle() {
+        try {
+            return mDelegate.getHomeButtonStyle();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isOverflowPopupNeeded() {
+        try {
+            return mDelegate.isOverflowPopupNeeded();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteAssetRepositoryAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteAssetRepositoryAdapter.java
new file mode 100644
index 0000000..490f829
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteAssetRepositoryAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.AssetRepository;
+import com.android.layout.remote.api.RemoteAssetRepository;
+import com.android.layout.remote.util.StreamUtil;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.rmi.RemoteException;
+
+public class RemoteAssetRepositoryAdapter extends AssetRepository {
+    private final RemoteAssetRepository mDelgate;
+
+    public RemoteAssetRepositoryAdapter(@NotNull RemoteAssetRepository remote) {
+        mDelgate = remote;
+    }
+
+    @Override
+    public boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    public InputStream openAsset(String path, int mode) throws IOException, RemoteException {
+        return StreamUtil.getInputStream(mDelgate.openAsset(path, mode));
+    }
+
+    @Override
+    public InputStream openNonAsset(int cookie, String path, int mode)
+            throws IOException, RemoteException {
+        return StreamUtil.getInputStream(mDelgate.openNonAsset(cookie, path, mode));
+    }
+}
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
new file mode 100644
index 0000000..b717feb
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteILayoutPullParserAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ILayoutPullParser;
+import com.android.layout.remote.api.RemoteILayoutPullParser;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+
+public class RemoteILayoutPullParserAdapter extends RemoteXmlPullParserAdapter
+        implements ILayoutPullParser {
+    public RemoteILayoutPullParserAdapter(@NotNull RemoteILayoutPullParser delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public Object getViewCookie() {
+        try {
+            return ((RemoteILayoutPullParser) mDelegate).getViewCookie();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ILayoutPullParser getParser(String layoutName) {
+        throw new UnsupportedOperationException();
+    }
+
+
+}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java
new file mode 100644
index 0000000..6878d46
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutLogAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * 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.LayoutLog;
+import com.android.layout.remote.api.RemoteLayoutLog;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+
+public class RemoteLayoutLogAdapter extends LayoutLog {
+    private final RemoteLayoutLog mLog;
+
+    public RemoteLayoutLogAdapter(@NotNull RemoteLayoutLog log) {
+        mLog = log;
+    }
+
+    @Override
+    public void warning(String tag, String message, Object data) {
+        try {
+            mLog.warning(tag, message, null);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void fidelityWarning(String tag, String message, Throwable throwable, Object viewCookie,
+            Object data) {
+        try {
+            mLog.fidelityWarning(tag, message, throwable, viewCookie, data);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void error(String tag, String message, Object data) {
+        try {
+            mLog.error(tag, message, null);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void error(String tag, String message, Throwable throwable, Object data) {
+        try {
+            mLog.error(tag, message, throwable, null);
+        } 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
new file mode 100644
index 0000000..b685098
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -0,0 +1,305 @@
+/*
+ * 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.ActionBarCallback;
+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;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.rmi.RemoteException;
+import java.util.HashMap;
+import java.util.function.Function;
+
+public class RemoteLayoutlibCallbackAdapter extends LayoutlibCallback {
+    private final RemoteLayoutlibCallback mDelegate;
+    private final PathClassLoader mPathClassLoader;
+
+    public RemoteLayoutlibCallbackAdapter(@NotNull RemoteLayoutlibCallback remote) {
+        mDelegate = remote;
+
+        // Views requested to this callback need to be brought from the "client" side.
+        // We transform any loadView into two operations:
+        //  - First we ask to where the class is located on disk via findClassPath
+        //  - Second, we instantiate the class in the "server" side
+        HashMap<String, Path> nameToPathCache = new HashMap<>();
+        Function<String, Path> getPathFromName = cacheName -> nameToPathCache.computeIfAbsent(
+                cacheName,
+                name -> {
+                    try {
+                        return mDelegate.findClassPath(name);
+                    } catch (RemoteException e) {
+                        throw new RuntimeException(e);
+                    }
+                });
+
+        mPathClassLoader = new PathClassLoader(getPathFromName);
+    }
+
+    @NotNull
+    private Object createNewInstance(@NotNull Class<?> clazz,
+            @Nullable Class<?>[] constructorSignature, @Nullable Object[] constructorParameters,
+            boolean isView)
+            throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException,
+            IllegalAccessException, InstantiationException {
+        Constructor<?> constructor = null;
+
+        try {
+            constructor = clazz.getConstructor(constructorSignature);
+        } catch (NoSuchMethodException e) {
+            if (!isView) {
+                throw e;
+            }
+
+            // View class has 1-parameter, 2-parameter and 3-parameter constructors
+
+            final int paramsCount = constructorSignature != null ? constructorSignature.length : 0;
+            if (paramsCount == 0) {
+                throw e;
+            }
+            assert constructorParameters != null;
+
+            for (int i = 3; i >= 1; i--) {
+                if (i == paramsCount) {
+                    continue;
+                }
+
+                final int k = paramsCount < i ? paramsCount : i;
+
+                final Class[] sig = new Class[i];
+                System.arraycopy(constructorSignature, 0, sig, 0, k);
+
+                final Object[] params = new Object[i];
+                System.arraycopy(constructorParameters, 0, params, 0, k);
+
+                for (int j = k + 1; j <= i; j++) {
+                    if (j == 2) {
+                        sig[j - 1] = findClass("android.util.AttributeSet");
+                        params[j - 1] = null;
+                    } else if (j == 3) {
+                        // parameter 3: int defstyle
+                        sig[j - 1] = int.class;
+                        params[j - 1] = 0;
+                    }
+                }
+
+                constructorSignature = sig;
+                constructorParameters = params;
+
+                try {
+                    constructor = clazz.getConstructor(constructorSignature);
+                    if (constructor != null) {
+                        if (constructorSignature.length < 2) {
+                            // TODO: Convert this to remote
+//                            LOG.info("wrong_constructor: Custom view " +
+//                                    clazz.getSimpleName() +
+//                                    " is not using the 2- or 3-argument " +
+//                                    "View constructors; XML attributes will not work");
+//                            mDelegate.warning("wrongconstructor", //$NON-NLS-1$
+//                                    String.format(
+//                                            "Custom view %1$s is not using the 2- or 3-argument
+// View constructors; XML attributes will not work",
+//                                            clazz.getSimpleName()), null, null);
+                        }
+                        break;
+                    }
+                } catch (NoSuchMethodException ignored) {
+                }
+            }
+
+            if (constructor == null) {
+                throw e;
+            }
+        }
+
+        constructor.setAccessible(true);
+        return constructor.newInstance(constructorParameters);
+    }
+
+    @Override
+    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+            throws Exception {
+        Class<?> viewClass = MockView.class;
+        try {
+            viewClass = findClass(name);
+        } catch (ClassNotFoundException ignore) {
+            // MockView will be used instead
+        }
+        return createNewInstance(viewClass, constructorSignature, constructorArgs, true);
+    }
+
+    @Override
+    public String getNamespace() {
+        try {
+            return mDelegate.getNamespace();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @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) {
+        try {
+            return mDelegate.resolveResourceId(id);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Integer getResourceId(ResourceType type, String name) {
+        try {
+            return mDelegate.getResourceId(type, name);
+        } 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));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+            ResourceReference itemRef, int fullPosition, int positionPerType,
+            int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
+            ViewAttribute viewAttribute, Object defaultValue) {
+        return null;
+    }
+
+    @Override
+    public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
+            Object viewObject) {
+        return null;
+    }
+
+    @Override
+    public ActionBarCallback getActionBarCallback() {
+        try {
+            return new RemoteActionBarCallbackAdapter(mDelegate.getActionBarCallback());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Object loadClass(String name, Class[] constructorSignature, Object[] constructorArgs)
+            throws ClassNotFoundException {
+        return super.loadClass(name, constructorSignature, constructorArgs);
+    }
+
+    @Override
+    public boolean supports(int ideFeature) {
+        try {
+            return mDelegate.supports(ideFeature);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public <T> T getFlag(Key<T> key) {
+        return super.getFlag(key);
+    }
+
+    @Override
+    public ParserFactory getParserFactory() {
+        try {
+            return new RemoteParserFactoryAdapter(mDelegate.getParserFactory());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Class<?> findClass(String name) throws ClassNotFoundException {
+        return mPathClassLoader.loadClass(name);
+    }
+
+    @Override
+    public XmlPullParser getXmlFileParser(String fileName) {
+        try {
+            return new RemoteXmlPullParserAdapter(mDelegate.getXmlFileParser(fileName));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Simple class loaders that loads classes from Paths
+     */
+    private static class PathClassLoader extends ClassLoader {
+        private final Function<String, Path> mGetPath;
+
+        private PathClassLoader(@NotNull Function<String, Path> getUrl) {
+            mGetPath = getUrl;
+        }
+
+        @Override
+        protected Class<?> findClass(@NotNull String name) throws ClassNotFoundException {
+            Path path = mGetPath.apply(name);
+
+            if (path != null) {
+                try {
+                    byte[] content = Files.readAllBytes(path);
+                    return defineClass(name, content, 0, content.length);
+                } catch (IOException ignore) {
+                }
+            }
+
+            throw new ClassNotFoundException(name);
+        }
+    }
+}
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
new file mode 100644
index 0000000..f4ce421
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteParserFactoryAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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
new file mode 100644
index 0000000..3f261df
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderResourcesAdapter.java
@@ -0,0 +1,192 @@
+/*
+ * 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.LayoutLog;
+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.StyleResourceValue;
+import com.android.layout.remote.api.RemoteRenderResources;
+import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.util.List;
+
+public class RemoteRenderResourcesAdapter extends RenderResources {
+    private final RemoteRenderResources mDelegate;
+
+    public RemoteRenderResourcesAdapter(@NotNull RemoteRenderResources remoteRenderResources) {
+        mDelegate = remoteRenderResources;
+    }
+
+    @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();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void applyStyle(StyleResourceValue theme, boolean useAsPrimary) {
+        try {
+            mDelegate.applyStyle(theme, useAsPrimary);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void clearStyles() {
+        try {
+            mDelegate.clearStyles();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @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);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ResourceValue findItemInTheme(ResourceReference attr) {
+        try {
+            return mDelegate.findItemInTheme(attr);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ResourceValue findItemInStyle(StyleResourceValue style, ResourceReference attr) {
+        try {
+            return mDelegate.findItemInStyle(style, attr);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ResourceValue dereference(ResourceValue resourceValue) {
+        try {
+            return mDelegate.dereference(resourceValue);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Returns a resource by namespace, type and name. The returned resource is unresolved. */
+    @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);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public ResourceValue resolveResValue(ResourceValue value) {
+        try {
+            return mDelegate.resolveValue(value);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public StyleResourceValue getParent(StyleResourceValue style) {
+        try {
+            return mDelegate.getParent(style);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderSessionAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderSessionAdapter.java
new file mode 100644
index 0000000..9d292e7
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteRenderSessionAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * 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.RenderSession;
+import com.android.ide.common.rendering.api.Result;
+import com.android.layout.remote.api.RemoteRenderSession;
+import com.android.layout.remote.util.SerializableImage;
+import com.android.layout.remote.util.SerializableImageImpl;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RemoteRenderSessionAdapter implements RemoteRenderSession {
+    private final RenderSession mDelegate;
+
+    private RemoteRenderSessionAdapter(@NotNull RenderSession delegate) {
+        mDelegate = delegate;
+    }
+
+    public static RemoteRenderSession create(@NotNull RenderSession delegate)
+            throws RemoteException {
+        return (RemoteRenderSession) UnicastRemoteObject.exportObject(
+                new RemoteRenderSessionAdapter(delegate), 0);
+    }
+
+    @NotNull
+    @Override
+    public Result getResult() throws RemoteException {
+        return mDelegate.getResult();
+    }
+
+    @Override
+    public Result render(long timeout, boolean forceMeasure) {
+        return mDelegate.render(timeout, forceMeasure);
+    }
+
+    @NotNull
+    @Override
+    public SerializableImage getSerializableImage() throws RemoteException {
+        return new SerializableImageImpl(mDelegate.getImage());
+    }
+
+    @Override
+    public void setSystemTimeNanos(long nanos) {
+        mDelegate.setSystemTimeNanos(nanos);
+    }
+
+    @Override
+    public void setSystemBootTimeNanos(long nanos) {
+        mDelegate.setSystemBootTimeNanos(nanos);
+    }
+
+    @Override
+    public void setElapsedFrameTimeNanos(long nanos) {
+        mDelegate.setElapsedFrameTimeNanos(nanos);
+    }
+
+    @Override
+    public void dispose() {
+        mDelegate.dispose();
+    }
+}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteXmlPullParserAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteXmlPullParserAdapter.java
new file mode 100644
index 0000000..6de03bb
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteXmlPullParserAdapter.java
@@ -0,0 +1,353 @@
+/*
+ * 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.layout.remote.api.RemoteXmlPullParser;
+import com.android.layout.remote.util.RemoteInputStreamAdapter;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.rmi.RemoteException;
+
+class RemoteXmlPullParserAdapter implements XmlPullParser {
+    protected RemoteXmlPullParser mDelegate;
+
+    RemoteXmlPullParserAdapter(@NotNull RemoteXmlPullParser remote) {
+        mDelegate = remote;
+    }
+
+    @Override
+    public void setFeature(String name, boolean state) throws XmlPullParserException {
+        try {
+            mDelegate.setFeature(name, state);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean getFeature(String name) {
+        try {
+            return mDelegate.getFeature(name);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setProperty(String name, Object value) throws XmlPullParserException {
+        try {
+            mDelegate.setProperty(name, value);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Object getProperty(String name) {
+        try {
+            return mDelegate.getProperty(name);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setInput(Reader in) throws XmlPullParserException {
+        try {
+            mDelegate.setInput(in);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setInput(InputStream inputStream, String inputEncoding)
+            throws XmlPullParserException {
+        try {
+            mDelegate.setInput(RemoteInputStreamAdapter.create(inputStream), inputEncoding);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getInputEncoding() {
+        try {
+            return mDelegate.getInputEncoding();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void defineEntityReplacementText(String entityName, String replacementText)
+            throws XmlPullParserException {
+        try {
+            mDelegate.defineEntityReplacementText(entityName, replacementText);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getNamespaceCount(int depth) throws XmlPullParserException {
+        try {
+            return mDelegate.getNamespaceCount(depth);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getNamespacePrefix(int pos) throws XmlPullParserException {
+        try {
+            return mDelegate.getNamespacePrefix(pos);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getNamespaceUri(int pos) throws XmlPullParserException {
+        try {
+            return mDelegate.getNamespaceUri(pos);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getNamespace(String prefix) {
+        try {
+            return mDelegate.getNamespace(prefix);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getDepth() {
+        try {
+            return mDelegate.getDepth();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getPositionDescription() {
+        try {
+            return mDelegate.getPositionDescription();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getLineNumber() {
+        try {
+            return mDelegate.getLineNumber();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getColumnNumber() {
+        try {
+            return mDelegate.getColumnNumber();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isWhitespace() throws XmlPullParserException {
+        try {
+            return mDelegate.isWhitespace();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getText() {
+        try {
+            return mDelegate.getText();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public char[] getTextCharacters(int[] holderForStartAndLength) {
+        try {
+            return mDelegate.getTextCharacters(holderForStartAndLength);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getNamespace() {
+        try {
+            return mDelegate.getNamespace();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getName() {
+        try {
+            return mDelegate.getName();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getPrefix() {
+        try {
+            return mDelegate.getPrefix();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isEmptyElementTag() throws XmlPullParserException {
+        try {
+            return mDelegate.isEmptyElementTag();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getAttributeCount() {
+        try {
+            return mDelegate.getAttributeCount();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getAttributeNamespace(int index) {
+        try {
+            return mDelegate.getAttributeNamespace(index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getAttributeName(int index) {
+        try {
+            return mDelegate.getAttributeName(index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getAttributePrefix(int index) {
+        try {
+            return mDelegate.getAttributePrefix(index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getAttributeType(int index) {
+        try {
+            return mDelegate.getAttributeType(index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isAttributeDefault(int index) {
+        try {
+            return mDelegate.isAttributeDefault(index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getAttributeValue(int index) {
+        try {
+            return mDelegate.getAttributeValue(index);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getAttributeValue(String namespace, String name) {
+        try {
+            return mDelegate.getAttributeValue(namespace, name);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int getEventType() throws XmlPullParserException {
+        try {
+            return mDelegate.getEventType();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public int next() throws XmlPullParserException, IOException {
+        return mDelegate.next();
+    }
+
+    @Override
+    public int nextToken() throws XmlPullParserException, IOException {
+        return mDelegate.nextToken();
+    }
+
+    @Override
+    public void require(int type, String namespace, String name)
+            throws XmlPullParserException, IOException {
+        mDelegate.require(type, namespace, name);
+    }
+
+    @Override
+    public String nextText() throws XmlPullParserException, IOException {
+        return mDelegate.nextText();
+    }
+
+    @Override
+    public int nextTag() throws XmlPullParserException, IOException {
+        return mDelegate.nextTag();
+    }
+}
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/package-info.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/package-info.java
new file mode 100644
index 0000000..2491759
--- /dev/null
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Package containing all the server side adapters. These adapters have the mission of receiving
+ * local calls and translating them into the remote API.
+ */
+package com.android.layoutlib.bridge.remote.server.adapters;
\ No newline at end of file
diff --git a/remote/tests/out/failures/activity.png b/remote/tests/out/failures/activity.png
new file mode 100644
index 0000000..2920b7d
--- /dev/null
+++ b/remote/tests/out/failures/activity.png
Binary files differ
diff --git a/remote/tests/out/failures/delta-activity.png b/remote/tests/out/failures/delta-activity.png
new file mode 100644
index 0000000..32d9415
--- /dev/null
+++ b/remote/tests/out/failures/delta-activity.png
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
new file mode 100644
index 0000000..ddb7f64
--- /dev/null
+++ b/remote/tests/out/failures/delta-remote_component_load.png
Binary files differ
diff --git a/remote/tests/out/failures/remote_component_load.png b/remote/tests/out/failures/remote_component_load.png
new file mode 100644
index 0000000..0ed85d1
--- /dev/null
+++ b/remote/tests/out/failures/remote_component_load.png
Binary files differ
diff --git a/remote/tests/remote tests.iml b/remote/tests/remote tests.iml
new file mode 100644
index 0000000..de0a53a
--- /dev/null
+++ b/remote/tests/remote tests.iml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="true" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="remote client" scope="TEST" />
+    <orderEntry type="module" module-name="bridge" scope="TEST" />
+    <orderEntry type="library" scope="TEST" name="layoutlib_api-prebuilt" level="project" />
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/sdk-common/sdk-common-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <ANNOTATIONS>
+          <root url="file://$MODULE_DIR$/../.." />
+        </ANNOTATIONS>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <ANNOTATIONS>
+          <root url="file://$MODULE_DIR$/../.." />
+        </ANNOTATIONS>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/sdk-common/sdk-common-sources.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="library" scope="TEST" name="framework.jar" level="project" />
+    <orderEntry type="module-library" scope="TEST">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../../../../libcore/xml/src/main/java" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="library" scope="TEST" name="junit" level="project" />
+    <orderEntry type="module" module-name="remote server" scope="TEST" />
+    <orderEntry type="module" module-name="remote common" scope="TEST" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/remote/tests/src/CustomComponent.java b/remote/tests/src/CustomComponent.java
new file mode 100644
index 0000000..6d6272d
--- /dev/null
+++ b/remote/tests/src/CustomComponent.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+@SuppressWarnings("unused") // Used by test
+public class CustomComponent extends TextView {
+    public CustomComponent(Context context) {
+        super(context);
+
+        init();
+    }
+
+    public CustomComponent(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        init();
+    }
+
+    public CustomComponent(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        init();
+    }
+
+    public CustomComponent(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        init();
+    }
+
+    private void init() {
+        setText("CustomComponent text");
+        setBackgroundColor(Color.RED);
+        setTextColor(Color.WHITE);
+        setTextSize(40f);
+    }
+}
diff --git a/remote/tests/src/RemoteBridgeTest.java b/remote/tests/src/RemoteBridgeTest.java
new file mode 100644
index 0000000..e0b0610
--- /dev/null
+++ b/remote/tests/src/RemoteBridgeTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+import com.android.ide.common.rendering.api.Bridge;
+import com.android.ide.common.rendering.api.Result;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.intensive.RenderResult;
+import com.android.layoutlib.bridge.intensive.RenderTestBase;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
+import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
+import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.layoutlib.bridge.intensive.util.ImageUtils;
+import com.android.layoutlib.bridge.remote.client.RemoteBridgeClient;
+import com.android.layoutlib.bridge.remote.server.ServerMain;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.NotBoundException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+
+public class RemoteBridgeTest extends RenderTestBase {
+    private ServerMain mServerMain;
+    private RemoteBridgeClient mClient;
+
+    /**
+     * Copy of RenderTestBase.renderAndVerify that allows using a different Bridge. TODO: Merge back
+     * into RenderTestBase
+     */
+    protected static RenderResult renderAndVerify(Bridge bridge, SessionParams params,
+            String goldenFileName, long frameTimeNanos) throws ClassNotFoundException {
+        RenderResult result = RenderTestBase.render(bridge, params, frameTimeNanos);
+        try {
+            String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenFileName;
+            assertNotNull(result.getImage());
+            ImageUtils.requireSimilar(goldenImagePath, result.getImage());
+        } catch (IOException e) {
+            getLogger().error(e, e.getMessage());
+        }
+
+        return result;
+    }
+
+    @Before
+    public void setupServer() throws IOException, NotBoundException, InterruptedException {
+        mServerMain = ServerMain.forkAndStartServer(ServerMain.REGISTRY_BASE_PORT, 10);
+        mClient = RemoteBridgeClient.getRemoteBridge(mServerMain.getPort());
+
+        File data_dir = new File(PLATFORM_DIR, "data");
+        File res = new File(data_dir, "res");
+        File fontLocation = new File(data_dir, "fonts");
+        File buildProp = new File(PLATFORM_DIR, "build.prop");
+        File attrs = new File(res, "values" + File.separator + "attrs.xml");
+
+        mClient.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
+                ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+    }
+
+    @After
+    public void stopServer() {
+        mClient.dispose();
+        mServerMain.stop();
+    }
+
+    /**
+     * Same test as RenderTest#testActivity but using the remote bridge
+     */
+    @Test
+    public void testActivity() throws IOException, ClassNotFoundException {
+        SessionParams params = createSessionParams("activity.xml", ConfigGenerator.NEXUS_5);
+        RenderResult result = renderAndVerify(mClient, params, "activity.png", 250);
+        assertEquals(Result.Status.SUCCESS, result.getResult().getStatus());
+        if (result.getResult().getException() != null) {
+            result.getResult().getException().printStackTrace();
+            fail("Unexpected exception");
+        }
+    }
+
+    /**
+     * Same test as RenderTest#testActivity but using the remote bridge
+     */
+    @Test
+    public void testCustomClassLoading() throws ClassNotFoundException {
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        LayoutPullParser parser = LayoutPullParser.createFromString(
+                "<CustomComponent xmlns:android=\"http://schemas" +
+                        ".android.com/apk/res/android\"\n" +
+                        "                android:layout_width=\"match_parent\"\n" +
+                        "                android:layout_height=\"match_parent\"\n>" +
+                        "</CustomComponent>");
+        SessionParams params =
+                getSessionParamsBuilder().setParser(parser).setCallback(layoutLibCallback).setTheme(
+                        "Theme.NoTitleBar", false).build();
+
+        RenderResult result = renderAndVerify(mClient, params, "remote_component_load.png", 250);
+        assertEquals(Result.Status.SUCCESS, result.getResult().getStatus());
+        if (result.getResult().getException() != null) {
+            result.getResult().getException().printStackTrace();
+            fail("Unexpected exception");
+        }
+
+        parser = LayoutPullParser.createFromString(
+                "<MissingCustomComponent xmlns:android=\"http://schemas" +
+                        ".android.com/apk/res/android\"\n" +
+                        "                android:layout_width=\"match_parent\"\n" +
+                        "                android:layout_height=\"match_parent\"\n>" +
+                        "</MissingCustomComponent>");
+        params =
+                getSessionParamsBuilder().setParser(parser).setCallback(layoutLibCallback).setTheme(
+                        "Theme.NoTitleBar", false).build();
+        result = renderAndVerify(mClient, params, "remote_component_load_fail.png", 250);
+        assertEquals(Result.Status.SUCCESS, result.getResult().getStatus());
+        if (result.getResult().getException() != null) {
+            result.getResult().getException().printStackTrace();
+            fail("Unexpected exception");
+        }
+    }
+}
\ No newline at end of file
diff --git a/rename_font/build_font_single.py b/rename_font/build_font_single.py
index 4245cdc..4678a4f 100755
--- a/rename_font/build_font_single.py
+++ b/rename_font/build_font_single.py
@@ -17,8 +17,8 @@
 """
 Rename the PS name of the input font.
 
-OpenType fonts (*.otf) are not currently supported. They are copied to the destination without renaming.
-XML files are also copied in case they are passed there by mistake.
+OpenType fonts (*.otf) and TrueType Collections (*.ttc) are not currently supported. They are copied to the destination
+without renaming. XML files are also copied in case they are passed there by mistake.
 
 Usage: build_font_single.py /path/to/input_font.ttf /path/to/output_font.ttf
 
@@ -63,7 +63,7 @@
 NAMEID_VERSION = 5
 
 # A list of extensions to process.
-EXTENSIONS = ['.ttf', '.otf', '.xml']
+EXTENSIONS = ['.ttf', '.ttc', '.otf', '.xml']
 
 def main(argv):
   if len(argv) < 2: