Add support for JUnit tests with Hilt Plugin

Android transforms are not applied for JUnit tests which breaks when using
the Hilt Plugin and its transform. This CL adds a new custom test transform
task just for tests so that transformed classes are used in Android JUnit tests.

The task does not transform test sources, therefore temporarily allow specifying
a base class in @AndroidEntryPoint that will be used even if the superclass
validation option is given to the processor. This helps us still have test-only
Android Entry Points.

Closes https://github.com/google/dagger/pull/1811

RELNOTES=n/a

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=309143731
diff --git a/.gitignore b/.gitignore
index 59acf87..cea18de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@
 /bazel-*
 
 *.pyc
+
+.gradle
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
index 932be76..16b9519 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
+++ b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
@@ -15,8 +15,7 @@
  */
 
 apply plugin: 'com.android.application'
-// TODO(bcorso): Reenable this plugin once it works with Robolectric tests.
-// apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'dagger.hilt.android.plugin'
 
 android {
     compileSdkVersion 29
@@ -33,7 +32,13 @@
         sourceCompatibility 1.8
         targetCompatibility 1.8
     }
-    testOptions.unitTests.includeAndroidResources = true
+    testOptions {
+        unitTests.includeAndroidResources = true
+    }
+}
+
+hilt {
+    enableTransformForLocalTests = true
 }
 
 dependencies {
@@ -46,11 +51,12 @@
 
   testImplementation 'com.google.truth:truth:1.0.1'
   testImplementation 'junit:junit:4.13'
-  testImplementation 'org.robolectric:robolectric:4.3'
+  testImplementation 'org.robolectric:robolectric:4.3.1'
+  testImplementation 'androidx.core:core:1.2.0'
   testImplementation 'androidx.test.ext:junit:1.1.1'
   testImplementation 'androidx.test:runner:1.2.0'
   // TODO(bcorso): This multidex dep shouldn't be required -- it's a dep for the generated code.
-  testImplementation 'androidx.multidex:multidex:2.0.0'
+  testImplementation 'androidx.multidex:multidex:2.0.1'
   testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
   testAnnotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
   testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
index b7df457..f508e48 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
@@ -23,8 +23,8 @@
 import javax.inject.Inject;
 
 /** The settings activity of the application. */
-@AndroidEntryPoint(AppCompatActivity.class)
-public class SettingsActivity extends Hilt_SettingsActivity {
+@AndroidEntryPoint
+public class SettingsActivity extends AppCompatActivity {
   private static final String TAG = SettingsActivity.class.getSimpleName();
 
   @Inject SettingsGreeter greeter;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
index 8ad4c0d..70e2e57 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
@@ -26,8 +26,8 @@
 import javax.inject.Inject;
 
 /** The main activity of the application. */
-@AndroidEntryPoint(AppCompatActivity.class)
-public class SimpleActivity extends Hilt_SimpleActivity {
+@AndroidEntryPoint
+public class SimpleActivity extends AppCompatActivity {
   private static final String TAG = SimpleActivity.class.getSimpleName();
 
   @Inject SimpleGreeter greeter;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
index aa04fb5..b691401 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
@@ -26,8 +26,8 @@
  * utilities in {@code Hilt} in Android.
  */
 @GenerateComponents
-@AndroidEntryPoint(Application.class)
-public class SimpleApplication extends Hilt_SimpleApplication {
+@AndroidEntryPoint
+public class SimpleApplication extends Application {
 
   // Shows that we can inject ApplicationComponent bindings into an application.
   @Inject @Model String model;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
index c87f726..c07e00f 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
@@ -71,6 +71,7 @@
   }
 
   /** Test activity used to test activity injection */
+  // TODO(user): Update to extend non-gen code when test sources are also transformed.
   @AndroidEntryPoint(AppCompatActivity.class)
   public static final class TestActivity extends Hilt_Injection1Test_TestActivity {
     @Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
@@ -94,4 +95,14 @@
       scenario.onActivity(activity -> assertThat(activity.activityValue).isEqualTo(ACTIVITY_VALUE));
     }
   }
+
+  @Test
+  public void testSuperClassTransformation() {
+    try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+      scenario.onActivity(
+          activity ->
+              assertThat(activity.getClass().getSuperclass().getSimpleName())
+                  .isEqualTo("Hilt_Injection1Test_TestActivity"));
+    }
+  }
 }
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
index 4b158d5..d43dcc0 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
@@ -71,6 +71,7 @@
   }
 
   /** Test activity used to test activity injection */
+  // TODO(user): Update to extend non-gen code when test sources are also transformed.
   @AndroidEntryPoint(AppCompatActivity.class)
   public static final class TestActivity extends Hilt_Injection2Test_TestActivity {
     @Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
index 6a23a96..5c7a146 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
@@ -63,4 +63,15 @@
                   .isEqualTo("ProdUser, you are on build FakeModel."));
     }
   }
