Merge Studio 2.2.2
diff --git a/adt-branding/src/idea/AndroidStudioApplicationInfo.xml b/adt-branding/src/idea/AndroidStudioApplicationInfo.xml
index 6da2e22..2a390c6 100755
--- a/adt-branding/src/idea/AndroidStudioApplicationInfo.xml
+++ b/adt-branding/src/idea/AndroidStudioApplicationInfo.xml
@@ -14,7 +14,7 @@
   ~ limitations under the License.
   -->
 <component>
-  <version major="2" minor="2" micro="0" patch="12" full="{0}.{1}" eap="false" />
+  <version major="2" minor="2" micro="2" patch="0" full="{0}.{1}.{2}" eap="false" />
   <company name="Google" url="http://developer.android.com"/>
   <build number="__BUILD_NUMBER__" date="__BUILD_DATE__" apiVersion="AI-145.1617.8"/>
   <install-over minbuild="0.1" maxbuild="999.999999" version="0"/>
diff --git a/android/guiTestSrc/com/android/tools/idea/tests/gui/layout/NewProjectTest.java b/android/guiTestSrc/com/android/tools/idea/tests/gui/layout/NewProjectTest.java
index f6c5b76..377ca37 100755
--- a/android/guiTestSrc/com/android/tools/idea/tests/gui/layout/NewProjectTest.java
+++ b/android/guiTestSrc/com/android/tools/idea/tests/gui/layout/NewProjectTest.java
@@ -150,7 +150,7 @@
 
       // This warning is wrong: http://b.android.com/192605
       "    Android > Lint > Usability",
-      "        Missing support for Google App Indexing",
+      "        Missing support for Firebase App Indexing",
       "            app",
       "                App is not indexable by Google Search; consider adding at least one Activity with an ACTION-VIEW intent filter. See issue explanation for more details."));
   }
diff --git a/android/resources/messages/AndroidBundle.properties b/android/resources/messages/AndroidBundle.properties
index c5059ef..8d6020d 100644
--- a/android/resources/messages/AndroidBundle.properties
+++ b/android/resources/messages/AndroidBundle.properties
@@ -453,9 +453,9 @@
 android.lint.inspections.full.backup.content=Valid Full Backup Content File
 android.lint.inspections.get.instance=Cipher.getInstance with ECB
 android.lint.inspections.gif.usage=Using .gif format for bitmaps is discouraged
-android.lint.inspections.google.app.indexing.api.warning=Missing support for Google App Indexing Api
-android.lint.inspections.google.app.indexing.url.error=URL not supported by app for Google App Indexing
-android.lint.inspections.google.app.indexing.warning=Missing support for Google App Indexing
+android.lint.inspections.google.app.indexing.api.warning=Missing support for Firebase App Indexing Api
+android.lint.inspections.google.app.indexing.url.error=URL not supported by app for Firebase App Indexing
+android.lint.inspections.google.app.indexing.warning=Missing support for Firebase App Indexing
 android.lint.inspections.gradle.compatible=Incompatible Gradle Versions
 android.lint.inspections.gradle.dependency=Obsolete Gradle Dependency
 android.lint.inspections.gradle.deprecated=Deprecated Gradle Construct
@@ -787,7 +787,7 @@
 instant.run.notification.ir.disabled.for.current.variant=Instant Run is disabled for current variant.<br>\
   Some settings (e.g. using jack) are currently not compatible with Instant Run. <a href="learnmore">Learn more</a>.
 instant.run.notification.ir.disabled.multidex.requires.21=Instant Run does not support deploying build variants with multidex enabled, to a target with API level 20 or below.<br>\
-  To use Instant Run with a multidex enabled build variant, deploy to a target with API level 21 or higher.");
+  To use Instant Run with a multidex enabled build variant, deploy to a target with API level 21 or higher.
 instant.run.notification.ir.disabled.multiple.devices=Instant Run is disabled:<br>\
   Instant Run does not support deploying to multiple targets.<br>\
   To enable Instant Run, deploy to a single target.
@@ -835,4 +835,30 @@
   You may need to <a href="restart">restart</a>{0} the current activity to see the changes.\n\
   You can also <a href="configure">configure</a> Instant Run to restart activities automatically.
 
-instant.run.notification.nochanges=No changes to deploy.
\ No newline at end of file
+instant.run.notification.nochanges=No changes to deploy.
+
+instant.run.flr.banner.subtitle=<html>\
+  We want to make Instant Run perfect, but we need more info about your project to investigate issues.<br>\
+  Please help us troubleshoot and fix Instant Run issues by doing the following:<br>\
+  <ol>\
+    <li>Re-enable Instant Run and activate extra logging</li>\
+    <li>Reproduce the Instant Run issue</li>\
+    <li>Immediately after reproducing the issue, click <b> Help | Report Instant Run Issue... </b> to send us the issue report.</li>\
+  </ol>\
+  </html>
+
+instant.run.flr.dialog.description=Please describe your Instant Run Issue:
+instant.run.flr.dialog.includeslogs=<html>To help Google troubleshoot and fix Instant Run issues, the following log files need to be sent<br>\
+  along with this issue report. These logs contain project details that may include personally<br>\
+  identifiable information (e.g. project paths on disk).<br><br>\
+  These logs will only be viewable by Google, and will be used for the sole purpose of troubleshooting<br>\
+  Instant Run issues.</html>
+instant.run.flr.would.you.like.to.enable=In order to report an Instant Run issue to Google, you need to first enable Instant Run with extra logging.\n\
+  Would you like to help Google troubleshoot and fix Instant Run issues?
+instant.run.flr.dialog.title=Report Instant Run Issue
+instant.run.flr.howto=<html>Thank you! We have now enabled Instant Run and extra logging to diagnose issues.<br>\
+  Next time you hit an Instant Run issue, you can click on <b> Help | Report Instant Run Issue... </b><br>\
+  to send us an error report.<br><br>\
+  If you change your mind, you can disable extra logging for Instant Run by going to <br>\
+  <b>Settings | Build, Execution, Deployment | Instant Run</b><br>\
+  and uncheck 'Log extra info for Instant Run'</html>
diff --git a/android/src/META-INF/androidstudio.xml b/android/src/META-INF/androidstudio.xml
index 7bde7ca..3da850e 100755
--- a/android/src/META-INF/androidstudio.xml
+++ b/android/src/META-INF/androidstudio.xml
@@ -39,6 +39,10 @@
       <add-to-group group-id="ToolbarRunGroup" anchor="before" relative-to-action="Stop" />
     </action>
 
+    <action id="Android.InstantRunFeedback" class="com.android.tools.idea.fd.actions.SubmitFeedback">
+      <add-to-group group-id="HelpMenu" anchor="after" relative-to-action="SendFeedback" />
+    </action>
+
     <action id="AndroidAddRTLSupport" class="com.android.tools.idea.actions.AndroidAddRtlSupportAction"
             text="Add RTL Support Where Possible..." description="Add right-to-left (RTL) support where possible">
       <add-to-group group-id="RefactoringMenu"/>
diff --git a/android/src/META-INF/plugin.xml b/android/src/META-INF/plugin.xml
index cdbbe9f..1922533 100755
--- a/android/src/META-INF/plugin.xml
+++ b/android/src/META-INF/plugin.xml
@@ -4,7 +4,7 @@
   <description>
     Supports the development of Open Handset Alliance Android applications with IntelliJ IDEA.
   </description>
-  <version>10.2.2</version>
+  <version>10.2.2.2</version>
   <vendor>JetBrains</vendor>
 
   <depends>JUnit</depends>
@@ -568,9 +568,9 @@
     <globalInspection hasStaticDescription="true" shortName="AndroidLintFullBackupContent" displayName="Valid Full Backup Content File" groupKey="android.lint.inspections.group.name.correctness" bundle="messages.AndroidBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintFullBackupContentInspection"/>
     <globalInspection hasStaticDescription="true" shortName="AndroidLintGetInstance" displayName="Cipher.getInstance with ECB" groupKey="android.lint.inspections.group.name.security" bundle="messages.AndroidBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGetInstanceInspection"/>
     <globalInspection hasStaticDescription="true" shortName="AndroidLintGifUsage" displayName="Using .gif format for bitmaps is discouraged" groupKey="android.lint.inspections.group.name.usability.icons" bundle="messages.AndroidBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGifUsageInspection"/>
-    <globalInspection hasStaticDescription="true" shortName="AndroidLintGoogleAppIndexingApiWarning" displayName="Missing support for Google App Indexing Api" groupKey="android.lint.inspections.group.name.usability" bundle="messages.AndroidBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGoogleAppIndexingApiWarningInspection"/>
-    <globalInspection hasStaticDescription="true" shortName="AndroidLintGoogleAppIndexingUrlError" displayName="URL not supported by app for Google App Indexing" groupKey="android.lint.inspections.group.name.usability" bundle="messages.AndroidBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGoogleAppIndexingUrlErrorInspection"/>
-    <globalInspection hasStaticDescription="true" shortName="AndroidLintGoogleAppIndexingWarning" displayName="Missing support for Google App Indexing" groupKey="android.lint.inspections.group.name.usability" bundle="messages.AndroidBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGoogleAppIndexingWarningInspection"/>
+    <globalInspection hasStaticDescription="true" shortName="AndroidLintGoogleAppIndexingApiWarning" displayName="Missing support for Firebase App Indexing Api" groupKey="android.lint.inspections.group.name.usability" bundle="messages.AndroidBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGoogleAppIndexingApiWarningInspection"/>
+    <globalInspection hasStaticDescription="true" shortName="AndroidLintGoogleAppIndexingUrlError" displayName="URL not supported by app for Firebase App Indexing" groupKey="android.lint.inspections.group.name.usability" bundle="messages.AndroidBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGoogleAppIndexingUrlErrorInspection"/>
+    <globalInspection hasStaticDescription="true" shortName="AndroidLintGoogleAppIndexingWarning" displayName="Missing support for Firebase App Indexing" groupKey="android.lint.inspections.group.name.usability" bundle="messages.AndroidBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGoogleAppIndexingWarningInspection"/>
     <globalInspection hasStaticDescription="true" shortName="AndroidLintGradleCompatible" displayName="Incompatible Gradle Versions" groupKey="android.lint.inspections.group.name.correctness" bundle="messages.AndroidBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGradleCompatibleInspection"/>
     <globalInspection hasStaticDescription="true" shortName="AndroidLintGradleDependency" displayName="Obsolete Gradle Dependency" groupKey="android.lint.inspections.group.name.correctness" bundle="messages.AndroidBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGradleDependencyInspection"/>
     <globalInspection hasStaticDescription="true" shortName="AndroidLintGradleDeprecated" displayName="Deprecated Gradle Construct" groupKey="android.lint.inspections.group.name.correctness" bundle="messages.AndroidBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.lint.AndroidLintInspectionToolProvider$AndroidLintGradleDeprecatedInspection"/>
@@ -832,6 +832,7 @@
     <projectService serviceImplementation="com.android.tools.idea.gradle.invoker.messages.GradleBuildTreeViewConfiguration"/>
     <projectService serviceImplementation="com.android.tools.idea.run.DevicePickerStateService" />
     <projectService serviceImplementation="com.android.tools.idea.fd.InstantRunStatsService" />
+    <projectService serviceImplementation="com.android.tools.idea.fd.FlightRecorder" />
 
     <projectService serviceInterface="com.android.tools.idea.gradle.compiler.AndroidGradleBuildConfiguration"
                     serviceImplementation="com.android.tools.idea.gradle.compiler.AndroidGradleBuildConfiguration"/>
@@ -1109,6 +1110,7 @@
     <java.elementFinder implementation="com.android.tools.idea.databinding.BrClassFinder" id="dataBinding.BrClassFinder" order="first, before java"/>
     <java.elementFinder implementation="com.android.tools.idea.databinding.DataBindingClassFinder" id="dataBinding.BindingClassFinder" order="first, before java"/>
     <java.elementFinder implementation="com.android.tools.idea.databinding.DataBindingComponentClassFinder" id="dataBinding.ComponentClassFinder" order="first, before java"/>
+    <java.elementFinder implementation="com.android.tools.idea.databinding.DataBindingPackageFinder" id="dataBinding.DataBindingPackageFinder" order="last, after java"/>
     <psi.referenceContributor implementation="com.android.tools.idea.lang.databinding.DataBindingXmlReferenceContributor"/>
     <completion.contributor implementationClass="com.android.tools.idea.lang.databinding.DataBindingCompletionContributor" language="AndroidDataBinding"/>
   </extensions>
diff --git a/android/src/com/android/tools/idea/apk/viewer/ApkFileSystem.java b/android/src/com/android/tools/idea/apk/viewer/ApkFileSystem.java
index a68b0d0..55acc5b 100644
--- a/android/src/com/android/tools/idea/apk/viewer/ApkFileSystem.java
+++ b/android/src/com/android/tools/idea/apk/viewer/ApkFileSystem.java
@@ -28,7 +28,7 @@
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import com.intellij.openapi.vfs.impl.ArchiveHandler;
-import com.intellij.openapi.vfs.impl.ZipHandler;
+import com.intellij.openapi.vfs.impl.jar.JarHandler;
 import com.intellij.openapi.vfs.newvfs.ArchiveFileSystem;
 import com.intellij.openapi.vfs.newvfs.VfsImplUtil;
 import com.intellij.util.io.URLUtil;
@@ -76,7 +76,7 @@
   @NotNull
   @Override
   protected ArchiveHandler getHandler(@NotNull VirtualFile entryFile) {
-    return VfsImplUtil.getHandler(this, entryFile, ZipHandler::new);
+    return VfsImplUtil.getHandler(this, entryFile, JarHandler::new);
   }
 
   /**
diff --git a/android/src/com/android/tools/idea/databinding/BrClassFinder.java b/android/src/com/android/tools/idea/databinding/BrClassFinder.java
index 8bd76fc..fa14cf0 100644
--- a/android/src/com/android/tools/idea/databinding/BrClassFinder.java
+++ b/android/src/com/android/tools/idea/databinding/BrClassFinder.java
@@ -89,15 +89,7 @@
   @Nullable
   @Override
   public PsiPackage findPackage(@NotNull String qualifiedName) {
-    if (!myComponent.hasAnyDataBindingEnabledFacet()) {
-      return null;
-    }
-    for (AndroidFacet facet : myComponent.getDataBindingEnabledFacets()) {
-      String generatedPackageName = DataBindingUtil.getGeneratedPackageName(facet);
-      if (generatedPackageName.equals(qualifiedName)) {
-        return myComponent.getOrCreateDataBindingPsiPackage(generatedPackageName);
-      }
-    }
+    // DO NOT find package. BR package is the same as R and it always exists
     return null;
   }
 }
diff --git a/android/src/com/android/tools/idea/databinding/DataBindingClassFinder.java b/android/src/com/android/tools/idea/databinding/DataBindingClassFinder.java
index a55ac4a..866f19a 100644
--- a/android/src/com/android/tools/idea/databinding/DataBindingClassFinder.java
+++ b/android/src/com/android/tools/idea/databinding/DataBindingClassFinder.java
@@ -17,75 +17,23 @@
 
 import com.android.tools.idea.res.DataBindingInfo;
 import com.android.tools.idea.res.LocalResourceRepository;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElementFinder;
 import com.intellij.psi.PsiPackage;
 import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.psi.util.CachedValue;
-import com.intellij.psi.util.CachedValuesManager;
 import org.jetbrains.android.facet.AndroidFacet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.Collections;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * PsiElementFinder extensions that finds classes generated for layout files.
  */
 public class DataBindingClassFinder extends PsiElementFinder {
-  private final CachedValue<Map<String, PsiPackage>> myPackageCache;
   private final DataBindingProjectComponent myComponent;
   public DataBindingClassFinder(DataBindingProjectComponent component) {
     myComponent = component;
-    myPackageCache = CachedValuesManager.getManager(myComponent.getProject()).createCachedValue(
-      new ProjectResourceCachedValueProvider<Map<String, PsiPackage>, Set<String>>(myComponent) {
-
-        @NotNull
-        @Override
-        protected Map<String, PsiPackage> merge(List<Set<String>> results) {
-          Map<String, PsiPackage> merged = Maps.newHashMap();
-          for (Set<String> result : results) {
-            for (String qualifiedPackage : result) {
-              if (!merged.containsKey(qualifiedPackage)) {
-                merged.put(qualifiedPackage, myComponent.getOrCreateDataBindingPsiPackage(qualifiedPackage));
-              }
-            }
-          }
-          return merged;
-        }
-
-        @Override
-        ResourceCacheValueProvider<Set<String>> createCacheProvider(AndroidFacet facet) {
-          return new ResourceCacheValueProvider<Set<String>>(facet) {
-            @Override
-            Set<String> doCompute() {
-              LocalResourceRepository moduleResources = getFacet().getModuleResources(true);
-              if (moduleResources == null) {
-                return Collections.emptySet();
-              }
-              Map<String, DataBindingInfo> dataBindingResourceFiles = moduleResources.getDataBindingResourceFiles();
-              if (dataBindingResourceFiles == null) {
-                return Collections.emptySet();
-              }
-              Set<String> result = Sets.newHashSet();
-              for (DataBindingInfo info : dataBindingResourceFiles.values()) {
-                result.add(info.getPackageName());
-              }
-              return result;
-            }
-
-            @Override
-            Set<String> defaultValue() {
-              return Collections.emptySet();
-            }
-          };
-        }
-      }, false);
   }
 
   @Nullable
@@ -128,9 +76,8 @@
   @Nullable
   @Override
   public PsiPackage findPackage(@NotNull String qualifiedName) {
-    if (!myComponent.hasAnyDataBindingEnabledFacet()) {
-      return null;
-    }
-    return myPackageCache.getValue().get(qualifiedName);
+    // data binding packages are found only if corresponding java packages does not exists. For those, we have DataBindingPackageFinder
+    // which has a low priority.
+    return null;
   }
 }