+
+  @Test
+  public void testSuperClassTransformation() {
+    try (ActivityScenario<SettingsActivity> scenario =
+        ActivityScenario.launch(SettingsActivity.class)) {
+      scenario.onActivity(
+          activity ->
+              assertThat(activity.getClass().getSuperclass().getSimpleName())
+                  .isEqualTo("Hilt_SettingsActivity"));
+    }
+  }
 }
diff --git a/java/dagger/hilt/android/example/gradle/simple/build.gradle b/java/dagger/hilt/android/example/gradle/simple/build.gradle
index e473e22..cb6d02a 100644
--- a/java/dagger/hilt/android/example/gradle/simple/build.gradle
+++ b/java/dagger/hilt/android/example/gradle/simple/build.gradle
@@ -17,6 +17,7 @@
 buildscript {
     ext {
         kotlin_version = '1.3.61'
+        agp_version = System.getenv('AGP_VERSION') ?: "3.6.3"
     }
     repositories {
         google()
@@ -24,7 +25,7 @@
         mavenLocal()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.5.3'
+        classpath "com.android.tools.build:gradle:$agp_version"
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
     }
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
index 208882c..56fcdd8 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
@@ -17,8 +17,7 @@
 apply plugin: 'com.android.library'
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-kapt'
-// TODO(bcorso): Reenable this plugin once it works with Robolectric tests.
-// apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'dagger.hilt.android.plugin'
 
 android {
     compileSdkVersion 29
@@ -39,6 +38,10 @@
  correctErrorTypes true
 }
 
+hilt {
+    enableTransformForLocalTests = true
+}
+
 dependencies {
     // This is api instead of implementation since Kotlin modules here consumed
     // by the app need to expose @kotlin.Metadata
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
index 55e004a..03b9ae9 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
@@ -23,8 +23,8 @@
 import dagger.hilt.android.AndroidEntryPoint
 import javax.inject.Inject
 
-@AndroidEntryPoint(AppCompatActivity::class)
-class FeatureActivity : Hilt_FeatureActivity() {
+@AndroidEntryPoint
+class FeatureActivity : AppCompatActivity() {
   @Inject lateinit var counter: FeatureCounter
 
   override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle.properties b/java/dagger/hilt/android/example/gradle/simple/gradle.properties
index 66e648b..646c51b 100644
--- a/java/dagger/hilt/android/example/gradle/simple/gradle.properties
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle.properties
@@ -1,3 +1,2 @@
 android.useAndroidX=true
 android.enableJetifier=true
-android.enableUnitTestBinaryResources=true
\ No newline at end of file
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
index f4d7b2b..a4b4429 100644
--- a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/BUILD b/java/dagger/hilt/android/example/gradle/simpleKotlin/BUILD
new file mode 100644
index 0000000..3671282
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/BUILD
@@ -0,0 +1,18 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+#   A skeletal Kotlin application that demonstrates wiring for an injected Application and Activity.
+
+package(default_visibility = ["//:src"])
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
new file mode 100644
index 0000000..52cce0d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'kotlin-kapt'
+
+android {
+    compileSdkVersion 29
+    buildToolsVersion "29.0.3"
+
+    defaultConfig {
+        applicationId "dagger.hilt.android.example.gradle.simpleKotlin"
+        minSdkVersion 21
+        targetSdkVersion 29
+        versionCode 1
+        versionName "1.0"
+    }
+    compileOptions {
+        sourceCompatibility 1.8
+        targetCompatibility 1.8
+    }
+    testOptions {
+        unitTests.includeAndroidResources = true
+    }
+}
+
+hilt {
+    enableTransformForLocalTests = true
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+
+    implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+    kapt 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+    implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+    kapt 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+
+    testImplementation 'com.google.truth:truth:1.0.1'
+    testImplementation 'junit:junit:4.13'
+    testImplementation 'org.robolectric:robolectric:4.3.1'
+    testImplementation 'androidx.core:core:1.1.0'
+    // TODO(bcorso): This multidex dep shouldn't be required -- it's a dep for the generated code.
+    testImplementation 'androidx.multidex:multidex:2.0.0'
+    testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+    kaptTest 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+    kaptTest 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2e274ab
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Dagger Authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="dagger.hilt.android.example.gradle.simpleKotlin">
+
+    <application
+        android:name=".KotlinApplication"
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.AppCompat.Light">
+        <activity android:name=".MainActivity" android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
new file mode 100644
index 0000000..8a85040
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityComponent
+
+@Module
+@InstallIn(ActivityComponent::class)
+object ActivityModule {
+  @UserName
+  @Provides
+  fun provideUserName(): String {
+    return "Android User"
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
new file mode 100644
index 0000000..00d9b35
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Build
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ApplicationComponent
+
+@Module
+@InstallIn(ApplicationComponent::class)
+object ApplicationModule {
+  @Provides
+  @Model
+  fun provideModel(): String {
+    return Build.MODEL
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
new file mode 100644
index 0000000..aa413d9
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.app.Application
+import dagger.hilt.GenerateComponents
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@GenerateComponents
+@AndroidEntryPoint
+class KotlinApplication : Application() {
+  // Shows that we can inject ApplicationComponent bindings into an application.
+  @Inject
+  @Model
+  @JvmField
+  var model: String? = null
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
new file mode 100644
index 0000000..564f05a
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity() {
+  // Shows that we can inject Application/Activity bindings into an activity.
+  @JvmField
+  @Model
+  @Inject
+  var model: String? = null
+
+  @JvmField
+  @UserName
+  @Inject
+  var name: String? = null
+
+  override fun onCreate(savedInstanceState: Bundle?) {
+    super.onCreate(savedInstanceState)
+    setContentView(R.layout.activity_main)
+    val greeting = findViewById<View>(R.id.greeting) as TextView
+    val text = resources.getString(R.string.welcome, name, model)
+    greeting.text = text
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
new file mode 100644
index 0000000..4fe168d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to [android.os.Build.MODEL].  */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class Model
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
new file mode 100644
index 0000000..a061ab5
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to the user name.  */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class UserName
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0d7637d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Dagger Authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:background="@android:color/background_light">
+
+  <TextView
+    android:id="@+id/greeting"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_alignParentTop="true"
+    android:textColor="@android:color/primary_text_light"
+    />
+</RelativeLayout>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c1e4f27
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2020 The Dagger Authors.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <!--The app name [CHAR_LIMIT=40]-->
+    <string name="app_name">Simple Hilt Kotlin Android App</string>
+
+    <!--The greeting message [CHAR_LIMIT=100]-->
+    <string name="welcome">Hello, %1$s! You are on build %2$s.</string>
+</resources>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
new file mode 100644
index 0000000..4aeeb08
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Build
+import androidx.test.core.app.ActivityScenario
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.GenerateComponents
+import dagger.hilt.android.testing.AndroidRobolectricEntryPoint
+import dagger.hilt.android.testing.HiltTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@GenerateComponents
+@AndroidRobolectricEntryPoint
+@RunWith(RobolectricTestRunner::class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = [Build.VERSION_CODES.P], application = SimpleTest_Application::class)
+class SimpleTest {
+  @Rule
+  @JvmField
+  var rule = HiltTestRule(this)
+
+  @Test
+  fun verifyMainActivity() {
+    ActivityScenario.launch(MainActivity::class.java).use { scenario ->
+      scenario.onActivity { activity ->
+        assertThat(activity::class.java.getSuperclass()?.getSimpleName())
+          .isEqualTo("Hilt_MainActivity")
+        assertThat(activity.model).isNotNull()
+        assertThat(activity.name).isNotNull()
+      }
+    }
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
new file mode 100644
index 0000000..e1edb8d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+    ext {
+        kotlin_version = '1.3.61'
+        agp_version = System.getenv('AGP_VERSION') ?: "3.6.3"
+    }
+    repositories {
+        google()
+        jcenter()
+        mavenLocal()
+    }
+    dependencies {
+        classpath "com.android.tools.build:gradle:$agp_version"
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+        classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
+    }
+}
+
+allprojects {
+    repositories {
+      google()
+      jcenter()
+      mavenCentral()
+      mavenLocal()
+    }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
new file mode 100644
index 0000000..646c51b
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a4b4429
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
new file mode 100644
index 0000000..b0d6d0a
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
new file mode 100644
index 0000000..e42f9d1
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='Simple Kotlin Hilt Android'
+include ':app'
diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle
index bd9db51..80e5c3f 100644
--- a/java/dagger/hilt/android/plugin/build.gradle
+++ b/java/dagger/hilt/android/plugin/build.gradle
@@ -34,7 +34,7 @@
 
 dependencies {
   implementation gradleApi()
-  implementation 'com.android.tools.build:gradle:3.5.3'
+  implementation 'com.android.tools.build:gradle:3.6.3'
   implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61'
   implementation 'org.javassist:javassist:3.26.0-GA'
 
diff --git a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
index f4d7b2b..a4b4429 100644
--- a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
+++ b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt
new file mode 100644
index 0000000..7b7b7c4
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.plugin
+
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import java.io.File
+import java.io.FileInputStream
+import java.util.zip.ZipInputStream
+import javassist.ClassPool
+import javassist.CtClass
+import org.slf4j.LoggerFactory
+
+/**
+ * A helper class for performing the transform.
+ *
+ * Create it with the list of all available source directories along with the root output directory
+ * and use [AndroidEntryPointClassTransformer.transformFile] or
+ * [AndroidEntryPointClassTransformer.transformJarContents] to perform the actual transformation.
+ */
+internal class AndroidEntryPointClassTransformer(
+  val taskName: String,
+  allInputs: List<File>,
+  private val sourceRootOutputDir: File,
+  private val copyNonTransformed: Boolean
+) {
+  private val logger = LoggerFactory.getLogger(AndroidEntryPointClassTransformer::class.java)
+
+  // A ClassPool created from the given input files, this allows us to use the higher
+  // level Javaassit APIs, but requires class parsing/loading.
+  private val classPool: ClassPool = ClassPool(true).also { pool ->
+    allInputs.forEach {
+      pool.appendClassPath(it.path)
+    }
+  }
+
+  init {
+    sourceRootOutputDir.mkdirs()
+  }
+
+  /**
+   * Transforms the classes inside the jar and copies re-written class files if and only if they are
+   * transformed.
+   *
+   * @param inputFile The jar file to transform, must be a jar.
+   */
+  fun transformJarContents(inputFile: File) {
+    require(inputFile.isJarFile()) {
+      "Invalid file, '$inputFile' is not a jar."
+    }
+    // Validate transform is not applied to a jar when copying is enabled, meaning the transformer
+    // is being used in the Android transform API pipeline which does not need to transform jars
+    // and handles copying them.
+    check(!copyNonTransformed) {
+      "Transforming a jar is not supported with 'copyNonTransformed'."
+    }
+    ZipInputStream(FileInputStream(inputFile)).use { input ->
+      var entry = input.nextEntry
+      while (entry != null) {
+        if (entry.isClassFile()) {
+          val clazz = classPool.makeClass(input, false)
+          transformClassToOutput(clazz)
+        }
+        entry = input.nextEntry
+      }
+    }
+  }
+
+  /**
+   * Transform a single class file.
+   *
+   * @param inputFile The file to transform, must be a class file.
+   */
+  fun transformFile(inputFile: File) {
+    check(inputFile.isClassFile()) {
+      "Invalid file, '$inputFile' is not a class."
+    }
+    val clazz = inputFile.inputStream().use { classPool.makeClass(it, false) }
+    transformClassToOutput(clazz)
+  }
+
+  private fun transformClassToOutput(clazz: CtClass) {
+    val transformed = transformClass(clazz)
+    if (transformed || copyNonTransformed) {
+      clazz.writeFile(sourceRootOutputDir.path)
+    }
+  }
+
+  private fun transformClass(clazz: CtClass): Boolean {
+    if (!clazz.hasAnnotation("dagger.hilt.android.AndroidEntryPoint")) {
+      // Not a AndroidEntryPoint annotated class, don't do anything.
+      return false
+    }
+
+    // TODO(danysantiago): Handle classes with '$' in their name if they do become an issue.
+    val superclassName = clazz.classFile.superclass
+    val entryPointSuperclassName =
+      clazz.packageName + ".Hilt_" + clazz.simpleName.replace("$", "_")
+    logger.info(
+      "[$taskName] Transforming ${clazz.name} to extend $entryPointSuperclassName instead of " +
+        "$superclassName."
+    )
+    clazz.superclass = classPool.get(entryPointSuperclassName)
+    return true
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
index 3927da3..bb35824 100644
--- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
@@ -16,18 +16,16 @@
 
 package dagger.hilt.android.plugin
 
-import com.android.SdkConstants
+import com.android.build.api.transform.DirectoryInput
 import com.android.build.api.transform.Format
+import com.android.build.api.transform.JarInput
 import com.android.build.api.transform.QualifiedContent
 import com.android.build.api.transform.Status
 import com.android.build.api.transform.Transform
 import com.android.build.api.transform.TransformInput
 import com.android.build.api.transform.TransformInvocation
+import dagger.hilt.android.plugin.util.isClassFile
 import java.io.File
-import java.util.zip.ZipEntry
-import javassist.ClassPool
-import javassist.CtClass
-import org.slf4j.LoggerFactory
 
 /**
  * Bytecode transformation to make @AndroidEntryPoint annotated classes extend the Hilt
@@ -43,9 +41,6 @@
  * See: [TransformPublic Docs](https://google.github.io/android-gradle-dsl/javadoc/current/com/android/build/api/transform/Transform.html)
  */
 class AndroidEntryPointTransform : Transform() {
-
-  private val logger = LoggerFactory.getLogger(javaClass)
-
   // The name of the transform. This name appears as a gradle task.
   override fun getName() = "AndroidEntryPointTransform"
 
@@ -75,11 +70,6 @@
       invocation.outputProvider.deleteAll()
     }
 
-    // Create a ClassPool with the given input and references, this allows us to use the higher
-    // level Javaassit APIs, but requires class parsing/loading, note that since this is a PROJECT
-    // scoped transform we can only load classes in the project and not its dependencies.
-    val pool = createClassPool(invocation.inputs, invocation.referencedInputs)
-
     invocation.inputs.forEach { transformInput ->
       transformInput.jarInputs.forEach { jarInput ->
         val outputJar =
@@ -91,7 +81,7 @@
           )
         if (invocation.isIncremental) {
           when (jarInput.status) {
-            Status.ADDED, Status.CHANGED -> copyJar(jarInput.file, outputJar, pool)
+            Status.ADDED, Status.CHANGED -> copyJar(jarInput.file, outputJar)
             Status.REMOVED -> outputJar.delete()
             Status.NOTCHANGED -> {
               // No need to transform.
@@ -101,7 +91,7 @@
             }
           }
         } else {
-          copyJar(jarInput.file, outputJar, pool)
+          copyJar(jarInput.file, outputJar)
         }
       }
       transformInput.directoryInputs.forEach { directoryInput ->
@@ -111,11 +101,14 @@
           directoryInput.scopes,
           Format.DIRECTORY
         )
+        val classTransformer =
+          createHiltClassTransformer(invocation.inputs, invocation.referencedInputs, outputDir)
         if (invocation.isIncremental) {
           directoryInput.changedFiles.forEach { (file, status) ->
             val outputFile = toOutputFile(outputDir, directoryInput.file, file)
             when (status) {
-              Status.ADDED, Status.CHANGED -> transformFile(file, outputFile.parentFile, pool)
+              Status.ADDED, Status.CHANGED ->
+                transformFile(file, outputFile.parentFile, classTransformer)
               Status.REMOVED -> outputFile.delete()
               Status.NOTCHANGED -> {
                 // No need to transform.
@@ -128,69 +121,54 @@
         } else {
           directoryInput.file.walkTopDown().forEach { file ->
             val outputFile = toOutputFile(outputDir, directoryInput.file, file)
-            transformFile(file, outputFile.parentFile, pool)
+            transformFile(file, outputFile.parentFile, classTransformer)
           }
         }
       }
     }
   }
 
-  // Create class pool using invocation inputs as classpath.
-  private fun createClassPool(
+  // Create a transformer given an invocation inputs. Note that since this is a PROJECT scoped
+  // transform the actual transformation is only done on project files and not its dependencies.
+  private fun createHiltClassTransformer(
     inputs: Collection<TransformInput>,
-    referencedInputs: Collection<TransformInput>
-  ) = ClassPool.getDefault().apply {
-    (inputs + referencedInputs).flatMap { input ->
-      (input.directoryInputs + input.jarInputs).map { it.file.path }
-    }.forEach {
-      appendClassPath(it)
+    referencedInputs: Collection<TransformInput>,
+    outputDir: File
+  ): AndroidEntryPointClassTransformer {
+    val classFiles = (inputs + referencedInputs).flatMap { input ->
+      (input.directoryInputs + input.jarInputs).map { it.file }
     }
+    return AndroidEntryPointClassTransformer(
+      taskName = name,
+      allInputs = classFiles,
+      sourceRootOutputDir = outputDir,
+      copyNonTransformed = true
+    )
   }
 
-  // We are only interested in project compiled classes but we have to copy received jars to the
-  // output.
-  private fun copyJar(inputJar: File, outputJar: File, pool: ClassPool) {
-    outputJar.parentFile?.mkdirs()
-    inputJar.copyTo(target = outputJar, overwrite = true)
-  }
-
-  // Transform a single class file.
-  private fun transformFile(inputFile: File, outputDir: File, pool: ClassPool) {
-    outputDir.mkdirs()
+  // Transform a single file. If the file is not a class file it is just copied to the output dir.
+  private fun transformFile(
+    inputFile: File,
+    outputDir: File,
+    transformer: AndroidEntryPointClassTransformer
+  ) {
     if (inputFile.isClassFile()) {
-      val clazz = inputFile.inputStream().use { pool.makeClass(it, false) }
-      transformClass(clazz, pool)
-      clazz.writeFile(outputDir.path)
+      transformer.transformFile(inputFile)
     } else if (inputFile.isFile) {
       // Copy all non .class files to the output.
+      outputDir.mkdirs()
       val outputFile = File(outputDir, inputFile.name)
       inputFile.copyTo(target = outputFile, overwrite = true)
     }
   }
 
-  // Transform a parsed class file.
-  private fun transformClass(clazz: CtClass, pool: ClassPool) {
-    if (!clazz.hasAnnotation("dagger.hilt.android.AndroidEntryPoint")) {
-      // Not a AndroidEntryPoint annotated class, don't do anything.
-      return
-    }
-
-    // TODO(danysantiago): Handle classes with '$' in their name if they do become an issue.
-    val superclassName = clazz.classFile.superclass
-    val entryPointSuperclassName =
-      clazz.packageName + ".Hilt_" + clazz.simpleName.replace("$", "_")
-    logger.info(
-      "[$name] Transforming ${clazz.name} to extend $entryPointSuperclassName instead of " +
-        "$superclassName."
-    )
-    clazz.superclass = pool.get(entryPointSuperclassName)
+  // We are only interested in project compiled classes but we have to copy received jars to the
+  // output.
+  private fun copyJar(inputJar: File, outputJar: File) {
+    outputJar.parentFile?.mkdirs()
+    inputJar.copyTo(target = outputJar, overwrite = true)
   }
 
   private fun toOutputFile(outputDir: File, inputDir: File, inputFile: File) =
     File(outputDir, inputFile.relativeTo(inputDir).path)
-
-  private fun ZipEntry.isClassFile() =
-    !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS)
-
-  private fun File.isClassFile() = this.isFile && this.name.endsWith(SdkConstants.DOT_CLASS)
 }
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt
new file mode 100644
index 0000000..4f6f578
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.plugin
+
+/**
+ * Configuration options for the Hilt Gradle Plugin
+ */
+interface HiltExtension {
+  /**
+   * If set to `true`, Hilt will register a transform task that will rewrite `@AndroidEntryPoint`
+   * annotated classes before the host-side JVM tests run. You should enable this option if you are
+   * running Robolectric UI tests as part of your JUnit tests.
+   */
+  var enableTransformForLocalTests: Boolean
+}
+
+internal open class HiltExtensionImpl : HiltExtension {
+  override var enableTransformForLocalTests: Boolean = false
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
index c339431..2c9bfe4 100644
--- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
@@ -17,6 +17,8 @@
 package dagger.hilt.android.plugin
 
 import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.TestedExtension
+import com.android.build.gradle.api.AndroidBasePlugin
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 
@@ -30,10 +32,50 @@
  */
 class HiltGradlePlugin : Plugin<Project> {
   override fun apply(project: Project) {
+    var configured = false
+    project.plugins.withType(AndroidBasePlugin::class.java) {
+      configured = true
+      configureHilt(project)
+    }
+    project.afterEvaluate {
+      check(configured) {
+        // Check if configuration was applied, if not inform the developer they have applied the
+        // plugin to a non-android project.
+        "The Hilt Android Gradle plugin can only be applied to an Android project."
+      }
+    }
+  }
+
+  private fun configureHilt(project: Project) {
+    val extension = project.extensions.create(
+      HiltExtension::class.java, "hilt", HiltExtensionImpl::class.java
+    )
+    configureTransform(project, extension)
+    configureProcessorFlags(project)
+    project.afterEvaluate {
+      verifyDependencies(it)
+    }
+  }
+
+  private fun configureTransform(project: Project, extension: HiltExtension) {
     val androidExtension = project.extensions.findByType(BaseExtension::class.java)
-      ?: throw error("The Hilt Android Gradle plugin can only be applied to an Android project.")
+      ?: throw error("Android BaseExtension not found.")
     androidExtension.registerTransform(AndroidEntryPointTransform())
 
+    // Create and configure a task for applying the transform for host-side unit tests. b/37076369
+    val testedExtensions = project.extensions.findByType(TestedExtension::class.java)
+    testedExtensions?.unitTestVariants?.all { unitTestVariant ->
+      HiltTransformTestClassesTask.create(
+        project = project,
+        unitTestVariant = unitTestVariant,
+        extension = extension
+      )
+    }
+  }
+
+  private fun configureProcessorFlags(project: Project) {
+    val androidExtension = project.extensions.findByType(BaseExtension::class.java)
+      ?: throw error("Android BaseExtension not found.")
     // Pass annotation processor flag to disable @AndroidEntryPoint superclass validation.
     androidExtension.defaultConfig.apply {
       javaCompileOptions.apply {
@@ -42,10 +84,6 @@
         }
       }
     }
-
-    project.afterEvaluate {
-      verifyDependencies(it)
-    }
   }
 
   private fun verifyDependencies(project: Project) {
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
new file mode 100644
index 0000000..28a7f75
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.plugin
+
+import com.android.build.gradle.api.UnitTestVariant
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import java.io.File
+import javax.inject.Inject
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.workers.WorkAction
+import org.gradle.workers.WorkParameters
+import org.gradle.workers.WorkerExecutor
+
+/**
+ * Task that transform classes used by host-side unit tests. See b/37076369
+ */
+@Suppress("UnstableApiUsage")
+abstract class HiltTransformTestClassesTask @Inject constructor(
+  private val workerExecutor: WorkerExecutor
+) : DefaultTask() {
+
+  @get:Classpath
+  abstract val compiledClasses: ConfigurableFileCollection
+
+  @get:OutputDirectory
+  abstract val outputDir: DirectoryProperty
+
+  internal interface Parameters : WorkParameters {
+    val name: Property<String>
+    val compiledClasses: ConfigurableFileCollection
+    val outputDir: DirectoryProperty
+  }
+
+  abstract class WorkerAction : WorkAction<Parameters> {
+    override fun execute() {
+      val outputDir = parameters.outputDir.asFile.get()
+      outputDir.deleteRecursively()
+      outputDir.mkdirs()
+
+      val classTransformer = AndroidEntryPointClassTransformer(
+        taskName = parameters.name.get(),
+        allInputs = parameters.compiledClasses.files.toList(),
+        sourceRootOutputDir = outputDir,
+        copyNonTransformed = false
+      )
+      // Parse the classpath in reverse so that we respect overwrites, if it ever happens.
+      parameters.compiledClasses.files.reversed().forEach {
+        if (it.isDirectory) {
+          it.walkTopDown().forEach { file ->
+            if (file.isClassFile()) {
+              classTransformer.transformFile(file)
+            }
+          }
+        } else if (it.isJarFile()) {
+          classTransformer.transformJarContents(it)
+        }
+      }
+    }
+  }
+
+  @TaskAction
+  fun transformClasses() {
+    workerExecutor.noIsolation().submit(WorkerAction::class.java) {
+      it.compiledClasses.from(compiledClasses)
+      it.outputDir.set(outputDir)
+      it.name.set(name)
+    }
+  }
+
+  internal class ConfigAction(
+    private val outputDir: File,
+    private val inputClasspath: FileCollection
+  ) : Action<HiltTransformTestClassesTask> {
+    override fun execute(transformTask: HiltTransformTestClassesTask) {
+      transformTask.description = "Transforms AndroidEntryPoint annotated classes for JUnit tests."
+      transformTask.outputDir.set(outputDir)
+      transformTask.compiledClasses.from(inputClasspath)
+    }
+  }
+
+  companion object {
+
+    private const val TASK_PREFIX = "hiltTransformForJUnit"
+
+    fun create(
+      project: Project,
+      unitTestVariant: UnitTestVariant,
+      extension: HiltExtension
+    ) {
+      if (!extension.enableTransformForLocalTests) {
+        // Not enabled, nothing to do here.
+        return
+      }
+      val outputDir =
+        project.buildDir.resolve("intermediates/hilt/${unitTestVariant.dirName}Output")
+      val outputFileCollection = project.objects.fileCollection()
+      val classpathKey = unitTestVariant.registerPreJavacGeneratedBytecode(
+        outputFileCollection.from(outputDir)
+      )
+      val hiltTransformProvider = project.tasks.register(
+        "$TASK_PREFIX${unitTestVariant.name.capitalize()}",
+        HiltTransformTestClassesTask::class.java,
+        ConfigAction(outputDir, unitTestVariant.getCompileClasspath(classpathKey))
+      )
+      outputFileCollection.builtBy(hiltTransformProvider)
+    }
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt
new file mode 100644
index 0000000..e5a101e
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt
@@ -0,0 +1,14 @@
+package dagger.hilt.android.plugin.util
+
+import com.android.SdkConstants
+import java.io.File
+import java.util.zip.ZipEntry
+
+/* Checks if a file is a .class file. */
+fun File.isClassFile() = this.isFile && this.extension == SdkConstants.EXT_CLASS
+
+/* Checks if a Zip entry is a .class file. */
+fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS)
+
+/* CHecks if a file is a .jar file. */
+fun File.isJarFile() = this.isFile && this.extension == SdkConstants.EXT_JAR
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties b/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties
new file mode 100644
index 0000000..5bac8ac
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt
index 08a647d..f2f8ba0 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt
@@ -27,6 +27,7 @@
   private val activities = mutableListOf<String>()
 
   private var buildFile: File? = null
+  private var gradlePropertiesFile: File? = null
   private var manifestFile: File? = null
 
   init {
@@ -75,6 +76,7 @@
 
   private fun setupFiles() {
     writeBuildFile()
+    writeGradleProperties()
     writeAndroidManifest()
   }
 
@@ -128,6 +130,17 @@
     }
   }
 
+  private fun writeGradleProperties() {
+    gradlePropertiesFile?.delete()
+    gradlePropertiesFile = tempFolder.newFile("gradle.properties").apply {
+      writeText(
+        """
+        android.useAndroidX=true
+        """.trimIndent()
+      )
+    }
+  }
+
   private fun writeAndroidManifest() {
     manifestFile?.delete()
     manifestFile = tempFolder.newFile("/src/main/AndroidManifest.xml").apply {
@@ -168,7 +181,7 @@
       projectRoot,
       "build/intermediates/transforms/AndroidEntryPointTransform/debug"
     ).listFiles()?.first { it.isDirectory }?.let { transformedDir ->
-      File(transformedDir, "minimal/$srcFilePath").also {
+      File(transformedDir, srcFilePath).also {
         if (!it.exists()) {
           error("Unable to find transformed class ${it.path}")
         }
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
index bc5c04e..b5b43f9 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
@@ -316,7 +316,6 @@
     // * All aggregating processor gen sources are re-compiled
     assertChangedFiles(
       FileType.CLASS,
-      classSrcApp, // re-compiles because superclass re-compiled
       classSrcActivity1,
       classGenHiltApp,
       classGenHiltActivity1,
@@ -365,7 +364,6 @@
     // * All aggregating processor gen sources are re-compiled
     assertChangedFiles(
       FileType.CLASS,
-      classSrcApp, // re-compiles because superclass re-compiled
       classSrcModule1,
       classGenHiltApp,
       classGenAppInjector,
@@ -457,7 +455,6 @@
     )
     assertChangedFiles(
       FileType.CLASS,
-      classSrcApp, // re-compiles because superclass re-compiled
       classGenHiltApp,
       classGenAppInjector,
       classGenAppInjectorDeps,
@@ -500,7 +497,6 @@
     )
     assertChangedFiles(
       FileType.CLASS,
-      classSrcApp, // re-compiles because superclass re-compiled
       classGenHiltApp,
       classGenAppInjector,
       classGenAppInjectorDeps,
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
index 0b246ed..1621734 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
@@ -208,17 +208,19 @@
         annotationClassName.simpleName());
     TypeElement androidEntryPointElement = (TypeElement) element;
 
+    final TypeElement androidEntryPointClassValue =
+        Processors.getAnnotationClassValue(
+            env.getElementUtils(),
+            Processors.getAnnotationMirror(androidEntryPointElement, annotationClassName),
+            "value");
     final TypeElement baseElement;
     final ClassName generatedClassName;
-    if (DISABLE_ANDROID_SUPERCLASS_VALIDATION.get(env)) {
+    if (DISABLE_ANDROID_SUPERCLASS_VALIDATION.get(env)
+        && MoreTypes.isTypeOf(Void.class, androidEntryPointClassValue.asType())) {
       baseElement = MoreElements.asType(env.getTypeUtils().asElement(androidEntryPointElement.getSuperclass()));
       generatedClassName = generatedClassName(androidEntryPointElement);
     } else {
-      baseElement =
-          Processors.getAnnotationClassValue(
-              env.getElementUtils(),
-              Processors.getAnnotationMirror(androidEntryPointElement, annotationClassName),
-              "value");
+      baseElement = androidEntryPointClassValue;
       ProcessorErrors.checkState(
           !MoreTypes.isTypeOf(Void.class, baseElement.asType()),
           androidEntryPointElement,
diff --git a/util/run-local-tests.sh b/util/run-local-tests.sh
index d7297a9..06208c0 100755
--- a/util/run-local-tests.sh
+++ b/util/run-local-tests.sh
@@ -7,19 +7,29 @@
 # Run tests with bazel
 bazel test $TEST_PARAMS //...
 
+# Install into local maven.
+util/install-local-snapshot.sh
+
 # Also run the gradle examples on the local maven snapshots.
 readonly _SIMPLE_EXAMPLE_DIR=java/dagger/example/gradle/simple
 readonly _ANDROID_EXAMPLE_DIR=java/dagger/example/gradle/android/simple
-
-util/install-local-snapshot.sh
 ./$_SIMPLE_EXAMPLE_DIR/gradlew -p $_SIMPLE_EXAMPLE_DIR build --no-daemon --stacktrace
 ./$_ANDROID_EXAMPLE_DIR/gradlew -p $_ANDROID_EXAMPLE_DIR build --no-daemon --stacktrace
 
 readonly _HILT_GRADLE_PLUGIN_DIR=java/dagger/hilt/android/plugin
 readonly _HILT_ANDROID_EXAMPLE_DIR=java/dagger/hilt/android/example/gradle/simple
+readonly _HILT_KOTLIN_ANDROID_EXAMPLE_DIR=java/dagger/hilt/android/example/gradle/simpleKotlin
 ./$_HILT_GRADLE_PLUGIN_DIR/gradlew -p $_HILT_GRADLE_PLUGIN_DIR test --no-daemon --stacktrace
-./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR buildDebug --no-daemon --stacktrace
-./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR testDebug --no-daemon --stacktrace
+# Run gradle tests with different versions of Android Gradle Plugin
+agp_verions=("4.1.0-alpha07" "4.0.0-beta05" "3.6.3")
+for version in "${agp_verions[@]}"
+do
+    echo "Running tests with AGP $version"
+    AGP_VERSION=$version ./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR buildDebug --no-daemon --stacktrace
+    AGP_VERSION=$version ./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR testDebug --no-daemon --stacktrace
+    AGP_VERSION=$version ./$_HILT_KOTLIN_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_KOTLIN_ANDROID_EXAMPLE_DIR buildDebug --no-daemon --stacktrace
+    AGP_VERSION=$version ./$_HILT_KOTLIN_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_KOTLIN_ANDROID_EXAMPLE_DIR testDebug --no-daemon --stacktrace
+done
 
 verify_version_file() {
   local m2_repo=$1