diff --git a/android/src/com/android/tools/idea/databinding/DataBindingPackageFinder.java b/android/src/com/android/tools/idea/databinding/DataBindingPackageFinder.java
new file mode 100644
index 0000000..1aba538
--- /dev/null
+++ b/android/src/com/android/tools/idea/databinding/DataBindingPackageFinder.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.databinding;
+
+import com.android.tools.idea.res.DataBindingInfo;
+import com.android.tools.idea.res.LocalResourceRepository;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElementFinder;
+import com.intellij.psi.PsiPackage;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.CachedValue;
+import com.intellij.psi.util.CachedValuesManager;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This element finder has minimum priority and only finds packages that are missing in the app.
+ * See {@link DataBindingClassFinder}, {@link DataBindingComponentClassFinder} and {@link BrClassFinder} for actual classes.
+ */
+public class DataBindingPackageFinder extends PsiElementFinder {
+  private final DataBindingProjectComponent myComponent;
+  private final CachedValue<Map<String, PsiPackage>> myPackageCache;
+
+
+  public DataBindingPackageFinder(final DataBindingProjectComponent component) {
+    myComponent = component;
+    myPackageCache = CachedValuesManager.getManager(myComponent.getProject()).createCachedValue(
+      new ProjectResourceCachedValueProvider<Map<String, PsiPackage>, Set<String>>(myComponent) {
+
+        @NotNull
+        @Override
+        protected Map<String, PsiPackage> merge(List<Set<String>> results) {
+          Map<String, PsiPackage> merged = Maps.newHashMap();
+          for (Set<String> result : results) {
+            for (String qualifiedPackage : result) {
+              if (!merged.containsKey(qualifiedPackage)) {
+                merged.put(qualifiedPackage, myComponent.getOrCreateDataBindingPsiPackage(qualifiedPackage));
+              }
+            }
+          }
+          return merged;
+        }
+
+        @Override
+        ResourceCacheValueProvider<Set<String>> createCacheProvider(AndroidFacet facet) {
+          return new ResourceCacheValueProvider<Set<String>>(facet) {
+            @Override
+            Set<String> doCompute() {
+              LocalResourceRepository moduleResources = getFacet().getModuleResources(true);
+              if (moduleResources == null) {
+                return Collections.emptySet();
+              }
+              Map<String, DataBindingInfo> dataBindingResourceFiles = moduleResources.getDataBindingResourceFiles();
+              if (dataBindingResourceFiles == null) {
+                return Collections.emptySet();
+              }
+              Set<String> result = Sets.newHashSet();
+              for (DataBindingInfo info : dataBindingResourceFiles.values()) {
+                result.add(info.getPackageName());
+              }
+              return result;
+            }
+
+            @Override
+            Set<String> defaultValue() {
+              return Collections.emptySet();
+            }
+          };
+        }
+      }, false);
+  }
+
+  @Nullable
+  @Override
+  public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
+    return null;
+  }
+
+  @NotNull
+  @Override
+  public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
+    return PsiClass.EMPTY_ARRAY;
+  }
+
+  @Nullable
+  @Override
+  public PsiPackage findPackage(@NotNull String qualifiedName) {
+    if (!myComponent.hasAnyDataBindingEnabledFacet()) {
+      return null;
+    }
+    return myPackageCache.getValue().get(qualifiedName);
+  }
+}
diff --git a/android/src/com/android/tools/idea/databinding/DataBindingUtil.java b/android/src/com/android/tools/idea/databinding/DataBindingUtil.java
index 83f6b85..42cef5b 100644
--- a/android/src/com/android/tools/idea/databinding/DataBindingUtil.java
+++ b/android/src/com/android/tools/idea/databinding/DataBindingUtil.java
@@ -396,9 +396,6 @@
           @Override
           PsiMethod[] doCompute() {
             List<PsiDataBindingResourceItem> variables = myInfo.getItems(DataBindingResourceType.VARIABLE);
-            if (variables.isEmpty()) {
-              return PsiMethod.EMPTY_ARRAY;
-            }
             List<PsiMethod> methods = Lists.newArrayListWithCapacity(variables.size() * 2 + STATIC_METHOD_COUNT);
             PsiElementFactory factory = PsiElementFactory.SERVICE.getInstance(myInfo.getProject());
             for (PsiDataBindingResourceItem variable : variables) {
diff --git a/android/src/com/android/tools/idea/fd/FlightRecorder.java b/android/src/com/android/tools/idea/fd/FlightRecorder.java
new file mode 100644
index 0000000..b370bc5
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/FlightRecorder.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd;
+
+import com.android.ddmlib.IDevice;
+import com.android.tools.fd.client.InstantRunBuildInfo;
+import com.android.tools.idea.ddms.DevicePropertyUtil;
+import com.android.tools.idea.logcat.AndroidLogcatService;
+import com.android.tools.idea.run.AndroidDevice;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class FlightRecorder {
+  private static final String FD_FLR_LOGS = "flr";
+  private static final String FN_GRADLE_LOG = "build.log";
+  private static final String FN_GRADLE_PROFILE = "profile.log";
+  private static final String FN_BUILD_INFO = "build-info.xml";
+
+  // Number of previous build logs to keep around.
+  // Ideally, we'd trim based on a more sophisticated logic that knows about APK installs, but for now
+  // we take the simple approach of keeping around logs from the last N builds.
+  private static final int MAX_LOG_ENTRY_COUNT = 10;
+
+  // A path specific to this project within which all flight recorder logs are saved
+  private final Path myBasePath;
+
+  // Log buffer for runtime logs
+  private final LogcatRecorder myLogcatRecorder;
+
+  // Time (in default timezone to match idea.log) at which the last build output was saved.
+  private LocalDateTime myTimestamp;
+
+  public static FlightRecorder get(@NotNull Project project) {
+    return ServiceManager.getService(project, FlightRecorder.class);
+  }
+
+  private FlightRecorder(@NotNull Project project) {
+    Path logs = Paths.get(PathManager.getLogPath());
+    Path flr = Paths.get(FD_FLR_LOGS, project.getLocationHash());
+    myBasePath = logs.resolve(flr);
+    myLogcatRecorder = new LogcatRecorder(AndroidLogcatService.getInstance());
+  }
+
+  public void saveBuildOutput(@NotNull String gradleOutput, @NotNull InstantRunBuildProgressListener instantRunProgressListener) {
+    myTimestamp = now();
+    ApplicationManager.getApplication().executeOnPooledThread(
+      new BuildOutputRecorderTask(myBasePath, myTimestamp, gradleOutput, instantRunProgressListener));
+  }
+
+  public void saveBuildInfo(@NotNull InstantRunBuildInfo instantRunBuildInfo) {
+    if (myTimestamp == null) {
+      // this would indicate an error since we didn't get the build output before,
+      // but the inconsistency doesn't matter for logging purposes
+      myTimestamp = now();
+    }
+
+    ApplicationManager.getApplication().executeOnPooledThread(
+      new BuildInfoRecorderTask(myBasePath, myTimestamp, instantRunBuildInfo));
+  }
+
+  public void setLaunchTarget(@NotNull AndroidDevice device) {
+    try {
+      Path deviceLog = myBasePath.resolve(timeStampToFolder(myTimestamp)).resolve(getDeviceLogFileName(device));
+      Files.write(deviceLog, new byte[0]);
+    }
+    catch (IOException e) {
+      Logger.getInstance(FlightRecorder.class).info("Unable to record deployment device info", e);
+    }
+
+    // start monitoring logcat if device is online
+    if (device.getLaunchedDevice().isDone()) {
+      try {
+        IDevice d = device.getLaunchedDevice().get();
+        myLogcatRecorder.startMonitoring(d, myTimestamp);
+      }
+      catch (InterruptedException | ExecutionException e) {
+        Logger.getInstance(FlightRecorder.class).info("Unable to start recording logcat", e);
+      }
+    }
+  }
+
+  // We need very little detail about the device itself, so we can encode it directly in the file name
+  private static String getDeviceLogFileName(@NotNull AndroidDevice device) {
+    // for avds, everything we need is already included in idea.log
+    if (device.isVirtual()) {
+      return "TARGET-AVD";
+    }
+
+    ListenableFuture<IDevice> launchedDevice = device.getLaunchedDevice();
+    if (!launchedDevice.isDone()) {
+      return "OFFLINE";
+    }
+
+    IDevice d;
+    try {
+      d = launchedDevice.get();
+    }
+    catch (InterruptedException | ExecutionException e) {
+      return "OFFLINE";
+    }
+
+    return DevicePropertyUtil.getManufacturer(d, "unknown").replace(' ', '.') +
+           '-' +
+           DevicePropertyUtil.getModel(d, "unknown").replace(' ', '.');
+  }
+
+  @NotNull
+  private static LocalDateTime now() {
+    // Use default timezone since the idea.log uses that and we want to correlate between the two
+    return LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
+  }
+
+  @NotNull
+  static String timeStampToFolder(@NotNull LocalDateTime dateTime) {
+    return dateTime
+      .truncatedTo(ChronoUnit.SECONDS)
+      .toString()
+      .replace(':', '.');
+  }
+
+  @NotNull
+  static LocalDateTime folderToTimeStamp(@NotNull Path path) {
+    String fileName = path.getFileName().toString();
+    return LocalDateTime.parse(fileName.replace('.', ':'));
+  }
+
+  static void trimOldLogs(@NotNull Path root, int count) {
+    // a filter that only return folders with name matching a timestamp
+    DirectoryStream.Filter<Path> filter = entry -> {
+      if (!Files.isDirectory(entry)) {
+        return false;
+      }
+
+      try {
+        folderToTimeStamp(entry);
+        return true;
+      } catch (DateTimeParseException e) {
+        return false;
+      }
+    };
+
+    // collect all folders matching the above filter
+    List<Path> allLogs;
+    try (DirectoryStream<Path> stream = Files.newDirectoryStream(root, filter)) {
+      allLogs = Lists.newArrayList(stream.iterator());
+    } catch (IOException e) {
+      Logger.getInstance(FlightRecorder.class).warn("Unable to cleanup flight recorder logs", e);
+      return;
+    }
+
+    // sort the folders by timestamp and only keep the most recent count entries
+    allLogs.stream()
+      .sorted((p1, p2) -> folderToTimeStamp(p2).compareTo(folderToTimeStamp(p1))) // note: reverse sort
+      .skip(count)
+      .forEach(path -> {
+        try {
+          FileUtils.deleteDirectoryContents(path.toFile());
+        }
+        catch (IOException ignored) {
+        }
+
+        try {
+          Files.delete(path);
+        }
+        catch (IOException ignored) {
+        }
+      });
+  }
+
+  @NotNull
+  public List<Path> getAllLogs() {
+    List<Path> logs = new ArrayList<>();
+
+    Path logsHome = Paths.get(PathManager.getLogPath());
+    if (logsHome == null) {
+      return logs;
+    }
+
+    logs.addAll(getIdeaLogs(logsHome));
+    logs.addAll(getFlightRecorderLogs());
+
+    Path logcatPath = logsHome.resolve("logcat.txt");
+    try {
+      Files.write(logcatPath, myLogcatRecorder.getLogs(), Charsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+      logs.add(logcatPath);
+    }
+    catch (IOException e) {
+      Logger.getInstance(FlightRecorder.class).info("Unexpected error saving logcat", e);
+    }
+
+    return logs;
+  }
+
+  @NotNull
+  private static List<Path> getIdeaLogs(@NotNull Path logsHome) {
+    try (Stream<Path> stream = Files.list(logsHome)) {
+      return stream
+        .filter(p -> Files.isReadable(p) && p.getFileName().toString().startsWith("idea.log"))
+        .sorted((p1, p2) -> Long.compare(p2.toFile().lastModified(), p1.toFile().lastModified()))
+        .limit(3)
+        .collect(Collectors.toList());
+    }
+    catch (IOException e) {
+      return ImmutableList.of();
+    }
+  }
+
+  @NotNull
+  private List<Path> getFlightRecorderLogs() {
+    try {
+      List<Path> list = new ArrayList<>(100);
+      Files.walkFileTree(myBasePath, new SimpleFileVisitor<Path>() {
+        @Override
+        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+          Path fileName = file.getFileName();
+          if (fileName != null && fileName.toString().startsWith(".")) { // skip past .DS_Store etc
+            return FileVisitResult.CONTINUE;
+          }
+
+          list.add(file);
+          // arbitrary limit to avoid uploading tons of unnecessary files
+          return list.size() > 100 ? FileVisitResult.TERMINATE : FileVisitResult.CONTINUE;
+        }
+      });
+      return list;
+    }
+    catch (IOException e) {
+      return ImmutableList.of();
+    }
+  }
+
+  @NotNull
+  public String getPresentablePath(@NotNull Path file) {
+    if (file.startsWith(myBasePath)) {
+      return myBasePath.relativize(file).toString();
+    }
+
+    return file.getFileName().toString();
+  }
+
+  private static class BuildOutputRecorderTask implements Runnable {
+    private final Path myLogsRoot;
+    private final Path myCurrentLogPath;
+    private final String myGradleOutput;
+    private final InstantRunBuildProgressListener myBuildProgressListener;
+
+    private BuildOutputRecorderTask(@NotNull Path logsRoot,
+                                    @NotNull LocalDateTime dateTime,
+                                    @NotNull String gradleOuput,
+                                    @NotNull InstantRunBuildProgressListener buildProgressListener) {
+      myLogsRoot = logsRoot;
+      myCurrentLogPath = logsRoot.resolve(timeStampToFolder(dateTime));
+      myGradleOutput = gradleOuput;
+      myBuildProgressListener = buildProgressListener;
+    }
+
+    @Override
+    public void run() {
+      saveCurrentLog();
+      trimOldLogs(myLogsRoot, MAX_LOG_ENTRY_COUNT);
+    }
+
+    private void saveCurrentLog() {
+      try {
+        Files.createDirectories(myCurrentLogPath);
+        Files.write(myCurrentLogPath.resolve(FN_GRADLE_LOG), myGradleOutput.getBytes(Charsets.UTF_8));
+
+        try (BufferedWriter bw = Files.newBufferedWriter(myCurrentLogPath.resolve(FN_GRADLE_PROFILE), Charsets.UTF_8)) {
+          myBuildProgressListener.serializeTo(bw);
+        }
+
+      } catch (IOException e) {
+        Logger.getInstance(FlightRecorder.class).info("Unable to save gradle output logs", e);
+      }
+    }
+  }
+
+  private static class BuildInfoRecorderTask implements Runnable {
+    private final Path myBasePath;
+    private final InstantRunBuildInfo myBuildInfo;
+
+    public BuildInfoRecorderTask(Path logsRoot,
+                                 LocalDateTime dateTime,
+                                 InstantRunBuildInfo instantRunBuildInfo) {
+      myBasePath = logsRoot.resolve(timeStampToFolder(dateTime));
+      myBuildInfo = instantRunBuildInfo;
+    }
+
+    @Override
+    public void run() {
+      try {
+        Files.createDirectories(myBasePath);
+
+        try (BufferedWriter bw = Files.newBufferedWriter(myBasePath.resolve(FN_BUILD_INFO), Charsets.UTF_8)) {
+          myBuildInfo.serializeTo(bw);
+        }
+      } catch (IOException e) {
+        Logger.getInstance(FlightRecorder.class).info("Unable to build info", e);
+      }
+    }
+  }
+}
diff --git a/android/src/com/android/tools/idea/fd/InstantRunBuildProgressListener.java b/android/src/com/android/tools/idea/fd/InstantRunBuildProgressListener.java
new file mode 100644
index 0000000..630ba83
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/InstantRunBuildProgressListener.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd;
+
+import org.gradle.tooling.events.ProgressEvent;
+import org.gradle.tooling.events.ProgressListener;
+import org.gradle.tooling.events.task.TaskOperationResult;
+import org.gradle.tooling.events.task.internal.DefaultTaskFinishEvent;
+import org.gradle.tooling.events.task.internal.DefaultTaskSkippedResult;
+import org.gradle.tooling.events.task.internal.DefaultTaskSuccessResult;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class InstantRunBuildProgressListener implements ProgressListener {
+  private final List<ProgressEvent> myEvents = new ArrayList<>(512);
+
+  @Override
+  public void statusChanged(ProgressEvent event) {
+    myEvents.add(event);
+  }
+
+  public void serializeTo(@NotNull Writer writer) throws IOException {
+    if (myEvents.isEmpty()) {
+      writer.append("No events");
+      return;
+    }
+
+    long startTime = myEvents.get(0).getEventTime();
+
+    for (ProgressEvent event : myEvents) {
+      writer.append(String.format(Locale.US, "%10d", (event.getEventTime() - startTime)));
+      writer.append(' ');
+
+      writer.append(event.toString());
+      writer.append('\n');
+    }
+  }
+}
diff --git a/android/src/com/android/tools/idea/fd/InstantRunBuilder.java b/android/src/com/android/tools/idea/fd/InstantRunBuilder.java
index 1273bf7..93436e3 100644
--- a/android/src/com/android/tools/idea/fd/InstantRunBuilder.java
+++ b/android/src/com/android/tools/idea/fd/InstantRunBuilder.java
@@ -31,6 +31,7 @@
 import com.android.tools.idea.run.InstalledApkCache;
 import com.android.tools.idea.run.InstalledPatchCache;
 import com.android.tools.idea.run.util.MultiUserUtils;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.hash.HashCode;
 import com.intellij.openapi.application.ApplicationManager;
@@ -43,9 +44,7 @@
 
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 
 import static com.android.builder.model.AndroidProject.PROPERTY_OPTIONAL_COMPILATION_STEPS;
@@ -60,6 +59,7 @@
   private final RunAsValidator myRunAsValidator;
   private final InstalledApkCache myInstalledApkCache;
   private final InstantRunClientDelegate myInstantRunClientDelegate;
+  private final boolean myFlightRecorderEnabled;
 
   public InstantRunBuilder(@Nullable IDevice device,
                            @NotNull InstantRunContext instantRunContext,
@@ -70,6 +70,7 @@
          instantRunContext,
          runConfigContext,
          tasksProvider,
+         InstantRunSettings.isRecorderEnabled(),
          runAsValidator,
          ServiceManager.getService(InstalledApkCache.class),
          new InstantRunClientDelegate() {
@@ -81,6 +82,7 @@
                     @NotNull InstantRunContext instantRunContext,
                     @NotNull AndroidRunConfigContext runConfigContext,
                     @NotNull InstantRunTasksProvider tasksProvider,
+                    boolean enableFlightRecorder,
                     @NotNull RunAsValidator runAsValidator,
                     @NotNull InstalledApkCache installedApkCache,
                     @NotNull InstantRunClientDelegate delegate) {
@@ -88,6 +90,7 @@
     myInstantRunContext = instantRunContext;
     myRunContext = runConfigContext;
     myTasksProvider = tasksProvider;
+    myFlightRecorderEnabled = enableFlightRecorder;
     myRunAsValidator = runAsValidator;
     myInstalledApkCache = installedApkCache;
     myInstantRunClientDelegate = delegate;
@@ -107,6 +110,7 @@
 
     FileChangeListener.Changes fileChanges = myInstantRunContext.getFileChangesAndReset();
     args.addAll(getInstantRunArguments(buildSelection.mode, fileChanges));
+    args.addAll(getFlightRecorderArguments());
 
     List<String> tasks = new LinkedList<>();
     if (buildSelection.mode == BuildMode.CLEAN) {
@@ -257,8 +261,6 @@
   }
 
   private static List<String> getInstantRunArguments(@NotNull BuildMode buildMode, @Nullable FileChangeListener.Changes changes) {
-    List<String> args = Lists.newArrayListWithExpectedSize(3);
-
     // TODO: Add a user-level setting to disable this?
     // TODO: Use constants from AndroidProject once we import the new model jar.
 
@@ -278,9 +280,12 @@
       sb.append(",").append("FULL_APK"); //TODO: Replace with enum reference after next model drop.
     }
 
-    args.add(sb.toString());
+    return Collections.singletonList(sb.toString());
+  }
 
-    return args;
+  @NotNull
+  private List<String> getFlightRecorderArguments() {
+    return myFlightRecorderEnabled ? ImmutableList.of("--info") : ImmutableList.of();
   }
 
   private static void appendChangeInfo(@NotNull StringBuilder sb, @Nullable FileChangeListener.Changes changes) {
diff --git a/android/src/com/android/tools/idea/fd/InstantRunConfigurable.form b/android/src/com/android/tools/idea/fd/InstantRunConfigurable.form
index c82f9f9..7d2f7f0 100644
--- a/android/src/com/android/tools/idea/fd/InstantRunConfigurable.form
+++ b/android/src/com/android/tools/idea/fd/InstantRunConfigurable.form
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.android.tools.idea.fd.InstantRunConfigurable">
-  <grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="7" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+  <grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="10" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
-      <xy x="20" y="20" width="563" height="250"/>
+      <xy x="20" y="20" width="787" height="523"/>
     </constraints>
     <properties/>
     <border type="none"/>
@@ -18,7 +18,7 @@
       </component>
       <component id="3e826" class="com.intellij.ui.components.JBCheckBox" binding="myRestartActivityCheckBox">
         <constraints>
-          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
         </constraints>
         <properties>
           <text value="Restart activity on code changes"/>
@@ -26,7 +26,7 @@
       </component>
       <vspacer id="a26c5">
         <constraints>
-          <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+          <grid row="8" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
         </constraints>
       </vspacer>
       <component id="72ff4" class="com.intellij.ui.components.JBLabel" binding="myGradleLabel">
@@ -46,7 +46,7 @@
       </component>
       <component id="cad68" class="com.intellij.ui.components.JBCheckBox" binding="myShowToastCheckBox">
         <constraints>
-          <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+          <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
         </constraints>
         <properties>
           <text value="Show toasts in the running app when changes are applied"/>
@@ -54,12 +54,76 @@
       </component>
       <component id="dbc36" class="com.intellij.ui.components.JBCheckBox" binding="myShowIrStatusNotifications">
         <constraints>
-          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+          <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
         </constraints>
         <properties>
           <text value="Show Instant Run status notifications"/>
         </properties>
       </component>
+      <component id="34eaf" class="com.intellij.ui.components.JBCheckBox" binding="myEnableRecorder">
+        <constraints>
+          <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Log extra info to help Google troubleshoot Instant Run issues (Recommended)"/>
+        </properties>
+      </component>
+      <grid id="cbc71" binding="myHelpGooglePanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="10" left="10" bottom="10" right="10"/>
+        <constraints>
+          <grid row="9" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="6d4cb" class="com.intellij.ui.components.JBLabel" binding="myHavingTroubleLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <fontColor value="NORMAL"/>
+              <text value="Having trouble with Instant Run?"/>
+            </properties>
+          </component>
+          <vspacer id="68131">
+            <constraints>
+              <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </vspacer>
+          <component id="22da" class="com.intellij.ui.components.JBLabel">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <fontColor value="BRIGHTER"/>
+              <text resource-bundle="messages/AndroidBundle" key="instant.run.flr.banner.subtitle"/>
+            </properties>
+          </component>
+          <component id="b0758" class="com.intellij.ui.HyperlinkLabel" binding="myReenableLink">
+            <constraints>
+              <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+          </component>
+        </children>
+      </grid>
+      <grid id="b6358" layout-manager="FlowLayout" hgap="0" vgap="5" flow-align="0">
+        <constraints>
+          <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="5" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="d9cef" class="com.intellij.ui.HyperlinkLabel" binding="myExtraInfoHyperlink">
+            <constraints/>
+            <properties/>
+          </component>
+          <component id="9f49d" class="com.intellij.ui.HyperlinkLabel" binding="myPrivacyPolicyLink">
+            <constraints/>
+            <properties/>
+          </component>
+        </children>
+      </grid>
     </children>
   </grid>
 </form>
diff --git a/android/src/com/android/tools/idea/fd/InstantRunConfigurable.java b/android/src/com/android/tools/idea/fd/InstantRunConfigurable.java
index a31e932..f99e95a 100644
--- a/android/src/com/android/tools/idea/fd/InstantRunConfigurable.java
+++ b/android/src/com/android/tools/idea/fd/InstantRunConfigurable.java
@@ -25,6 +25,7 @@
 import com.android.tools.idea.gradle.project.GradleSyncListener;
 import com.android.tools.idea.gradle.util.GradleUtil;
 import com.android.tools.idea.sdk.progress.StudioLoggerProgressIndicator;
+import com.intellij.ide.BrowserUtil;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleManager;
@@ -34,8 +35,10 @@
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.ui.HyperlinkLabel;
+import com.intellij.ui.JBColor;
 import com.intellij.ui.components.JBCheckBox;
 import com.intellij.ui.components.JBLabel;
+import com.intellij.util.ui.JBUI;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.android.sdk.AndroidSdkUtils;
 import org.jetbrains.annotations.Nls;
@@ -63,9 +66,37 @@
   private JBCheckBox myShowToastCheckBox;
   private JBCheckBox myShowIrStatusNotifications;
 
+  private JBCheckBox myEnableRecorder;
+  private HyperlinkLabel myExtraInfoHyperlink;
+  private HyperlinkLabel myPrivacyPolicyLink;
+
+  private HyperlinkLabel myReenableLink;
+  private JPanel myHelpGooglePanel;
+  private JBLabel myHavingTroubleLabel;
+
   public InstantRunConfigurable() {
+    myExtraInfoHyperlink.setHtmlText("Learn more about <a href=\"more\">what is logged</a>,");
+    myExtraInfoHyperlink.addHyperlinkListener(e -> BrowserUtil.browse("https://developer.android.com/r/studio-ui/ir-flight-recorder.html"));
+
+    myPrivacyPolicyLink.setHtmlText("and our <a href=\"privacy\">privacy policy.</a>");
+    myPrivacyPolicyLink.addHyperlinkListener(e -> BrowserUtil.browse("https://www.google.com/policies/privacy/"));
+
+    myHelpGooglePanel.setBackground(UIUtil.getPanelBackground().brighter());
+    myHelpGooglePanel.setBorder(BorderFactory.createLineBorder(JBColor.GRAY));
+    myHavingTroubleLabel.setFont(JBUI.Fonts.label().asBold().biggerOn(1.2f));
+
+    myReenableLink.setHtmlText("<a href=\"reenable\">Re-enable and activate extra logging</a>");
+    myReenableLink.addHyperlinkListener(e -> {
+      myInstantRunCheckBox.setSelected(true);
+      enableIrOptions(true);
+      myEnableRecorder.setSelected(true);
+    });
+
+    myInstantRunCheckBox.addActionListener(e -> enableIrOptions(myInstantRunCheckBox.isSelected()));
+
     myBuildConfiguration = InstantRunConfiguration.getInstance();
     updateLinkState();
+    enableIrOptions(myBuildConfiguration.INSTANT_RUN);
   }
 
   @NotNull
@@ -103,7 +134,8 @@
     return myBuildConfiguration.INSTANT_RUN != isInstantRunEnabled() ||
            myBuildConfiguration.RESTART_ACTIVITY != isRestartActivity() ||
            myBuildConfiguration.SHOW_TOAST != isShowToast() ||
-           myBuildConfiguration.SHOW_IR_STATUS_NOTIFICATIONS != isShowStatusNotifications();
+           myBuildConfiguration.SHOW_IR_STATUS_NOTIFICATIONS != isShowStatusNotifications() ||
+           myBuildConfiguration.ENABLE_RECORDER != isEnableRecorder();
   }
 
   @Override
@@ -112,6 +144,7 @@
     myBuildConfiguration.RESTART_ACTIVITY = isRestartActivity();
     myBuildConfiguration.SHOW_TOAST = isShowToast();
     myBuildConfiguration.SHOW_IR_STATUS_NOTIFICATIONS = isShowStatusNotifications();
+    myBuildConfiguration.ENABLE_RECORDER = isEnableRecorder();
 
     for (Project project : ProjectManager.getInstance().getOpenProjects()) {
       if (project.isDefault()) {
@@ -127,6 +160,7 @@
     myRestartActivityCheckBox.setSelected(myBuildConfiguration.RESTART_ACTIVITY);
     myShowToastCheckBox.setSelected(myBuildConfiguration.SHOW_TOAST);
     myShowIrStatusNotifications.setSelected(myBuildConfiguration.SHOW_IR_STATUS_NOTIFICATIONS);
+    myEnableRecorder.setSelected(myBuildConfiguration.ENABLE_RECORDER);
   }
 
   @Override
@@ -149,6 +183,10 @@
     return myShowIrStatusNotifications.isSelected();
   }
 
+  private boolean isEnableRecorder() {
+    return myEnableRecorder.isSelected();
+  }
+
   private void createUIComponents() {
     myOldVersionLabel = new HyperlinkLabel();
     setSyncLinkMessage("");
@@ -186,9 +224,17 @@
     boolean enabled = isGradle && isCurrentPlugin;
 
     myInstantRunCheckBox.setEnabled(isGradle); // allow turning off instant run even if the plugin is not the latest
+    enableIrOptions(enabled);
+  }
+
+  private void enableIrOptions(boolean enabled) {
     myRestartActivityCheckBox.setEnabled(enabled);
     myShowToastCheckBox.setEnabled(enabled);
     myShowIrStatusNotifications.setEnabled(enabled);
+    myEnableRecorder.setEnabled(enabled);
+    myExtraInfoHyperlink.setEnabled(enabled);
+
+    myHelpGooglePanel.setVisible(!enabled);
   }
 
   @Override
diff --git a/android/src/com/android/tools/idea/fd/InstantRunConfiguration.java b/android/src/com/android/tools/idea/fd/InstantRunConfiguration.java
index 913cedd..7c11b8e 100644
--- a/android/src/com/android/tools/idea/fd/InstantRunConfiguration.java
+++ b/android/src/com/android/tools/idea/fd/InstantRunConfiguration.java
@@ -29,8 +29,7 @@
   public boolean RESTART_ACTIVITY = true;
   public boolean SHOW_TOAST = true;
   public boolean SHOW_IR_STATUS_NOTIFICATIONS = true;
-  public boolean COLD_SWAP = true;
-  public String COLD_SWAP_MODE;
+  public boolean ENABLE_RECORDER = false;
 
   public static InstantRunConfiguration getInstance() {
     return ServiceManager.getService(InstantRunConfiguration.class);
diff --git a/android/src/com/android/tools/idea/fd/InstantRunNotificationProvider.java b/android/src/com/android/tools/idea/fd/InstantRunNotificationProvider.java
index 6120b66..c386dff 100644
--- a/android/src/com/android/tools/idea/fd/InstantRunNotificationProvider.java
+++ b/android/src/com/android/tools/idea/fd/InstantRunNotificationProvider.java
@@ -36,14 +36,13 @@
     BuildCause.APP_NOT_INSTALLED
   );
 
-  private static final Map<BuildCause, String> ourNotificationsByCause = new ImmutableMap.Builder<BuildCause, String>()
+  private static final Map<BuildCause, String> ourFullBuildNotificationsByCause = new ImmutableMap.Builder<BuildCause, String>()
     .put(BuildCause.USER_REQUESTED_CLEAN_BUILD, AndroidBundle.message("instant.run.notification.cleanbuild.on.user.request"))
     .put(BuildCause.MISMATCHING_TIMESTAMPS, AndroidBundle.message("instant.run.notification.cleanbuild.mismatching.timestamps"))
     .put(BuildCause.API_TOO_LOW_FOR_INSTANT_RUN, AndroidBundle.message("instant.run.notification.fullbuild.api.less.than.15"))
     .put(BuildCause.MANIFEST_RESOURCE_CHANGED, AndroidBundle.message("instant.run.notification.fullbuild.manifestresourcechanged"))
     .put(BuildCause.FREEZE_SWAP_REQUIRES_API21, AndroidBundle.message("instant.run.notification.fullbuild.api.less.than.21"))
     .put(BuildCause.FREEZE_SWAP_REQUIRES_WORKING_RUN_AS, AndroidBundle.message("instant.run.notification.fullbuild.broken.runas"))
-    .put(BuildCause.APP_USES_MULTIPLE_PROCESSES, AndroidBundle.message("instant.run.notification.coldswap.multiprocess"))
     .build();
 
   private final BuildSelection myBuildSelection;
@@ -67,8 +66,8 @@
       return null;
     }
 
-    if (ourNotificationsByCause.containsKey(buildCause)) {
-      return ourNotificationsByCause.get(buildCause);
+    if (ourFullBuildNotificationsByCause.containsKey(buildCause)) {
+      return ourFullBuildNotificationsByCause.get(buildCause);
     }
 
     if (buildCause == BuildCause.APP_NOT_RUNNING) {
@@ -100,6 +99,10 @@
           }
         }
         else if (buildMode == BuildMode.COLD) {
+          if (buildCause == BuildCause.APP_USES_MULTIPLE_PROCESSES) {
+            return AndroidBundle.message("instant.run.notification.coldswap.multiprocess");
+          }
+
           // we requested a cold swap build, so mention why we requested such a build
           sb.append(' ').append(buildCause).append('.');
         }
diff --git a/android/src/com/android/tools/idea/fd/InstantRunSettings.java b/android/src/com/android/tools/idea/fd/InstantRunSettings.java
index 2e5e5d2..79f8d0e 100644
--- a/android/src/com/android/tools/idea/fd/InstantRunSettings.java
+++ b/android/src/com/android/tools/idea/fd/InstantRunSettings.java
@@ -29,6 +29,11 @@
     return configuration.INSTANT_RUN;
   }
 
+  public static void setInstantRunEnabled(boolean en) {
+    InstantRunConfiguration configuration = InstantRunConfiguration.getInstance();
+    configuration.INSTANT_RUN = en;
+  }
+
   /** Is showing toasts enabled in the given project */
   public static boolean isShowToastEnabled() {
     InstantRunConfiguration configuration = InstantRunConfiguration.getInstance();
@@ -50,4 +55,14 @@
     InstantRunConfiguration configuration = InstantRunConfiguration.getInstance();
     configuration.SHOW_IR_STATUS_NOTIFICATIONS = en;
   }
+
+  public static boolean isRecorderEnabled() {
+    InstantRunConfiguration configuration = InstantRunConfiguration.getInstance();
+    return configuration.ENABLE_RECORDER;
+  }
+
+  public static void setRecorderEnabled(boolean en) {
+    InstantRunConfiguration configuration = InstantRunConfiguration.getInstance();
+    configuration.ENABLE_RECORDER = en;
+  }
 }
diff --git a/android/src/com/android/tools/idea/fd/LogcatRecorder.java b/android/src/com/android/tools/idea/fd/LogcatRecorder.java
new file mode 100644
index 0000000..6ece112
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/LogcatRecorder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd;
+
+import com.android.annotations.concurrency.GuardedBy;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.NullOutputReceiver;
+import com.android.ddmlib.logcat.LogCatMessage;
+import com.android.tools.idea.logcat.AndroidLogcatService;
+import com.google.common.collect.EvictingQueue;
+import com.google.common.collect.ImmutableList;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * {@link LogcatRecorder} records logcat output from the instant run runtime library on a given device into an in memory ring buffer.
+ * This class is solely intended for use by the {@link FlightRecorder}, and hence behaves in a form that is useful for later viewing:
+ * <ul>
+ * <li>Maintains a single log buffer across devices - we are interested in all runs of the project across all devices</li>
+ * <li>Adds an entry containing the timestamp on each launch to help correlate with other logs</li>
+ * </ul>
+ */
+public class LogcatRecorder {
+  private static final int BUFSIZE = 8192;
+
+  private final Object LOCK = new Object();
+
+  @GuardedBy("LOCK")
+  private final EvictingQueue<String> myLogs = EvictingQueue.create(BUFSIZE);
+
+  private final AndroidLogcatService myLogcatService;
+
+  // Device currently being monitored
+  private AtomicReference<IDevice> myDeviceRef = new AtomicReference<>();
+  private AndroidLogcatService.LogLineListener myLogListener;
+
+  public LogcatRecorder(@NotNull AndroidLogcatService logcatService) {
+    myLogcatService = logcatService;
+    myLogListener = new MyLogLineListener();
+  }
+
+  public void startMonitoring(@NotNull IDevice device, @NotNull LocalDateTime buildTimeStamp) {
+    IDevice old = myDeviceRef.getAndSet(device);
+    if (old != device) {
+      if (old != null) {
+        myLogcatService.removeListener(old, myLogListener);
+      }
+      myLogcatService.addListener(device, myLogListener);
+
+      enableInstantRunLog(device);
+    }
+
+    addLog("------------Launch on " + device.getName() + " @ " + buildTimeStamp.toString());
+  }
+
+  private static void enableInstantRunLog(IDevice device) {
+    ApplicationManager.getApplication().executeOnPooledThread(() -> {
+      try {
+        device.executeShellCommand("setprop log.tag.InstantRun VERBOSE", new NullOutputReceiver());
+      }
+      catch (Exception ignored) {
+      }
+    });
+  }
+
+  public List<String> getLogs() {
+    synchronized (LOCK) {
+      return ImmutableList.copyOf(myLogs);
+    }
+  }
+
+  private void addLog(@NotNull String s) {
+    synchronized (LOCK) {
+      myLogs.add(s);
+    }
+  }
+
+  private class MyLogLineListener implements AndroidLogcatService.LogLineListener {
+    @Override
+    public void receiveLogLine(@NotNull LogCatMessage line) {
+      if ("InstantRun".equals(line.getTag())) { // only save logs from the instant run library
+        addLog(line.toString());
+      }
+    }
+  }
+}
diff --git a/android/src/com/android/tools/idea/fd/actions/InstantRunFeedbackDialog.form b/android/src/com/android/tools/idea/fd/actions/InstantRunFeedbackDialog.form
new file mode 100644
index 0000000..f5b3855
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/actions/InstantRunFeedbackDialog.form
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.android.tools.idea.fd.actions.InstantRunFeedbackDialog">
+  <grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="733" height="434"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="3f6b1" class="com.intellij.ui.components.JBLabel">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text resource-bundle="messages/AndroidBundle" key="instant.run.flr.dialog.description"/>
+        </properties>
+      </component>
+      <component id="c168d" class="com.intellij.ui.components.JBLabel">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text resource-bundle="messages/AndroidBundle" key="instant.run.flr.dialog.includeslogs"/>
+        </properties>
+      </component>
+      <scrollpane id="5750c" class="com.intellij.ui.components.JBScrollPane">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="9ed1" class="javax.swing.JTextArea" binding="myIssueTextArea">
+            <constraints/>
+            <properties>
+              <rows value="5"/>
+            </properties>
+          </component>
+        </children>
+      </scrollpane>
+      <scrollpane id="15233" class="com.intellij.ui.components.JBScrollPane">
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="de46d" class="com.intellij.ui.components.JBList" binding="myFilesList">
+            <constraints/>
+            <properties/>
+          </component>
+        </children>
+      </scrollpane>
+    </children>
+  </grid>
+</form>
diff --git a/android/src/com/android/tools/idea/fd/actions/InstantRunFeedbackDialog.java b/android/src/com/android/tools/idea/fd/actions/InstantRunFeedbackDialog.java
new file mode 100644
index 0000000..26a0254
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/actions/InstantRunFeedbackDialog.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd.actions;
+
+import com.android.tools.idea.fd.FlightRecorder;
+import com.intellij.ide.actions.ShowFilePathAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import com.intellij.openapi.ui.ValidationInfo;
+import com.intellij.ui.*;
+import com.intellij.ui.SingleSelectionModel;
+import com.intellij.ui.components.JBList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.nio.file.Path;
+import java.util.List;
+
+public class InstantRunFeedbackDialog extends DialogWrapper {
+  private final List<Path> myLogs;
+
+  private JPanel myPanel;
+  private JTextArea myIssueTextArea;
+  private JBList myFilesList;
+  private String myIssueText;
+
+  protected InstantRunFeedbackDialog(@NotNull Project project) {
+    super(project);
+
+    myFilesList.setVisibleRowCount(4);
+    myFilesList.setEmptyText("No Log Files found");
+    myLogs = FlightRecorder.get(project).getAllLogs();
+    myFilesList.setModel(new CollectionListModel<>(myLogs));
+    myFilesList.setCellRenderer(new ColoredListCellRenderer<Path>() {
+      @Override
+      protected void customizeCellRenderer(JList list, Path value, int index, boolean selected, boolean hasFocus) {
+        append(value.toString());
+      }
+    });
+
+    myFilesList.setSelectionModel(new SingleSelectionModel());
+    myFilesList.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
+    new ClickListener() {
+      @Override
+      public boolean onClick(@NotNull MouseEvent event, int clickCount) {
+        if (event.getButton() == MouseEvent.BUTTON1) {
+          Object selectedValue = myFilesList.getSelectedValue();
+          if (selectedValue instanceof Path) {
+            ShowFilePathAction.openFile(((Path)selectedValue).toFile());
+            return true;
+          }
+        }
+        return false;
+      }
+    }.installOn(myFilesList);
+
+    setTitle("Report Instant Run Issue");
+    setModal(true);
+    init();
+  }
+
+  @Nullable
+  @Override
+  protected JComponent createCenterPanel() {
+    return myPanel;
+  }
+
+  @NotNull
+  public List<Path> getLogs() {
+    return myLogs;
+  }
+
+  @NotNull
+  public String getIssueText() {
+    return myIssueText;
+  }
+
+  @Override
+  protected void doOKAction() {
+    myIssueText = getIssueReport();
+    super.doOKAction();
+  }
+
+  @Nullable
+  @Override
+  protected ValidationInfo doValidate() {
+    String issueReport = getIssueReport();
+    if (issueReport.isEmpty()) {
+      return new ValidationInfo("Please describe the issue", myIssueTextArea);
+    }
+    return super.doValidate();
+  }
+
+  private String getIssueReport() {
+    Document document = myIssueTextArea.getDocument();
+    try {
+      return document.getText(0, document.getLength());
+    }
+    catch (BadLocationException e) { // can't happen since we explicitly get from [0..length]
+      assert false : e.toString();
+      return e.toString();
+    }
+  }
+}
diff --git a/android/src/com/android/tools/idea/fd/actions/SubmitFeedback.java b/android/src/com/android/tools/idea/fd/actions/SubmitFeedback.java
new file mode 100644
index 0000000..0b9caa9
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/actions/SubmitFeedback.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd.actions;
+
+import com.android.tools.idea.fd.FlightRecorder;
+import com.android.tools.idea.fd.InstantRunSettings;
+import com.android.tools.idea.fd.crash.GoogleCrash;
+import com.google.common.escape.Escaper;
+import com.google.common.net.UrlEscapers;
+import com.intellij.ide.BrowserUtil;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationInfo;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import org.jetbrains.android.util.AndroidBundle;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.TimeUnit;
+
+public class SubmitFeedback extends DumbAwareAction {
+  private static final NotificationGroup FLR_NOTIFICATION_GROUP = NotificationGroup.balloonGroup("instant.run.flight.recorder");
+
+  public SubmitFeedback() {
+    super("Report Instant Run Issue...");
+  }
+
+  @Override
+  public void update(AnActionEvent e) {
+    Project project = e.getProject();
+    getTemplatePresentation().setVisible(project != null && !project.isDefault());
+  }
+
+  @Override
+  public void actionPerformed(AnActionEvent e) {
+    Project project = e.getProject();
+    if (project == null) {
+      Logger.getInstance(SubmitFeedback.class).info("Unable to identify current project");
+      return;
+    }
+
+    if (!InstantRunSettings.isInstantRunEnabled() || !InstantRunSettings.isRecorderEnabled()) {
+      int result = Messages.showYesNoDialog(
+        project,
+        AndroidBundle.message("instant.run.flr.would.you.like.to.enable"),
+        AndroidBundle.message("instant.run.flr.dialog.title"),
+        "Yes, I'd like to help",
+        "Cancel",
+        Messages.getQuestionIcon());
+      if (result == Messages.NO) {
+        return;
+      }
+
+      InstantRunSettings.setInstantRunEnabled(true);
+      InstantRunSettings.setRecorderEnabled(true);
+      Messages.showInfoMessage(project,
+                               AndroidBundle.message("instant.run.flr.howto"),
+                               AndroidBundle.message("instant.run.flr.dialog.title"));
+      return;
+    }
+
+    InstantRunFeedbackDialog dialog = new InstantRunFeedbackDialog(project);
+    boolean ok = dialog.showAndGet();
+    if (ok) {
+      new Task.Backgroundable(project, "Submitting Instant Run Issue") {
+        public CompletableFuture<String> myReport;
+
+        @Override
+        public void run(@NotNull ProgressIndicator indicator) {
+          myReport =
+            GoogleCrash.getInstance().submit(FlightRecorder.get(project), dialog.getIssueText(), dialog.getLogs());
+
+          while (!myReport.isDone()) {
+            try {
+              myReport.get(200, TimeUnit.MILLISECONDS);
+            }
+            catch (Exception ignored) {
+            }
+
+            if (indicator.isCanceled()) {
+              return;
+            }
+          }
+        }
+
+        @Override
+        public void onSuccess() {
+          if (myReport.isDone()) {
+            String reportId;
+            try {
+              reportId = myReport.getNow("00");
+            }
+            catch (CancellationException e) {
+              Logger.getInstance(SubmitFeedback.class).info("Submission of flight recorder logs cancelled");
+              return;
+            }
+            catch (CompletionException e) {
+              FLR_NOTIFICATION_GROUP.createNotification("Unexpected error while submitting instant run logs: " + e.getMessage(),
+                                                        NotificationType.ERROR);
+              Logger.getInstance(SubmitFeedback.class).info(e);
+              return;
+            }
+            String message = String.format("<html>Thank you for submitting the bug report.<br>" +
+                                           "If you would like to follow up on this report, please file a bug at <a href=\"bug\">b.android.com</a> and specify the report id '%1$s'<html>",
+                                           reportId);
+            FLR_NOTIFICATION_GROUP
+              .createNotification("", message, NotificationType.INFORMATION, (notification, event) -> {
+                Escaper escaper = UrlEscapers.urlFormParameterEscaper();
+                String comment = String.format("Build: %1$s\nInstant Run Report: %2$s",
+                                               ApplicationInfo.getInstance().getFullVersion(),
+                                               reportId);
+                String url = String.format("https://code.google.com/p/android/issues/entry?template=%1$s&comment=%2$s&status=New",
+                                           escaper.escape("Android Studio Instant Run Bug"),
+                                           escaper.escape(comment));
+                BrowserUtil.browse(url);
+              })
+              .notify(project);
+          }
+        }
+      }.queue();
+    }
+  }
+}
diff --git a/android/src/com/android/tools/idea/fd/crash/GoogleCrash.java b/android/src/com/android/tools/idea/fd/crash/GoogleCrash.java
new file mode 100644
index 0000000..3911ea8
--- /dev/null
+++ b/android/src/com/android/tools/idea/fd/crash/GoogleCrash.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd.crash;
+
+import com.android.tools.analytics.Anonymizer;
+import com.android.tools.idea.fd.FlightRecorder;
+import com.android.utils.NullLogger;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Splitter;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.application.ApplicationInfo;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.updateSettings.impl.UpdateChecker;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtilRt;
+import com.intellij.openapi.util.text.StringUtil;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.entity.GzipCompressingEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.*;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryUsage;
+import java.lang.management.RuntimeMXBean;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * {@link GoogleCrash} provides APIs to upload crash reports to Google crash reporting service.
+ * @see <a href="http://go/studio-g3doc/implementation/crash">Crash Backend</a> for more information.
+ */
+public class GoogleCrash {
+  private static final boolean UNIT_TEST_MODE = ApplicationManager.getApplication() == null;
+  private static final boolean DEBUG_BUILD = !UNIT_TEST_MODE && ApplicationManager.getApplication().isInternal();
+
+  // Send crashes during development to the staging backend
+  private static final String CRASH_URL =
+    (UNIT_TEST_MODE || DEBUG_BUILD) ? "https://clients2.google.com/cr/staging_report" : "https://clients2.google.com/cr/report";
+
+  @Nullable
+  private static final String ANONYMIZED_UID = getAnonymizedUid();
+  private static final String LOCALE = Locale.getDefault() == null ? "unknown" : Locale.getDefault().toString();
+
+  // The standard keys expected by crash backend. The product id and version are required, others are optional.
+  static final String KEY_PRODUCT_ID = "productId";
+  static final String KEY_VERSION = "version";
+
+  private static GoogleCrash ourInstance;
+  private final String myCrashUrl;
+
+  @Nullable
+  private static String getAnonymizedUid() {
+    if (UNIT_TEST_MODE) {
+      return "UnitTest";
+    }
+
+    try {
+      return Anonymizer.anonymizeUtf8(new NullLogger(), UpdateChecker.getInstallationUID(PropertiesComponent.getInstance()));
+    }
+    catch (IOException e) {
+      return null;
+    }
+  }
+
+  private GoogleCrash() {
+    this(CRASH_URL);
+  }
+
+  @VisibleForTesting
+  GoogleCrash(@NotNull String crashUrl) {
+    myCrashUrl = crashUrl;
+  }
+
+  public CompletableFuture<String> submit(@NotNull FlightRecorder flightRecorder, @NotNull String issueText, @NotNull List<Path> logFiles) {
+    CompletableFuture<String> future = new CompletableFuture<>();
+    ForkJoinPool.commonPool().submit(() -> {
+      try {
+        HttpClient client = HttpClients.createDefault();
+        HttpResponse response = client.execute(createPost(flightRecorder, issueText, logFiles));
+        StatusLine statusLine = response.getStatusLine();
+        if (statusLine.getStatusCode() >= 300) {
+          future.completeExceptionally(new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()));
+          if (DEBUG_BUILD) {
+            //noinspection UseOfSystemOutOrSystemErr
+            System.out.println("Error submitting report: " + statusLine);
+          }
+          return;
+        }
+
+        HttpEntity entity = response.getEntity();
+        if (entity == null) {
+          future.completeExceptionally(new NullPointerException("Empty response entity"));
+          return;
+        }
+
+        String reportId = EntityUtils.toString(entity);
+        if (DEBUG_BUILD) {
+          //noinspection UseOfSystemOutOrSystemErr
+          System.out.println("Report submitted: http://go/crash-staging/" + reportId);
+        }
+        future.complete(reportId);
+      }
+      catch (IOException e) {
+        future.completeExceptionally(e);
+      }
+    });
+    return future;
+  }
+
+  @NotNull
+  private HttpUriRequest createPost(@NotNull FlightRecorder flightRecorder, @NotNull String issueText, @NotNull List<Path> logFiles) {
+    HttpPost post = new HttpPost(myCrashUrl);
+
+    ApplicationInfo applicationInfo = getApplicationInfo();
+
+    String strictVersion = applicationInfo == null ? "0.0.0.0" : applicationInfo.getStrictVersion();
+
+    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+
+    // key names recognized by crash
+    builder.addTextBody(KEY_PRODUCT_ID, "AndroidStudio");
+    builder.addTextBody(KEY_VERSION, strictVersion);
+    builder.addTextBody("exception_info", getUniqueStackTrace());
+    builder.addTextBody("user_report", issueText);
+
+    if (ANONYMIZED_UID != null) {
+      builder.addTextBody("guid", ANONYMIZED_UID);
+    }
+    RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+    builder.addTextBody("ptime", Long.toString(runtimeMXBean.getUptime()));
+
+    // product specific key value pairs
+    builder.addTextBody("fullVersion", applicationInfo == null ? "0.0.0.0" : applicationInfo.getFullVersion());
+
+    builder.addTextBody("osName", StringUtil.notNullize(SystemInfo.OS_NAME));
+    builder.addTextBody("osVersion", StringUtil.notNullize(SystemInfo.OS_VERSION));
+    builder.addTextBody("osArch", StringUtil.notNullize(SystemInfo.OS_ARCH));
+    builder.addTextBody("locale", StringUtil.notNullize(LOCALE));
+
+    builder.addTextBody("vmName", StringUtil.notNullize(runtimeMXBean.getVmName()));
+    builder.addTextBody("vmVendor", StringUtil.notNullize(runtimeMXBean.getVmVendor()));
+    builder.addTextBody("vmVersion", StringUtil.notNullize(runtimeMXBean.getVmVersion()));
+
+    MemoryUsage usage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
+    builder.addTextBody("heapUsed", Long.toString(usage.getUsed()));
+    builder.addTextBody("heapCommitted", Long.toString(usage.getCommitted()));
+    builder.addTextBody("heapMax", Long.toString(usage.getMax()));
+
+    // add report specific data
+    builder.addTextBody("Type", "InstantRunFlightRecorder");
+    addFlightRecorderLogs(builder, flightRecorder, logFiles);
+
+    post.setEntity(new GzipCompressingEntity(builder.build()));
+    return post;
+  }
+
+  static String getUniqueStackTrace() {
+    StringBuilder sb = new StringBuilder(100);
+    sb.append("com.android.InstantRunException: Flight Recorder Information: ");
+    sb.append(System.currentTimeMillis());
+    sb.append('\n');
+    sb.append("\tat ");
+
+    int i = 0;
+    for (String u : Splitter.on('-').split(UUID.randomUUID().toString())) {
+      sb.append('p');
+      sb.append(u);
+      sb.append('.');
+    }
+    sb.append("FlightRecorder.report(Flight");
+    sb.append(System.currentTimeMillis());
+    sb.append("Recorder.java:500)");
+
+    return sb.toString();
+  }
+
+  private static void addFlightRecorderLogs(@NotNull MultipartEntityBuilder builder,
+                                            @NotNull FlightRecorder flightRecorder,
+                                            @NotNull List<Path> logFiles) {
+    try {
+      // Crash backend restricts uploads to 1.2M total, so we need to zip up all the files together.
+      Path path = zipFiles(flightRecorder, logFiles);
+      builder.addBinaryBody(path.getFileName().toString(),
+                            Files.readAllBytes(path),
+                            ContentType.APPLICATION_OCTET_STREAM,
+                            path.getFileName().toString());
+    }
+    catch (IOException e) {
+      builder.addTextBody("IOError", e.toString());
+    }
+  }
+
+  @NotNull
+  private static Path zipFiles(@NotNull FlightRecorder flightRecorder, @NotNull List<Path> logFiles)
+    throws IOException {
+    Path tempFile = Files.createTempFile("flr", ".zip");
+    String baseName = FileUtilRt.getNameWithoutExtension(tempFile.getFileName().toString());
+
+    byte[] data = new byte[4096];
+    int i;
+
+    try (FileOutputStream fos = new FileOutputStream(tempFile.toFile());
+         BufferedOutputStream bos = new BufferedOutputStream(fos);
+         ZipOutputStream zos = new ZipOutputStream(bos)) {
+      zos.setMethod(ZipOutputStream.DEFLATED);
+
+      for (Path file : logFiles) {
+        String name = baseName + "/" + flightRecorder.getPresentablePath(file);
+        zos.putNextEntry(new ZipEntry(name.replace(File.separatorChar, '/')));
+
+        try (InputStream is = Files.newInputStream(file);
+             BufferedInputStream bis = new BufferedInputStream(is)) {
+          while ((i = bis.read(data)) > 0) {
+            zos.write(data, 0, i);
+          }
+        }
+      }
+    }
+
+    return tempFile;
+  }
+
+  @Nullable
+  private static ApplicationInfo getApplicationInfo() {
+    // We obtain the ApplicationInfo only if running with an application instance. Otherwise, a call to a ServiceManager never returns..
+    return ApplicationManager.getApplication() == null ? null : ApplicationInfo.getInstance();
+  }
+
+  public static synchronized GoogleCrash getInstance() {
+    if (ourInstance == null) {
+      ourInstance = new GoogleCrash();
+    }
+
+    return ourInstance;
+  }
+}
+
diff --git a/android/src/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpec.java b/android/src/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpec.java
index 0d84fbe..1b36650 100644
--- a/android/src/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpec.java
+++ b/android/src/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpec.java
@@ -116,8 +116,7 @@
     this.extension = emptyToNull(extension);
   }
 
-  @Override
-  public boolean equals(Object o) {
+  public boolean equalsIgnoreVersion(Object o) {
     if (this == o) {
       return true;
     }
@@ -127,12 +126,20 @@
     ArtifactDependencySpec that = (ArtifactDependencySpec)o;
     return Objects.equal(name, that.name) &&
            Objects.equal(group, that.group) &&
-           Objects.equal(version, that.version) &&
            Objects.equal(classifier, that.classifier) &&
            Objects.equal(extension, that.extension);
   }
 
   @Override
+  public boolean equals(Object o) {
+    if (equalsIgnoreVersion(o)) {
+      ArtifactDependencySpec that = (ArtifactDependencySpec)o;
+      return Objects.equal(version, that.version);
+    }
+    return false;
+  }
+
+  @Override
   public int hashCode() {
     return Objects.hashCode(name, group, version, classifier, extension);
   }
diff --git a/android/src/com/android/tools/idea/gradle/invoker/GradleTasksExecutor.java b/android/src/com/android/tools/idea/gradle/invoker/GradleTasksExecutor.java
index 1c99ebe..b139035 100644
--- a/android/src/com/android/tools/idea/gradle/invoker/GradleTasksExecutor.java
+++ b/android/src/com/android/tools/idea/gradle/invoker/GradleTasksExecutor.java
@@ -21,6 +21,9 @@
 import com.android.ide.common.blame.SourceFilePosition;
 import com.android.ide.common.blame.SourcePosition;
 import com.android.ide.common.blame.parser.PatternAwareOutputParser;
+import com.android.tools.idea.fd.InstantRunBuildProgressListener;
+import com.android.tools.idea.fd.FlightRecorder;
+import com.android.tools.idea.fd.InstantRunSettings;
 import com.android.tools.idea.gradle.GradleModel;
 import com.android.tools.idea.gradle.compiler.AndroidGradleBuildConfiguration;
 import com.android.tools.idea.gradle.facet.AndroidGradleFacet;
@@ -288,6 +291,7 @@
       GradleOutputForwarder output = new GradleOutputForwarder(consoleView);
 
       BuildException buildError = null;
+      InstantRunBuildProgressListener instantRunProgressListener = null;
       ExternalSystemTaskId id = myContext.getTaskId();
       CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource();
       try {
@@ -357,6 +361,11 @@
           }
         });
 
+        if (InstantRunSettings.isInstantRunEnabled() && InstantRunSettings.isRecorderEnabled()) {
+          instantRunProgressListener = new InstantRunBuildProgressListener();
+          launcher.addProgressListener(instantRunProgressListener);
+        }
+
         launcher.run();
       }
       catch (BuildException e) {
@@ -368,6 +377,9 @@
       finally {
         myContext.dropCancellationInfoFor(id);
         String gradleOutput = output.toString();
+        if (instantRunProgressListener != null) {
+          FlightRecorder.get(myProject).saveBuildOutput(gradleOutput, instantRunProgressListener);
+        }
         Application application = ApplicationManager.getApplication();
         if (isGuiTestingMode()) {
           String testOutput = application.getUserData(GRADLE_BUILD_OUTPUT_IN_GUI_TEST_KEY);
diff --git a/android/src/com/android/tools/idea/gradle/structure/services/GradleOperations.java b/android/src/com/android/tools/idea/gradle/structure/services/GradleOperations.java
index 82382ec..f5e290c 100644
--- a/android/src/com/android/tools/idea/gradle/structure/services/GradleOperations.java
+++ b/android/src/com/android/tools/idea/gradle/structure/services/GradleOperations.java
@@ -29,6 +29,7 @@
 import com.intellij.openapi.command.WriteCommandAction;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
+import com.intellij.util.text.VersionComparatorUtil;
 import org.jetbrains.android.facet.AndroidFacet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -89,7 +90,9 @@
         for (ArtifactDependencyModel dependency : dependenciesModel.artifacts()) {
           ArtifactDependencySpec spec = ArtifactDependencySpec.create(dependency);
           for (String dependencyValue : metadata.getDependencies()) {
-            if (spec.equals(ArtifactDependencySpec.create(dependencyValue))) {
+            ArtifactDependencySpec value = ArtifactDependencySpec.create(dependencyValue);
+            // Ensure that the found version is at least the target version.
+            if (value.equalsIgnoreVersion(spec) && VersionComparatorUtil.compare(spec.version, value.version) >= 0) {
               return true;
             }
           }
diff --git a/android/src/com/android/tools/idea/npw/deprecated/ConfigureAndroidProjectPath.java b/android/src/com/android/tools/idea/npw/deprecated/ConfigureAndroidProjectPath.java
index f7b8fac..29430c7 100755
--- a/android/src/com/android/tools/idea/npw/deprecated/ConfigureAndroidProjectPath.java
+++ b/android/src/com/android/tools/idea/npw/deprecated/ConfigureAndroidProjectPath.java
@@ -108,18 +108,6 @@
     BuildToolInfo buildTool = sdkHandler.getLatestBuildTool(progress, false);
     Revision minimumRequiredBuildToolVersion = Revision.parseRevision(SdkConstants.MIN_BUILD_TOOLS_VERSION);
 
-    // TODO: remove once maven dependency downloading is available in studio
-    StudioSdkUtil.reloadRemoteSdkWithModalProgress();
-    GradleCoordinate constraintCoordinate = GradleCoordinate.parseCoordinateString(SdkConstants.CONSTRAINT_LAYOUT_LIB_ARTIFACT + ":+");
-    RepositoryPackages packages = sdkHandler.getSdkManager(progress).getPackages();
-    RepoPackage constraintPackage = SdkMavenRepository.findBestPackageMatching(constraintCoordinate, packages.getLocalPackages().values());
-    if (constraintPackage == null) {
-      constraintPackage = SdkMavenRepository.findBestPackageMatching(constraintCoordinate, packages.getRemotePackages().values());
-      if (constraintPackage != null) {
-        state.listPush(WizardConstants.INSTALL_REQUESTS_KEY, constraintPackage.getPath());
-      }
-    }
-
     if (buildTool != null && buildTool.getRevision().compareTo(minimumRequiredBuildToolVersion) >= 0) {
       state.put(WizardConstants.BUILD_TOOLS_VERSION_KEY, buildTool.getRevision().toString());
     }
diff --git a/android/src/com/android/tools/idea/rendering/RenderErrorPanel.java b/android/src/com/android/tools/idea/rendering/RenderErrorPanel.java
index 3f099a8..cc63a13 100644
--- a/android/src/com/android/tools/idea/rendering/RenderErrorPanel.java
+++ b/android/src/com/android/tools/idea/rendering/RenderErrorPanel.java
@@ -218,6 +218,11 @@
       return;
     }
 
+    if (myHTMLViewer == null) {
+      // Already disposed
+      return;
+    }
+
     mySeverity = severity;
     if (html == null) {
       myResult = null;
@@ -251,6 +256,11 @@
   }
 
   private void setupStyle() {
+    if (myHTMLViewer != null) {
+      // Already disposed
+      return;
+    }
+
     // Make the scrollPane transparent
     JViewport viewPort = myScrollPane.getViewport();
     viewPort.setOpaque(false);
@@ -275,7 +285,7 @@
   }
 
   public int getPreferredHeight(@SuppressWarnings("UnusedParameters") int width) {
-    return myHTMLViewer.getPreferredSize().height;
+    return myHTMLViewer != null ? myHTMLViewer.getPreferredSize().height : 0;
   }
 
   @Nullable
@@ -1299,6 +1309,10 @@
   @SuppressWarnings({"HardCodedStringLiteral"})
   private void showEmpty() {
     UIUtil.invokeLaterIfNeeded(() -> {
+      if (myHTMLViewer == null) {
+        // Already disposed
+        return;
+      }
       try {
         myHTMLViewer.read(new StringReader("<html><body></body></html>"), null);
       }
diff --git a/android/src/com/android/tools/idea/run/AndroidLaunchTasksProviderFactory.java b/android/src/com/android/tools/idea/run/AndroidLaunchTasksProviderFactory.java
index dcee05d..d7891c9 100644
--- a/android/src/com/android/tools/idea/run/AndroidLaunchTasksProviderFactory.java
+++ b/android/src/com/android/tools/idea/run/AndroidLaunchTasksProviderFactory.java
@@ -15,14 +15,14 @@
  */
 package com.android.tools.idea.run;
 
-import com.android.tools.idea.fd.InstantRunBuildAnalyzer;
-import com.android.tools.idea.fd.InstantRunContext;
-import com.android.tools.idea.fd.InstantRunStatsService;
+import com.android.tools.fd.client.InstantRunBuildInfo;
+import com.android.tools.idea.fd.*;
 import com.android.tools.idea.run.tasks.LaunchTasksProvider;
 import com.android.tools.idea.run.tasks.LaunchTasksProviderFactory;
 import com.android.tools.idea.run.tasks.UpdateSessionTasksProvider;
 import com.intellij.execution.process.ProcessHandler;
 import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.openapi.project.Project;
 import org.jetbrains.android.facet.AndroidFacet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -33,6 +33,7 @@
   private final AndroidFacet myFacet;
   private final ApplicationIdProvider myApplicationIdProvider;
   private final ApkProvider myApkProvider;
+  private final DeviceFutures myDeviceFutures;
   private final LaunchOptions myLaunchOptions;
   private final ProcessHandler myPreviousSessionProcessHandler;
   private final InstantRunContext myInstantRunContext;
@@ -42,6 +43,7 @@
                                            @NotNull AndroidFacet facet,
                                            @NotNull ApplicationIdProvider applicationIdProvider,
                                            @NotNull ApkProvider apkProvider,
+                                           @NotNull DeviceFutures deviceFutures,
                                            @NotNull LaunchOptions launchOptions,
                                            @Nullable ProcessHandler processHandler,
                                            @Nullable InstantRunContext instantRunContext) {
@@ -50,6 +52,7 @@
     myFacet = facet;
     myApplicationIdProvider = applicationIdProvider;
     myApkProvider = apkProvider;
+    myDeviceFutures = deviceFutures;
     myLaunchOptions = launchOptions;
     myPreviousSessionProcessHandler = processHandler;
     myInstantRunContext = instantRunContext;
@@ -58,11 +61,20 @@
   @NotNull
   @Override
   public LaunchTasksProvider get() {
-    InstantRunStatsService.get(myEnv.getProject()).notifyDeployStarted();
+    Project project = myEnv.getProject();
+    InstantRunStatsService.get(project).notifyDeployStarted();
 
     InstantRunBuildAnalyzer analyzer = null;
-    if (myInstantRunContext != null && myInstantRunContext.getInstantRunBuildInfo() != null) {
-      analyzer = new InstantRunBuildAnalyzer(myEnv.getProject(), myInstantRunContext, myPreviousSessionProcessHandler);
+    InstantRunBuildInfo instantRunBuildInfo = myInstantRunContext != null ? myInstantRunContext.getInstantRunBuildInfo() : null;
+    if (instantRunBuildInfo != null) {
+      analyzer = new InstantRunBuildAnalyzer(project, myInstantRunContext, myPreviousSessionProcessHandler);
+
+      if (InstantRunSettings.isRecorderEnabled()) {
+        if (!myDeviceFutures.getDevices().isEmpty()) { // Instant Run is guaranteed to be for exactly 1 device
+          FlightRecorder.get(project).setLaunchTarget(myDeviceFutures.getDevices().get(0));
+        }
+        FlightRecorder.get(project).saveBuildInfo(instantRunBuildInfo);
+      }
     }
 
     if (analyzer != null && analyzer.canReuseProcessHandler()) {
diff --git a/android/src/com/android/tools/idea/run/AndroidRunConfigurationBase.java b/android/src/com/android/tools/idea/run/AndroidRunConfigurationBase.java
index 1c90943..2ea3399 100755
--- a/android/src/com/android/tools/idea/run/AndroidRunConfigurationBase.java
+++ b/android/src/com/android/tools/idea/run/AndroidRunConfigurationBase.java
@@ -59,7 +59,6 @@
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.WriteExternalException;
 import icons.AndroidIcons;
-import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
 import org.jdom.Element;
 import org.jetbrains.android.actions.AndroidEnableAdbServiceAction;
 import org.jetbrains.android.facet.AndroidFacet;
@@ -77,8 +76,6 @@
 import java.util.concurrent.TimeUnit;
 
 import static com.android.tools.idea.fd.gradle.InstantRunGradleSupport.*;
-import static com.android.tools.idea.fd.gradle.InstantRunGradleSupport.LEGACY_MULTIDEX_REQUIRES_ART;
-import static com.android.tools.idea.fd.gradle.InstantRunGradleSupport.VARIANT_DOES_NOT_SUPPORT_INSTANT_RUN;
 import static com.android.tools.idea.gradle.util.Projects.requiredAndroidModelMissing;
 
 public abstract class AndroidRunConfigurationBase extends ModuleBasedConfiguration<JavaRunConfigurationModule> implements
@@ -497,7 +494,7 @@
 
     ApkProvider apkProvider = getApkProvider(facet, applicationIdProvider);
     LaunchTasksProviderFactory providerFactory =
-      new AndroidLaunchTasksProviderFactory(this, env, facet, applicationIdProvider, apkProvider, launchOptions, processHandler,
+      new AndroidLaunchTasksProviderFactory(this, env, facet, applicationIdProvider, apkProvider, deviceFutures, launchOptions, processHandler,
                                             instantRunContext);
 
     InstantRunStatsService.get(project).notifyBuildStarted();
diff --git a/android/src/com/android/tools/idea/templates/recipe/DefaultRecipeExecutor.java b/android/src/com/android/tools/idea/templates/recipe/DefaultRecipeExecutor.java
index aa860d9..049e20a 100644
--- a/android/src/com/android/tools/idea/templates/recipe/DefaultRecipeExecutor.java
+++ b/android/src/com/android/tools/idea/templates/recipe/DefaultRecipeExecutor.java
@@ -146,7 +146,7 @@
       DependenciesModel buildscriptDependencies = buildModel.buildscript().dependencies();
       ArtifactDependencyModel targetDependencyModel = null;
       for (ArtifactDependencyModel dependencyModel : buildscriptDependencies.artifacts(CLASSPATH_CONFIGURATION_NAME)) {
-        if(equalsIgnoreVersion(toBeAddedDependency, ArtifactDependencySpec.create(dependencyModel))) {
+        if (toBeAddedDependency.equalsIgnoreVersion(ArtifactDependencySpec.create(dependencyModel))) {
           targetDependencyModel = dependencyModel;
         }
       }
@@ -175,13 +175,6 @@
     myNeedsGradleSync = true;
   }
 
-  private static boolean equalsIgnoreVersion(@NotNull ArtifactDependencySpec spec1, @NotNull ArtifactDependencySpec spec2) {
-    return Objects.equal(spec1.name, spec2.name) &&
-           Objects.equal(spec1.group, spec2.group) &&
-           Objects.equal(spec1.classifier, spec2.classifier) &&
-           Objects.equal(spec1.extension, spec2.extension);
-  }
-
   @NotNull
   private static String formatClasspath(@NotNull String dependency) {
     return "buildscript {" + LINE_SEPARATOR +
diff --git a/android/src/org/jetbrains/android/util/AndroidResourceUtil.java b/android/src/org/jetbrains/android/util/AndroidResourceUtil.java
index 20f0135..18fb639 100644
--- a/android/src/org/jetbrains/android/util/AndroidResourceUtil.java
+++ b/android/src/org/jetbrains/android/util/AndroidResourceUtil.java
@@ -1433,6 +1433,11 @@
     assert rootTag != null;
     final XmlElementFactory elementFactory = XmlElementFactory.getInstance(file.getProject());
 
+    if (StringUtil.isEmpty(namespaceUri)) {
+      // The style attribute has an empty namespaceUri:
+      return "";
+    }
+
     String prefix = rootTag.getPrefixByNamespace(namespaceUri);
     if (prefix != null) {
       return prefix;
diff --git a/android/testData/projects/projectWithDataBinding/app/src/main/res/layout/no_variable_layout.xml b/android/testData/projects/projectWithDataBinding/app/src/main/res/layout/no_variable_layout.xml
new file mode 100644
index 0000000..94ac371
--- /dev/null
+++ b/android/testData/projects/projectWithDataBinding/app/src/main/res/layout/no_variable_layout.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<layout>
+  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:tools="http://schemas.android.com/tools"
+                  android:layout_width="match_parent"
+                  android:layout_height="match_parent"
+                  android:paddingLeft="@dimen/activity_horizontal_margin"
+                  android:paddingRight="@dimen/activity_horizontal_margin"
+                  android:paddingTop="@dimen/activity_vertical_margin"
+                  android:paddingBottom="@dimen/activity_vertical_margin"
+                  tools:context="com.android.example.appwithdatabinding.MainActivity">
+
+    <TextView
+        android:id="@+id/view1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+    <View
+        android:id="@+id/view2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+  </RelativeLayout>
+</layout>
diff --git a/android/testSrc/com/android/tools/idea/fd/FlightRecorderTest.java b/android/testSrc/com/android/tools/idea/fd/FlightRecorderTest.java
new file mode 100644
index 0000000..bcc664c
--- /dev/null
+++ b/android/testSrc/com/android/tools/idea/fd/FlightRecorderTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.idea.fd;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import static org.junit.Assert.*;
+
+public class FlightRecorderTest {
+  @Rule
+  public TemporaryFolder myFolder = new TemporaryFolder();
+
+  @Test
+  public void folderNameConversions() {
+    LocalDateTime dateTime = LocalDateTime.parse("2016-09-23T11:13:41");
+    assertEquals("2016-09-23T11.13.41", FlightRecorder.timeStampToFolder(dateTime));
+    assertEquals(dateTime, FlightRecorder.folderToTimeStamp(Paths.get("/foo/bar/2016-09-23T11.13.41")));
+  }
+
+  @Test
+  public void trimOldLogs() throws IOException {
+    LocalDateTime now = LocalDateTime.now();
+    Random random = new Random(System.currentTimeMillis());
+
+    // create a bunch of log folders at different times in the past
+    List<LocalDateTime> instants = new ArrayList<>();
+    for (int i = 0; i < 10; i++) {
+      LocalDateTime instant = now.minus(random.nextInt(500), ChronoUnit.SECONDS);
+      instants.add(instant);
+
+      // create a log folder and add some logs within it
+      File logFolder = myFolder.newFolder(FlightRecorder.timeStampToFolder(instant));
+      Files.write("", new File(logFolder, "build.log"), Charsets.UTF_8);
+    }
+
+    Collections.sort(instants);
+
+    int count = 3;
+    FlightRecorder.trimOldLogs(myFolder.getRoot().toPath(), count);
+
+    for (int i = 0; i < instants.size() - count; i++) {
+      File f = new File(myFolder.getRoot(), FlightRecorder.timeStampToFolder(instants.get(i)));
+
+      if (i < instants.size() - count) {
+        assertFalse("Stale log folder still exists", f.exists());
+      } else {
+        assertTrue("New log folder unexpectedly trimmed", f.exists());
+      }
+    }
+  }
+}
diff --git a/android/testSrc/com/android/tools/idea/fd/InstantRunBuilderTest.java b/android/testSrc/com/android/tools/idea/fd/InstantRunBuilderTest.java
index 646c3ea..1f81f06 100644
--- a/android/testSrc/com/android/tools/idea/fd/InstantRunBuilderTest.java
+++ b/android/testSrc/com/android/tools/idea/fd/InstantRunBuilderTest.java
@@ -159,8 +159,8 @@
     myInstantRunClientDelegate = createInstantRunClientDelegate();
 
     myBuilder =
-      new InstantRunBuilder(myDevice, myInstantRunContext, myRunConfigContext, myTasksProvider, ourRunAsSupported, myInstalledApkCache,
-                            myInstantRunClientDelegate);
+      new InstantRunBuilder(myDevice, myInstantRunContext, myRunConfigContext, myTasksProvider, false,
+                            ourRunAsSupported, myInstalledApkCache, myInstantRunClientDelegate);
   }
 
   @NotNull
@@ -186,8 +186,8 @@
   @Test
   public void cleanBuildIfNoDevice() throws Exception {
     InstantRunBuilder builder =
-      new InstantRunBuilder(null, myInstantRunContext, myRunConfigContext, myTasksProvider, ourRunAsSupported, myInstalledApkCache,
-                            myInstantRunClientDelegate);
+      new InstantRunBuilder(null, myInstantRunContext, myRunConfigContext, myTasksProvider, false,
+                            ourRunAsSupported, myInstalledApkCache, myInstantRunClientDelegate);
     builder.build(myTaskRunner, Arrays.asList("-Pdevice.api=14", "-Pprofiling=on"));
     assertEquals(
       "gradlew -Pdevice.api=14 -Pprofiling=on -Pandroid.optional.compilation=INSTANT_DEV,FULL_APK clean :app:gen :app:assemble",
@@ -337,8 +337,8 @@
     setUpDeviceForHotSwap();
 
     myBuilder =
-      new InstantRunBuilder(myDevice, myInstantRunContext, myRunConfigContext, myTasksProvider, ourRunAsNotSupported, myInstalledApkCache,
-                            myInstantRunClientDelegate);
+      new InstantRunBuilder(myDevice, myInstantRunContext, myRunConfigContext, myTasksProvider, false,
+                            ourRunAsNotSupported, myInstalledApkCache, myInstantRunClientDelegate);
     myBuilder.build(myTaskRunner, Collections.emptyList());
     assertEquals(
       "gradlew -Pandroid.optional.compilation=INSTANT_DEV,FULL_APK :app:assemble",
@@ -411,6 +411,17 @@
       myTaskRunner.getBuilds());
   }
 
+  @Test
+  public void flightRecorderOptions() throws Exception {
+    InstantRunBuilder builder =
+      new InstantRunBuilder(null, myInstantRunContext, myRunConfigContext, myTasksProvider, true,
+                            ourRunAsSupported, myInstalledApkCache, myInstantRunClientDelegate);
+    builder.build(myTaskRunner, Arrays.asList("-Pdevice.api=14", "-Pprofiling=on"));
+    assertEquals(
+      "gradlew -Pdevice.api=14 -Pprofiling=on -Pandroid.optional.compilation=INSTANT_DEV,FULL_APK --info clean :app:gen :app:assemble",
+      myTaskRunner.getBuilds());
+  }
+
   private void setUpDeviceForHotSwap() {
     HashCode resourcesHash = HashCode.fromInt(1);
     myInstalledPatchCache.setInstalledManifestResourcesHash(myDevice, APPLICATION_ID, resourcesHash);
diff --git a/android/testSrc/com/android/tools/idea/fd/InstantRunNotificationProviderTest.java b/android/testSrc/com/android/tools/idea/fd/InstantRunNotificationProviderTest.java
index a16c408..f287564 100644
--- a/android/testSrc/com/android/tools/idea/fd/InstantRunNotificationProviderTest.java
+++ b/android/testSrc/com/android/tools/idea/fd/InstantRunNotificationProviderTest.java
@@ -64,4 +64,17 @@
     InstantRunNotificationProvider provider = new InstantRunNotificationProvider(buildSelection, DeployType.HOTSWAP, "");
     assertEquals(AndroidBundle.message("instant.run.notification.hotswap", ""), provider.getNotificationText());
   }
+
+  @Test
+  public void multiProcessOnApi19() {
+    BuildSelection buildSelection = new BuildSelection(BuildMode.COLD, BuildCause.APP_USES_MULTIPLE_PROCESSES);
+
+    // if we generated an apk, then we shouldn't talk about multi process
+    InstantRunNotificationProvider provider = new InstantRunNotificationProvider(buildSelection, DeployType.FULLAPK, "");
+    assertEquals("Instant Run re-installed and restarted the app", provider.getNotificationText());
+
+    // but if we generated cold swap patches, then we should specify that we did so because of multi process
+    provider = new InstantRunNotificationProvider(buildSelection, DeployType.DEX, "");
+    assertEquals(AndroidBundle.message("instant.run.notification.coldswap.multiprocess"), provider.getNotificationText());
+  }
 }
diff --git a/android/testSrc/com/android/tools/idea/gradle/customizer/dependency/TransitiveDependencySetupTest.java b/android/testSrc/com/android/tools/idea/gradle/customizer/dependency/TransitiveDependencySetupTest.java
index 41b376a..ff09939 100644
--- a/android/testSrc/com/android/tools/idea/gradle/customizer/dependency/TransitiveDependencySetupTest.java
+++ b/android/testSrc/com/android/tools/idea/gradle/customizer/dependency/TransitiveDependencySetupTest.java
@@ -35,6 +35,12 @@
  * Integration test that verifies that transitive dependencies are set up correctly.
  */
 public class TransitiveDependencySetupTest extends AndroidGradleTestCase {
+
+  @Override
+  protected void runTest() throws Throwable {
+    // Ignore this whole class. See http://b.android.com/221883.
+  }
+
   @Override
   protected void tearDown() throws Exception {
     super.tearDown();
@@ -170,4 +176,4 @@
     }
     return allLibraries;
   }
-}
\ No newline at end of file
+}
diff --git a/android/testSrc/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpecTest.java b/android/testSrc/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpecTest.java
index eeb2c42..22d7e51 100644
--- a/android/testSrc/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpecTest.java
+++ b/android/testSrc/com/android/tools/idea/gradle/dsl/model/dependencies/ArtifactDependencySpecTest.java
@@ -96,4 +96,36 @@
     myDependency.extension = "ext";
     assertEquals("group:name:version:classifier@ext", myDependency.compactNotation());
   }
+
+  @Test
+  public void testEqualsFalse() {
+    myDependency = ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-alpha4:jdk15@jar");
+    ArtifactDependencySpec theirDependency =
+      ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-beta1:jdk15@jar");
+    assertFalse(myDependency.equals(theirDependency));
+  }
+
+  @Test
+  public void testEqualsTrue() {
+    myDependency = ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-alpha4:jdk15@jar");
+    ArtifactDependencySpec theirDependency =
+      ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-alpha4:jdk15@jar");
+    assertTrue(myDependency.equals(theirDependency));
+  }
+
+  @Test
+  public void testEqualsIgnoreVersionFalse() {
+    myDependency = ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-alpha4:jdk15@jar");
+    ArtifactDependencySpec theirDependency =
+      ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-alpha4:jdk16@jar");
+    assertFalse(myDependency.equalsIgnoreVersion(theirDependency));
+  }
+
+  @Test
+  public void testeEqualsIgnoreVersionTrue() {
+    myDependency = ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-alpha4:jdk15@jar");
+    ArtifactDependencySpec theirDependency =
+      ArtifactDependencySpec.create("org.gradle.test.classifiers:service:1.0.0-beta1:jdk15@jar");
+    assertTrue(myDependency.equalsIgnoreVersion(theirDependency));
+  }
 }
\ No newline at end of file
diff --git a/android/testSrc/com/android/tools/idea/gradle/testing/AndroidJunitPatcherWithTestArtifactTest.java b/android/testSrc/com/android/tools/idea/gradle/testing/AndroidJunitPatcherWithTestArtifactTest.java
index 2cf2027..8d9b61e 100644
--- a/android/testSrc/com/android/tools/idea/gradle/testing/AndroidJunitPatcherWithTestArtifactTest.java
+++ b/android/testSrc/com/android/tools/idea/gradle/testing/AndroidJunitPatcherWithTestArtifactTest.java
@@ -25,7 +25,8 @@
 
 public class AndroidJunitPatcherWithTestArtifactTest extends AndroidGradleTestCase {
 
-  public void testRemoveAndroidTestClasspath() throws Exception {
+  // See http://b.android.com/221883.
+  public void ignore_testRemoveAndroidTestClasspath() throws Exception {
     loadProject("projects/sync/multiproject", false);
     JUnitPatcher myPatcher = new AndroidJunitPatcher();
 
diff --git a/android/testSrc/com/android/tools/idea/gradle/testing/TestArtifactSearchScopesTest.java b/android/testSrc/com/android/tools/idea/gradle/testing/TestArtifactSearchScopesTest.java
index ecab3ff..1048414 100644
--- a/android/testSrc/com/android/tools/idea/gradle/testing/TestArtifactSearchScopesTest.java
+++ b/android/testSrc/com/android/tools/idea/gradle/testing/TestArtifactSearchScopesTest.java
@@ -71,7 +71,8 @@
     assertFalse(scopes.getAndroidTestExcludeScope().accept(module3RsRoot));
   }
 
-  public void testLibrariesExcluding() throws Exception {
+  // See http://b.android.com/221883.
+  public void ignore_testLibrariesExcluding() throws Exception {
     TestArtifactSearchScopes scopes = loadMultiProjectAndTestScopes();
 
     LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(myFixture.getProject());
diff --git a/android/testSrc/com/android/tools/idea/run/GradleApkProviderTest.java b/android/testSrc/com/android/tools/idea/run/GradleApkProviderTest.java
index f369456..40fd093 100644
--- a/android/testSrc/com/android/tools/idea/run/GradleApkProviderTest.java
+++ b/android/testSrc/com/android/tools/idea/run/GradleApkProviderTest.java
@@ -69,7 +69,7 @@
     if (modelVersion != null) {
       if (modelVersion.compareIgnoringQualifiers("2.2.0") < 0
           // Packaging reverted in alpha4?
-          || modelVersion.compareTo("2.2.0-alpha4") >= 0) {
+          || modelVersion.compareTo("2.2.0-alpha4") == 0) {
         assertThat(path).endsWith(getName() + "-debug-androidTest-unaligned.apk");
       }
       else {
diff --git a/android/testSrc/org/jetbrains/android/databinding/GeneratedCodeMatchTest.java b/android/testSrc/org/jetbrains/android/databinding/GeneratedCodeMatchTest.java
index 4dbe588..1d8257d 100644
--- a/android/testSrc/org/jetbrains/android/databinding/GeneratedCodeMatchTest.java
+++ b/android/testSrc/org/jetbrains/android/databinding/GeneratedCodeMatchTest.java
@@ -71,7 +71,7 @@
     }
   }
 
-  public void ignore_testGeneratedCodeMatch() throws Exception {
+  public void testGeneratedCodeMatch() throws Exception {
     File projectFolder = virtualToIoFile(myFixture.getProject().getBaseDir());
     createGradlePropertiesFile(projectFolder);
     loadProject("projects/projectWithDataBinding");
diff --git a/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintModel.java b/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintModel.java
index 7c9b10b..8a71977 100644
--- a/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintModel.java
+++ b/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintModel.java
@@ -528,14 +528,16 @@
       }
     }
 
+    boolean saveXML = false;
+
     // Make sure the components exist
     for (NlComponent component : components) {
-      createSolverWidgetFromComponent(component);
+      saveXML |= createSolverWidgetFromComponent(component);
     }
 
     // Now update our widget from the list of components...
     for (NlComponent component : components) {
-      updateSolverWidgetFromComponent(component, deepUpdate);
+      saveXML |= updateSolverWidgetFromComponent(component, deepUpdate);
     }
 
     if (USE_GUIDELINES_DURING_DND) {
@@ -547,7 +549,9 @@
     }
 
     // Update the ConstraintLayout instances
-    updateConstraintLayoutRoots(myWidgetsScene.getRoot());
+    if (!saveXML) {
+      updateConstraintLayoutRoots(myWidgetsScene.getRoot());
+    }
 
     // Finally, layout using our model.
     WidgetContainer root = myWidgetsScene.getRoot();
@@ -557,6 +561,9 @@
         root.layout();
       }
     }
+    if (saveXML) {
+      saveToXML(true);
+    }
   }
 
   /**
@@ -582,15 +589,17 @@
    * Create the widget associated to a component if necessary.
    *
    * @param component the component we want to represent
+   * @return true if we need to save the XML
    */
-  private void createSolverWidgetFromComponent(@NotNull NlComponent component) {
+  private boolean createSolverWidgetFromComponent(@NotNull NlComponent component) {
     ConstraintWidget widget = myWidgetsScene.getWidget(component);
+    boolean saveXML = false;
     if (widget != null && isConstraintLayout(component)) {
       if (!(widget instanceof ConstraintWidgetContainer)) {
         if (widget instanceof WidgetContainer) {
           ConstraintWidgetContainer container = new ConstraintWidgetContainer();
           myWidgetsScene.transformContainerToContainer((WidgetContainer)widget, container);
-          setupConstraintWidget(component, container);
+          saveXML = setupConstraintWidget(component, container);
           widget = container;
         }
         else {
@@ -640,7 +649,7 @@
           }
         }
       }
-      setupConstraintWidget(component, widget);
+      saveXML |= setupConstraintWidget(component, widget);
       myWidgetsScene.setWidget(widget);
       if (USE_GUIDELINES_DURING_DND) {
         if (dropWidget) {
@@ -652,8 +661,9 @@
     }
 
     for (NlComponent child : component.getChildren()) {
-      createSolverWidgetFromComponent(child);
+      saveXML |= createSolverWidgetFromComponent(child);
     }
+    return saveXML;
   }
 
   private static boolean isConstraintLayout(@NotNull NlComponent component) {
@@ -676,8 +686,9 @@
    *
    * @param component
    * @param widget
+   * @return true if we need to save the XML
    */
-  private void setupConstraintWidget(@NotNull NlComponent component, ConstraintWidget widget) {
+  private boolean setupConstraintWidget(@NotNull NlComponent component, ConstraintWidget widget) {
     WidgetDecorator blueprintDecorator = createDecorator(component, widget);
     WidgetDecorator androidDecorator = createDecorator(component, widget);
     blueprintDecorator.setStateModel(this);
@@ -692,7 +703,7 @@
     companion.setWidgetTag(component);
     widget.setCompanionWidget(companion);
     widget.setDebugName(component.getId());
-    ConstraintUtilities.updateWidget(this, widget, component);
+    return ConstraintUtilities.updateWidget(this, widget, component);
   }
 
   /**
@@ -740,7 +751,7 @@
    * @param component  the component we want to update from
    * @param deepUpdate do a thorough update or not
    */
-  private void updateSolverWidgetFromComponent(@NotNull NlComponent component, boolean deepUpdate) {
+  private boolean updateSolverWidgetFromComponent(@NotNull NlComponent component, boolean deepUpdate) {
     ConstraintWidget widget = myWidgetsScene.getWidget(component);
     if (USE_GUIDELINES_DURING_DND) {
       if (myDragDropWidget != null) {
@@ -749,22 +760,23 @@
         if (companion.getWidgetModel() == component) {
           saveToXML(true); // will retrigger an update
           myDragDropWidget = null;
-          return;
+          return false;
         }
       }
     }
-    ConstraintUtilities.updateWidget(this, widget, component);
+    boolean saveXML = ConstraintUtilities.updateWidget(this, widget, component);
     for (NlComponent child : component.getChildren()) {
-      updateSolverWidgetFromComponent(child, deepUpdate);
+      saveXML |= updateSolverWidgetFromComponent(child, deepUpdate);
     }
+    return saveXML;
   }
 
   /**
    * Traverse the hierarchy to find all ConstraintLayout instances
    * and update them. We set all the wrap_content sizes of the ConstraintLayout children
    * from layout lib
+   *  @param container
    *
-   * @param container
    */
   private void updateConstraintLayoutRoots(WidgetContainer container) {
     if (container == null) {
diff --git a/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintUtilities.java b/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintUtilities.java
index 86421a3..c94e4b3 100644
--- a/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintUtilities.java
+++ b/designer/src/com/android/tools/idea/uibuilder/handlers/constraint/ConstraintUtilities.java
@@ -29,6 +29,7 @@
 import com.android.tools.sherpa.drawing.decorator.TextWidget;
 import com.android.tools.sherpa.drawing.decorator.WidgetDecorator;
 import com.android.tools.sherpa.interaction.WidgetInteractionTargets;
+import com.android.tools.sherpa.scout.Scout;
 import com.android.tools.sherpa.structure.WidgetCompanion;
 import com.android.tools.sherpa.structure.WidgetsScene;
 import com.intellij.openapi.application.ApplicationManager;
@@ -39,6 +40,7 @@
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.ArrayList;
 import java.util.Collection;
 
 /**
@@ -46,6 +48,9 @@
  */
 public class ConstraintUtilities {
 
+  final static int MINIMUM_SIZE = 48; // in dp
+  final static int MINIMUM_SIZE_EXPAND = 6; // in dp
+
   /**
    * Return the corresponding margin attribute for the given anchor
    *
@@ -908,12 +913,13 @@
    * @param constraintModel the constraint model we are working with
    * @param widget          constraint widget
    * @param component       the model component
+   * @return true if need to save the xml
    */
-  static void updateWidget(@NotNull ConstraintModel constraintModel,
+  static boolean updateWidget(@NotNull ConstraintModel constraintModel,
                            @Nullable ConstraintWidget widget,
                            @Nullable NlComponent component) {
     if (component == null || widget == null) {
-      return;
+      return false;
     }
 
     AttributesTransaction attributes = component.startAttributeTransaction();
@@ -969,53 +975,7 @@
       }
     }
 
-    // FIXME: need to agree on the correct magic value for this rather than simply using zero.
-    String layout_width = attributes.getAttribute(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_WIDTH);
-    if (component.w == 0 || getLayoutDimensionDpValue(component, layout_width) == 0) {
-      widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
-    }
-    else if (layout_width != null && layout_width.equalsIgnoreCase(SdkConstants.VALUE_WRAP_CONTENT)) {
-      widget.setWrapWidth(widget.getWidth());
-      widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.WRAP_CONTENT);
-    }
-    else if (layout_width != null && layout_width.equalsIgnoreCase(SdkConstants.VALUE_MATCH_PARENT)) {
-      widget.setWrapWidth(widget.getWidth());
-      if (isWidgetInsideConstraintLayout(widget)) {
-        if (widget.getAnchor(ConstraintAnchor.Type.LEFT).isConnected()
-            && widget.getAnchor(ConstraintAnchor.Type.RIGHT).isConnected()) {
-          widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
-        }
-        else {
-          widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
-        }
-      }
-    }
-    else {
-      widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
-    }
-    String layout_height = attributes.getAttribute(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_HEIGHT);
-    if (component.h == 0 || getLayoutDimensionDpValue(component, layout_height) == 0) {
-      widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
-    }
-    else if (layout_height != null && layout_height.equalsIgnoreCase(SdkConstants.VALUE_WRAP_CONTENT)) {
-      widget.setWrapHeight(widget.getHeight());
-      widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.WRAP_CONTENT);
-    }
-    else if (layout_height != null && layout_height.equalsIgnoreCase(SdkConstants.VALUE_MATCH_PARENT)) {
-      widget.setWrapHeight(widget.getHeight());
-      if (isWidgetInsideConstraintLayout(widget)) {
-        if ((widget.getAnchor(ConstraintAnchor.Type.TOP).isConnected()
-             && widget.getAnchor(ConstraintAnchor.Type.BOTTOM).isConnected())) {
-          widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
-        }
-        else {
-          widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
-        }
-      }
-    }
-    else {
-      widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
-    }
+    // First set the origin of the widget
 
     int x = constraintModel.pxToDp(component.x);
     int y = constraintModel.pxToDp(component.y);
@@ -1058,6 +1018,92 @@
       widget.forceUpdateDrawPosition();
     }
 
+    boolean overrideDimension = false;
+
+    // FIXME: need to agree on the correct magic value for this rather than simply using zero.
+    String layout_width = attributes.getAttribute(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_WIDTH);
+    if (component.w == 0 || getLayoutDimensionDpValue(component, layout_width) == 0) {
+      widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
+    }
+    else if (layout_width != null && layout_width.equalsIgnoreCase(SdkConstants.VALUE_WRAP_CONTENT)) {
+      if (widget.getWidth() < MINIMUM_SIZE && widget instanceof WidgetContainer
+          && ((WidgetContainer) widget).getChildren().size() == 0) {
+        widget.setWidth(MINIMUM_SIZE);
+        widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
+        overrideDimension = true;
+      } else {
+        widget.setWrapWidth(widget.getWidth());
+        widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.WRAP_CONTENT);
+      }
+    }
+    else if (layout_width != null && layout_width.equalsIgnoreCase(SdkConstants.VALUE_MATCH_PARENT)) {
+      if (isWidgetInsideConstraintLayout(widget)) {
+        if (widget.getAnchor(ConstraintAnchor.Type.LEFT).isConnected()
+            && widget.getAnchor(ConstraintAnchor.Type.RIGHT).isConnected()) {
+          widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
+        }
+        else {
+          widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
+          widget.setWidth(MINIMUM_SIZE_EXPAND);
+          int height = widget.getHeight();
+          ConstraintWidget.DimensionBehaviour verticalBehaviour = widget.getVerticalDimensionBehaviour();
+          if (height <= 1 && widget instanceof WidgetContainer) {
+            widget.setHeight(MINIMUM_SIZE_EXPAND);
+          }
+          ArrayList<ConstraintWidget> widgets = new ArrayList<>();
+          widgets.add(widget);
+          Scout.arrangeWidgets(Scout.Arrange.ExpandHorizontally, widgets, true);
+          widget.setHeight(height);
+          widget.setVerticalDimensionBehaviour(verticalBehaviour);
+          overrideDimension = true;
+        }
+      }
+    }
+    else {
+      widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
+    }
+    String layout_height = attributes.getAttribute(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_HEIGHT);
+    if (component.h == 0 || getLayoutDimensionDpValue(component, layout_height) == 0) {
+      widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
+    }
+    else if (layout_height != null && layout_height.equalsIgnoreCase(SdkConstants.VALUE_WRAP_CONTENT)) {
+      if (widget.getHeight() < MINIMUM_SIZE && widget instanceof WidgetContainer
+          && ((WidgetContainer) widget).getChildren().size() == 0) {
+        widget.setHeight(MINIMUM_SIZE);
+        widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
+        overrideDimension = true;
+      } else {
+        widget.setWrapHeight(widget.getHeight());
+        widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.WRAP_CONTENT);
+      }
+    }
+    else if (layout_height != null && layout_height.equalsIgnoreCase(SdkConstants.VALUE_MATCH_PARENT)) {
+      if (isWidgetInsideConstraintLayout(widget)) {
+        if ((widget.getAnchor(ConstraintAnchor.Type.TOP).isConnected()
+             && widget.getAnchor(ConstraintAnchor.Type.BOTTOM).isConnected())) {
+          widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
+        }
+        else {
+          widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
+          widget.setHeight(MINIMUM_SIZE_EXPAND);
+          int width = widget.getWidth();
+          ConstraintWidget.DimensionBehaviour horizontalBehaviour = widget.getHorizontalDimensionBehaviour();
+          if (width <= 1 && widget instanceof WidgetContainer) {
+            widget.setWidth(MINIMUM_SIZE_EXPAND);
+          }
+          ArrayList<ConstraintWidget> widgets = new ArrayList<>();
+          widgets.add(widget);
+          Scout.arrangeWidgets(Scout.Arrange.ExpandVertically, widgets, true);
+          widget.setWidth(width);
+          widget.setHorizontalDimensionBehaviour(horizontalBehaviour);
+          overrideDimension = true;
+        }
+      }
+    }
+    else {
+      widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
+    }
+
     widget.setBaselineDistance(constraintModel.pxToDp(component.getBaseline()));
     widget.resetAnchors();
 
@@ -1136,6 +1182,8 @@
       //noinspection ConstantConditions
       textWidget.setTextSize(constraintModel.pxToDp(size));
     }
+
+    return overrideDimension; // if true, need to update the XML
   }
 
   /**
diff --git a/designer/src/com/android/tools/idea/uibuilder/property/NlIdPropertyItem.java b/designer/src/com/android/tools/idea/uibuilder/property/NlIdPropertyItem.java
index 62476b2..fc005f6 100644
--- a/designer/src/com/android/tools/idea/uibuilder/property/NlIdPropertyItem.java
+++ b/designer/src/com/android/tools/idea/uibuilder/property/NlIdPropertyItem.java
@@ -23,6 +23,7 @@
 import com.intellij.openapi.ui.DialogBuilder;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.xml.XmlAttribute;
 import com.intellij.psi.xml.XmlAttributeValue;
 import com.intellij.psi.xml.XmlTag;
@@ -157,6 +158,6 @@
       }
     }
 
-    super.setValue(value != null ? NEW_ID_PREFIX + value : null);
+    super.setValue(!StringUtil.isEmpty(newId) ? NEW_ID_PREFIX + newId : null);
   }
 }
diff --git a/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java b/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java
index 827cdf2..6d1476b 100644
--- a/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java
+++ b/designer/src/com/android/tools/idea/uibuilder/property/NlPropertiesPanel.java
@@ -46,6 +46,8 @@
 public class NlPropertiesPanel extends JPanel implements ViewAllPropertiesAction.Model {
   private static final String CARD_ADVANCED = "table";
   private static final String CARD_DEFAULT = "default";
+  private static final int VERTICAL_SCROLLING_UNIT_INCREMENT = 50;
+  private static final int VERTICAL_SCROLLING_BLOCK_INCREMENT = 25;
 
   private final PTable myTable;
   private final JPanel myTablePanel;
@@ -87,7 +89,10 @@
     myCardPanel.add(CARD_DEFAULT, ScrollPaneFactory.createScrollPane(myInspectorPanel,
                                                                      ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                                                                      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER));
-    myCardPanel.add(CARD_ADVANCED, ScrollPaneFactory.createScrollPane(myTablePanel));
+    JScrollPane tableScrollPane = ScrollPaneFactory.createScrollPane(myTablePanel);
+    tableScrollPane.getVerticalScrollBar().setUnitIncrement(VERTICAL_SCROLLING_UNIT_INCREMENT);
+    tableScrollPane.getVerticalScrollBar().setBlockIncrement(VERTICAL_SCROLLING_BLOCK_INCREMENT);
+    myCardPanel.add(CARD_ADVANCED, tableScrollPane);
     myComponents = Collections.emptyList();
     myProperties = Collections.emptyList();
   }
diff --git a/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java
index 8684cf4..597f91b 100644
--- a/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java
+++ b/designer/src/com/android/tools/idea/uibuilder/property/editors/NlEnumEditor.java
@@ -104,6 +104,8 @@
       case ATTR_DROPDOWN_WIDTH:
       case ATTR_ON_CLICK:
         return true;
+      case ATTR_ID:
+        return false;
       case ATTR_STYLE:
         String tagName = property.getTagName();
         return tagName != null && StyleFilter.hasWidgetStyles(property.getModel().getProject(), property.getResolver(), tagName);
diff --git a/designer/testSrc/com/android/tools/idea/uibuilder/palette/PaletteTestCase.java b/designer/testSrc/com/android/tools/idea/uibuilder/palette/PaletteTestCase.java
index 73c803b..9573b47 100644
--- a/designer/testSrc/com/android/tools/idea/uibuilder/palette/PaletteTestCase.java
+++ b/designer/testSrc/com/android/tools/idea/uibuilder/palette/PaletteTestCase.java
@@ -575,7 +575,7 @@
   }
 
   public void assertTabItem(@NotNull Palette.BaseItem item) {
-    assertStandardView(item, TAB_ITEM, DESIGN_LIB_ARTIFACT, 1.0);
+    assertNoPreviewView(item, TAB_ITEM, DESIGN_LIB_ARTIFACT);
   }
 
   public void assertNestedScrollViewItem(@NotNull Palette.BaseItem item) {
@@ -607,7 +607,9 @@
   }
 
   public void assertTextInputLayoutItem(@NotNull Palette.BaseItem item) {
-    assertLimitedHeightLayout(item, TEXT_INPUT_LAYOUT, DESIGN_LIB_ARTIFACT);
+    checkItem(item, TEXT_INPUT_LAYOUT, STANDARD_VIEW.getTitle(TEXT_INPUT_LAYOUT), STANDARD_LAYOUT.getIcon(TEXT_INPUT_LAYOUT),
+              TEXT_INPUT_LAYOUT_XML, NO_PREVIEW, NO_PREVIEW,
+              DESIGN_LIB_ARTIFACT, NO_SCALE);
   }
 
   public void assertCardView(@NotNull Palette.BaseItem item) {
@@ -623,6 +625,17 @@
   }
 
   @Language("XML")
+  private static final String TEXT_INPUT_LAYOUT_XML =
+    "<android.support.design.widget.TextInputLayout\n" +
+    "  android:layout_width=\"match_parent\"\n" +
+    "  android:layout_height=\"wrap_content\">\n" +
+    "  <EditText\n" +
+    "    android:layout_width=\"match_parent\"\n" +
+    "    android:layout_height=\"wrap_content\"\n" +
+    "    android:hint=\"hint\" />\n" +
+    "  </android.support.design.widget.TextInputLayout>\n";
+
+  @Language("XML")
   private static final String TOOLBAR_XML =
     "<android.support.v7.widget.Toolbar\n" +
     "  android:layout_width=\"match_parent\"\n" +