Merge "Snap for 9550355 from cb0e023d0548afed3d7d3a9b541ca6a86aad5c52 to sdk-release" into sdk-release
diff --git a/Android.bp b/Android.bp
index 6648cf6..e1d33c7 100644
--- a/Android.bp
+++ b/Android.bp
@@ -60,6 +60,13 @@
     ],
 
     sdk_version: "core_current",
+
+    errorprone: {
+        javacflags: [
+            "-Xep:EmptyTopLevelDeclaration:WARN",
+            "-Xep:ReturnValueIgnored:WARN",
+        ],
+    },
 }
 
 // Build Dexmaker's MockMaker, a plugin to Mockito
@@ -179,6 +186,12 @@
         "libdexmakerjvmtiagent",
         "libstaticjvmtiagent",
     ],
+
+    errorprone: {
+        javacflags: [
+            "-Xep:ReturnValueIgnored:WARN",
+        ],
+    },
 }
 
 // Provides mockito functionality for on-device tests. Does not allow stubbing of final or static
@@ -279,6 +292,12 @@
         "dexmaker",
         "junit",
     ],
+
+    errorprone: {
+        javacflags: [
+            "-Xep:ReturnValueIgnored:WARN",
+        ],
+    },
 }
 
 // dexmaker-mockito tests
@@ -294,6 +313,12 @@
         "junit",
         "dexmaker-mockmaker",
     ],
+
+    errorprone: {
+        javacflags: [
+            "-Xep:ReturnValueIgnored:WARN",
+        ],
+    },
 }
 
 // dexmaker-mockito-inline tests
diff --git a/README.version b/README.version
index 3e8229a..e3a494a 100644
--- a/README.version
+++ b/README.version
@@ -1,5 +1,5 @@
 URL: https://github.com/linkedin/dexmaker/
-Version: master (8ff85edb2793cc1e0f6a93c67b127cb9c43d924e)
+Version: main (65749b3da4b058c0b322b2adbf3e8f70488f8d8b)
 License: Apache 2.0
 Description:
 Dexmaker is a Java-language API for doing compile time or runtime code generation targeting the Dalvik VM. Unlike cglib or ASM, this library creates Dalvik .dex files instead of Java .class files.
@@ -13,8 +13,5 @@
         Allow to share classloader via dexmaker.share_classloader system property (I324cddd644610eef811c620a1fccf6a24b2b9406)
         Do not read Build.VERSION to allow non-standard Android distributions (Ia8c4ba4c82cd6f193c565f1bfe48faffc4aac08f)
         Temporarily ignore failing test (Ibf7b6c2eb05c5ff83f0817f9224369e20c0b775d)
-        Restrict InspectClass to current thread. (Ic62951ff81bed60ac7512455fad65210e4b728a9, need upstreaming)
         Exclude Stress#mockALot from presubmit (Ic9a2927ffa07924bd759429e31c56dc1b71a826c)
         Extend timeout of Stress#mockALot() for CTS. (Iad30a8cb07b38054b490b7006d11908fc752a024)
-        Update to Mockito 2.25.0 and impl InlineMockMaker (29a8674036d345e4ec8635b1d38d8b2a4fe91980a, need upstreaming)
-	guessPath not to depend on the first level of app directory (I66f1d7036949c2f05e6d37bc270d47f8e77e51c1, need upstreaming)
diff --git a/dexmaker-mockito-inline-dispatcher/build.gradle b/dexmaker-mockito-inline-dispatcher/build.gradle
index 73a93f3..b811904 100644
--- a/dexmaker-mockito-inline-dispatcher/build.gradle
+++ b/dexmaker-mockito-inline-dispatcher/build.gradle
@@ -1,18 +1,12 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.3'
+    compileSdkVersion 32
 
     defaultConfig {
         applicationId 'com.android.dexmaker.mockito.inline.dispatcher'
         minSdkVersion 28
-        targetSdkVersion 28
+        targetSdkVersion 32
         versionName VERSION_NAME
     }
 }
-
-repositories {
-    jcenter()
-    google()
-}
diff --git a/dexmaker-mockito-inline-extended-tests/build.gradle b/dexmaker-mockito-inline-extended-tests/build.gradle
index b5664f2..e5dc213 100644
--- a/dexmaker-mockito-inline-extended-tests/build.gradle
+++ b/dexmaker-mockito-inline-extended-tests/build.gradle
@@ -1,20 +1,7 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
-}
-
-apply plugin: "net.ltgt.errorprone"
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.3'
+    compileSdkVersion 32
 
     android {
         lintOptions {
@@ -24,8 +11,7 @@
 
     defaultConfig {
         minSdkVersion 28
-        targetSdkVersion 28
-        versionName VERSION_NAME
+        targetSdkVersion 32
 
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
     }
@@ -36,19 +22,14 @@
     }
 }
 
-repositories {
-    jcenter()
-    google()
-}
-
 dependencies {
     implementation project(':dexmaker-mockito-inline-tests')
     compileOnly project(':dexmaker-mockito-inline-extended')
     androidTestImplementation project(':dexmaker-mockito-inline-extended')
 
-    implementation 'junit:junit:4.12'
-    implementation 'com.android.support.test:runner:1.0.2'
-    implementation 'com.android.support.test:rules:1.0.2'
+    implementation 'junit:junit:4.13.2'
+    implementation 'androidx.test:runner:1.4.0'
+    implementation 'androidx.test:rules:1.4.0'
 
-    api 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' }
+    api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' }
 }
diff --git a/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java b/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java
index 7c7941b..eff42ab 100644
--- a/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java
+++ b/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java
@@ -16,6 +16,7 @@
 
 package com.android.dx.mockito.inline.extended.tests;
 
+import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.provider.Settings;
 
@@ -33,8 +34,9 @@
 import static org.mockito.ArgumentMatchers.eq;
 
 public class StaticMockitoSession {
+
     @Test
-    public void strictUnnecessaryStubbing() throws Exception {
+    public void strictUnnecessaryStubbing() {
         MockitoSession session = mockitoSession().spyStatic(Settings.Global.class).startMocking();
 
         // Set up unnecessary stubbing
@@ -51,7 +53,7 @@
     }
 
     @Test
-    public void lenientUnnecessaryStubbing() throws Exception {
+    public void lenientUnnecessaryStubbing() {
         MockitoSession session = mockitoSession().strictness(Strictness.LENIENT)
                 .spyStatic(Settings.Global.class).startMocking();
 
@@ -61,4 +63,22 @@
 
         session.finishMocking();
     }
+
+    @Test
+    public void spyStatic() {
+        mockitoSession()
+            .initMocks(this)
+            .spyStatic(PendingIntent.class)
+            .startMocking()
+            .finishMocking();
+    }
+
+    @Test
+    public void mockStatic() {
+        mockitoSession()
+            .initMocks(this)
+            .mockStatic(PendingIntent.class)
+            .startMocking()
+            .finishMocking();
+    }
 }
diff --git a/dexmaker-mockito-inline-extended/build.gradle b/dexmaker-mockito-inline-extended/build.gradle
index fee152e..b02cd37 100644
--- a/dexmaker-mockito-inline-extended/build.gradle
+++ b/dexmaker-mockito-inline-extended/build.gradle
@@ -1,25 +1,13 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
+plugins {
+    id("net.ltgt.errorprone") version "1.3.0"
 }
-
-apply plugin: "net.ltgt.errorprone"
 apply plugin: 'com.android.library'
-apply plugin: 'maven-publish'
-apply plugin: 'ivy-publish'
-apply plugin: 'com.jfrog.artifactory'
+apply from: "$rootDir/gradle/publishing_aar.gradle"
 
-version = VERSION_NAME
+description = 'Extension of the Mockito Inline API to allow mocking static methods on the Android Dalvik VM'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.3'
+    compileSdkVersion 32
 
     android {
         lintOptions {
@@ -29,9 +17,8 @@
     }
 
     defaultConfig {
-        minSdkVersion 1
-        targetSdkVersion 28
-        versionName VERSION_NAME
+        minSdkVersion 9
+        targetSdkVersion 32
     }
 
     externalNativeBuild {
@@ -47,77 +34,16 @@
 }
 
 tasks.withType(JavaCompile) {
-    options.compilerArgs += ["-Xep:StringSplitter:OFF"]
-}
-
-task sourcesJar(type: Jar) {
-    classifier = 'sources'
-    from android.sourceSets.main.java.srcDirs
-}
-
-task javadoc(type: Javadoc) {
-    source = android.sourceSets.main.java.srcDirs
-    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
-    failOnError false
-}
-
-task javadocJar(type: Jar, dependsOn: javadoc) {
-    classifier = 'javadoc'
-    from javadoc.destinationDir
-}
-
-publishing {
-    publications {
-        ivyLib(IvyPublication) {
-            from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies())
-            artifact sourcesJar
-            artifact javadocJar
-        }
-
-        lib(MavenPublication) {
-            from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies())
-
-            artifact sourcesJar
-            artifact javadocJar
-
-            pom.withXml {
-                asNode().children().last() + {
-                    resolveStrategy = Closure.DELEGATE_FIRST
-                    description = 'Extension of the Mockito Inline API to allow mocking static methods on the Android Dalvik VM'
-                    url 'https://github.com/linkedin/dexmaker'
-                    scm {
-                        url 'https://github.com/linkedin/dexmaker'
-                        connection 'scm:git:git://github.com/linkedin/dexmaker.git'
-                        developerConnection 'https://github.com/linkedin/dexmaker.git'
-                    }
-                    licenses {
-                        license {
-                            name 'The Apache Software License, Version 2.0'
-                            url 'http://www.apache.org/license/LICENSE-2.0.txt'
-                            distribution 'repo'
-                        }
-                    }
-
-                    developers {
-                        developer {
-                            id 'com.linkedin'
-                            name 'LinkedIn Corp'
-                            email ''
-                        }
-                    }
-                }
-            }
-        }
+    options.errorprone {
+        disable("StringSplitter")
     }
 }
 
-repositories {
-    jcenter()
-    google()
-}
-
 dependencies {
+    errorprone "com.google.errorprone:error_prone_core:2.5.1"
+    errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
+
     implementation project(':dexmaker-mockito-inline')
 
-    implementation 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' }
+    api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' }
 }
diff --git a/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java
index 6ba11cd..05b067d 100644
--- a/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java
+++ b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java
@@ -112,7 +112,7 @@
      * are modified, some are not. This list helps the {@link MockMethodAdvice} help figure out if a
      * object's method calls should be intercepted.
      */
-    private final HashMap<Object, InvocationHandlerAdapter> markerToHandler = new HashMap<>();
+    private final Map<Object, InvocationHandlerAdapter> markerToHandler = new MarkerToHandlerMap();
     private final Map<Class, Object> classToMarker = new HashMap<>();
 
     /**
@@ -126,8 +126,7 @@
     public InlineStaticMockMaker() {
         if (INITIALIZATION_ERROR != null) {
             throw new RuntimeException("Could not initialize inline mock maker.\n" + "\n" +
-                    "Release: Android " + Build.VERSION.RELEASE_OR_CODENAME + " "
-                    + Build.VERSION.INCREMENTAL
+                    "Release: Android " + Build.VERSION.RELEASE + " " + Build.VERSION.INCREMENTAL
                     + "Device: " + Build.BRAND + " " + Build.MODEL, INITIALIZATION_ERROR);
         }
 
diff --git a/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/MarkerToHandlerMap.java b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/MarkerToHandlerMap.java
new file mode 100644
index 0000000..74a38b8
--- /dev/null
+++ b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/MarkerToHandlerMap.java
@@ -0,0 +1,118 @@
+package com.android.dx.mockito.inline;
+
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A map for mock marker object -> {@link InvocationHandlerAdapter} but
+ * does not use the mock marker object as the key directly.
+ * The problem of not doing so is that the object's real hashCode() and equals() =
+ * methods will be invoked during
+ * {@link InlineStaticMockMaker#createMock(MockCreationSettings, MockHandler)}. This poses a
+ * potential test runtime error depending on the object's hashCode() implementation
+ */
+class MarkerToHandlerMap implements Map<Object, InvocationHandlerAdapter> {
+
+    private final Map<MockMarkerKey, InvocationHandlerAdapter> markerToHandler = new HashMap<>();
+
+    @Override
+    public int size() {
+        return markerToHandler.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return markerToHandler.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return markerToHandler.containsKey(new MockMarkerKey(key));
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return markerToHandler.containsValue(value);
+    }
+
+    @Override
+    public InvocationHandlerAdapter get(Object key) {
+        return markerToHandler.get(new MockMarkerKey(key));
+    }
+
+    @Override
+    public InvocationHandlerAdapter put(Object key, InvocationHandlerAdapter value) {
+        return markerToHandler.put(new MockMarkerKey(key), value);
+    }
+
+    @Override
+    public InvocationHandlerAdapter remove(Object key) {
+        return markerToHandler.remove(new MockMarkerKey(key));
+    }
+
+    @Override
+    public void putAll(Map<?, ? extends InvocationHandlerAdapter> m) {
+        for (Entry<?, ? extends InvocationHandlerAdapter> entry : m.entrySet()) {
+            put(new MockMarkerKey(entry.getKey()), entry.getValue());
+        }
+    }
+
+    @Override
+    public void clear() {
+        markerToHandler.clear();
+    }
+
+    @Override
+    public Set<Object> keySet() {
+        Set<Object> set = new HashSet<>(entrySet().size());
+        for (MockMarkerKey key : markerToHandler.keySet()) {
+            set.add(key.mockMarker);
+        }
+        return set;
+    }
+
+    @Override
+    public Collection<InvocationHandlerAdapter> values() {
+        return markerToHandler.values();
+    }
+
+    @Override
+    public Set<Entry<Object, InvocationHandlerAdapter>> entrySet() {
+        Set<Entry<Object, InvocationHandlerAdapter>> set = new HashSet<>(entrySet().size());
+        for (Entry<MockMarkerKey, InvocationHandlerAdapter> entry : markerToHandler.entrySet()) {
+            set.add(new AbstractMap.SimpleImmutableEntry<>(entry.getKey().mockMarker, entry.getValue()));
+        }
+        return set;
+    }
+
+    private static class MockMarkerKey {
+
+        private final Object mockMarker;
+
+        public MockMarkerKey(Object mockMarker) {
+            this.mockMarker = mockMarker;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            MockMarkerKey mockMarkerKey = (MockMarkerKey) o;
+
+            return mockMarker == mockMarkerKey.mockMarker;
+        }
+
+        @Override
+        public int hashCode() {
+            return System.identityHashCode(mockMarker);
+        }
+    }
+}
diff --git a/dexmaker-mockito-inline-tests/build.gradle b/dexmaker-mockito-inline-tests/build.gradle
index 02d0751..4961c5e 100644
--- a/dexmaker-mockito-inline-tests/build.gradle
+++ b/dexmaker-mockito-inline-tests/build.gradle
@@ -1,20 +1,7 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
-}
-
-apply plugin: "net.ltgt.errorprone"
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.3'
+    compileSdkVersion 32
 
     android {
         lintOptions {
@@ -25,8 +12,7 @@
 
     defaultConfig {
         minSdkVersion 28
-        targetSdkVersion 28
-        versionName VERSION_NAME
+        targetSdkVersion 32
 
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
     }
@@ -38,17 +24,12 @@
     }
 }
 
-repositories {
-    jcenter()
-    google()
-}
-
 dependencies {
     implementation project(':dexmaker-mockito-tests')
     compileOnly project(':dexmaker-mockito-inline')
     androidTestImplementation project(':dexmaker-mockito-inline')
 
-    implementation 'junit:junit:4.12'
-    implementation 'com.android.support.test:runner:1.0.2'
-    api 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' }
+    implementation 'junit:junit:4.13.2'
+    implementation 'androidx.test:runner:1.4.0'
+    api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' }
 }
diff --git a/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java b/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java
index d78bb10..55f5893 100644
--- a/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java
+++ b/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java
@@ -29,7 +29,7 @@
     private static final int ARRAY_LENGTH = 1 << 20;  // 4 MB
 
     @Test
-    public void callMethodWithMocksCycalically() {
+    public void callMethodWithMocksCyclically() {
         for (int i = 0; i < 100; ++i) {
             final A a = mock(A.class);
             a.largeArray = new int[ARRAY_LENGTH];
diff --git a/dexmaker-mockito-inline/build.gradle b/dexmaker-mockito-inline/build.gradle
index 00913a4..dcffd65 100644
--- a/dexmaker-mockito-inline/build.gradle
+++ b/dexmaker-mockito-inline/build.gradle
@@ -1,25 +1,13 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
+plugins {
+    id("net.ltgt.errorprone") version "1.3.0"
 }
-
-apply plugin: "net.ltgt.errorprone"
 apply plugin: 'com.android.library'
-apply plugin: 'maven-publish'
-apply plugin: 'ivy-publish'
-apply plugin: 'com.jfrog.artifactory'
+apply from: "$rootDir/gradle/publishing_aar.gradle"
 
-version = VERSION_NAME
+description = 'Implementation of the Mockito Inline API for use on the Android Dalvik VM'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.3'
+    compileSdkVersion 32
 
     android {
         lintOptions {
@@ -30,8 +18,7 @@
 
     defaultConfig {
         minSdkVersion 1
-        targetSdkVersion 28
-        versionName VERSION_NAME
+        targetSdkVersion 32
     }
 
     externalNativeBuild {
@@ -42,77 +29,16 @@
 }
 
 tasks.withType(JavaCompile) {
-    options.compilerArgs += ["-Xep:StringSplitter:OFF"]
-}
-
-task sourcesJar(type: Jar) {
-    classifier = 'sources'
-    from android.sourceSets.main.java.srcDirs
-}
-
-task javadoc(type: Javadoc) {
-    source = android.sourceSets.main.java.srcDirs
-    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
-}
-
-task javadocJar(type: Jar, dependsOn: javadoc) {
-    classifier = 'javadoc'
-    from javadoc.destinationDir
-}
-
-publishing {
-    publications {
-        ivyLib(IvyPublication) {
-          from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies())
-          artifact sourcesJar
-          artifact javadocJar
-        }
-
-        lib(MavenPublication) {
-          from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies())
-
-            artifact sourcesJar
-            artifact javadocJar
-
-            pom.withXml {
-                asNode().children().last() + {
-                    resolveStrategy = Closure.DELEGATE_FIRST
-                    description = 'Implementation of the Mockito Inline API for use on the Android Dalvik VM'
-                    url 'https://github.com/linkedin/dexmaker'
-                    scm {
-                        url 'https://github.com/linkedin/dexmaker'
-                        connection 'scm:git:git://github.com/linkedin/dexmaker.git'
-                        developerConnection 'https://github.com/linkedin/dexmaker.git'
-                    }
-                    licenses {
-                        license {
-                            name 'The Apache Software License, Version 2.0'
-                            url 'http://www.apache.org/license/LICENSE-2.0.txt'
-                            distribution 'repo'
-                        }
-                    }
-
-                    developers {
-                        developer {
-                            id 'com.linkedin'
-                            name 'LinkedIn Corp'
-                            email ''
-                        }
-                    }
-                }
-            }
-        }
+    options.errorprone {
+        disable("StringSplitter")
     }
 }
 
-repositories {
-    jcenter()
-    google()
-}
-
 dependencies {
+    errorprone "com.google.errorprone:error_prone_core:2.5.1"
+    errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
+
     implementation project(':dexmaker')
 
-    implementation 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' }
+    api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' }
 }
-
diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java
index 9741118..0a594e0 100644
--- a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java
+++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java
@@ -180,8 +180,7 @@
             throw new RuntimeException(
                     "Could not initialize inline mock maker.\n"
                     + "\n"
-                    + "Release: Android " + Build.VERSION.RELEASE_OR_CODENAME + " "
-                    + Build.VERSION.INCREMENTAL
+                    + "Release: Android " + Build.VERSION.RELEASE + " " + Build.VERSION.INCREMENTAL
                     + "Device: " + Build.BRAND + " " + Build.MODEL, INITIALIZATION_ERROR);
         }
 
diff --git a/dexmaker-mockito-tests/build.gradle b/dexmaker-mockito-tests/build.gradle
index 9379d17..918f277 100644
--- a/dexmaker-mockito-tests/build.gradle
+++ b/dexmaker-mockito-tests/build.gradle
@@ -1,19 +1,7 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
-}
-
-apply plugin: "net.ltgt.errorprone"
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 28
+    compileSdkVersion 32
 
     android {
         lintOptions {
@@ -23,24 +11,18 @@
     }
 
     defaultConfig {
-        minSdkVersion 8
-        targetSdkVersion 28
-        versionName VERSION_NAME
+        minSdkVersion 14
+        targetSdkVersion 32
 
         testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
     }
 }
 
-repositories {
-    jcenter()
-    google()
-}
-
 dependencies {
     compileOnly project(':dexmaker-mockito')
     androidTestImplementation project(':dexmaker-mockito')
 
-    implementation 'com.android.support.test:runner:0.5'
-    implementation 'junit:junit:4.12'
-    api 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' }
+    implementation 'androidx.test:runner:1.4.0'
+    implementation 'junit:junit:4.13.2'
+    api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' }
 }
diff --git a/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java b/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java
index 4a345b3..126647d 100644
--- a/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java
+++ b/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java
@@ -117,7 +117,7 @@
         parent.measure(100, 100);
     }
 
-    @SuppressLint({"PrivateApi", "CheckReturnValue"})
+    @SuppressLint({"PrivateApi", "CheckReturnValue", "SoonBlockedPrivateApi"})
     @Test
     public void cannotCallBlackListedAfterSpying() {
         // Spying and mocking might change the View class's byte code
@@ -134,7 +134,7 @@
     }
 
     public static class CallBlackListedMethod {
-        @SuppressLint("PrivateApi")
+        @SuppressLint({"PrivateApi", "SoonBlockedPrivateApi"})
         boolean callingBlacklistedMethodCausesException() {
             // Settings.Global#isValidZenMode is a blacklisted method. Resolving it should fail
             try {
@@ -153,7 +153,7 @@
     }
 
     public static abstract class CallBlacklistedMethodAbstract {
-        @SuppressLint("PrivateApi")
+        @SuppressLint({"PrivateApi", "SoonBlockedPrivateApi"})
         public boolean callingBlacklistedMethodCausesException() {
             // Settings.Global#isValidZenMode is a blacklisted method. Resolving it should fail
             try {
diff --git a/dexmaker-mockito/build.gradle b/dexmaker-mockito/build.gradle
index b15b30c..c3095ad 100644
--- a/dexmaker-mockito/build.gradle
+++ b/dexmaker-mockito/build.gradle
@@ -1,30 +1,26 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
+plugins {
+    id("net.ltgt.errorprone") version "1.3.0"
 }
 
-apply plugin: "net.ltgt.errorprone"
-apply plugin: 'java'
-apply from: "$rootDir/gradle/publishing.gradle"
-
-version = VERSION_NAME
 description = "Implementation of the Mockito API for use on the Android Dalvik VM"
 
+apply plugin: 'java-library'
+apply from: "$rootDir/gradle/publishing.gradle"
+
 targetCompatibility = '1.7'
 sourceCompatibility = '1.7'
 
-repositories {
-    jcenter()
+tasks.withType(JavaCompile) {
+    options.errorprone {
+        disable("StringSplitter")
+    }
 }
 
 dependencies {
+    errorprone "com.google.errorprone:error_prone_core:2.5.1"
+    errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
+
     implementation project(':dexmaker')
 
-    implementation 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' }
+    api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' }
 }
diff --git a/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java b/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java
index 4cdaf44..df14e66 100644
--- a/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java
+++ b/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java
@@ -39,11 +39,12 @@
     private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
     private boolean isApi28;
 
-    public DexmakerMockMaker() throws Exception {
+    public DexmakerMockMaker() {
         try {
             Class buildVersion = Class.forName("android.os.Build$VERSION");
+
             isApi28 = buildVersion.getDeclaredField("SDK_INT").getInt(null) >= 28;
-        } catch (ClassNotFoundException e) {
+        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
             System.err.println("Could not determine platform API level, assuming >= 28: " + e);
             isApi28 = true;
         }
@@ -76,6 +77,17 @@
                 System.err.println("Cannot allow LenientCopyTool to copy spies of blacklisted "
                         + "fields. This might break spying on system classes.");
             }
+
+            // The ProxyBuilder class needs to be able to see hidden methods in order to proxy
+            // them correctly. If it cannot see blacklisted methods, then other system classes
+            // which call hidden methods on the mock will call through to the real method rather
+            // than the proxy, which may cause crashes or other unexpected behavior.
+            try {
+                allowHiddenApiReflectionFromMethod.invoke(null, ProxyBuilder.class);
+            } catch (InvocationTargetException | IllegalAccessException e) {
+                System.err.println("Cannot allow ProxyBuilder to proxy blacklisted "
+                        + "methods. This might break mocking on system classes.");
+            }
         }
     }
 
diff --git a/dexmaker-tests/build.gradle b/dexmaker-tests/build.gradle
index 5ae5f2d..996008d 100644
--- a/dexmaker-tests/build.gradle
+++ b/dexmaker-tests/build.gradle
@@ -1,25 +1,12 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
-}
-
-apply plugin: "net.ltgt.errorprone"
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.3'
+    compileSdkVersion 32
 
     defaultConfig {
         applicationId 'com.linkedin.dexmaker'
-        minSdkVersion 8
-        targetSdkVersion 28
+        minSdkVersion 14
+        targetSdkVersion 32
         versionCode 1
         versionName VERSION_NAME
 
@@ -27,15 +14,9 @@
     }
 }
 
-repositories {
-    jcenter()
-    google()
-}
-
 dependencies {
     implementation project(":dexmaker")
 
-    //noinspection GradleDependency
-    androidTestImplementation 'com.android.support.test:runner:0.5'
-    androidTestImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test:runner:1.4.0'
+    androidTestImplementation 'junit:junit:4.13.2'
 }
diff --git a/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java b/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java
index af520e1..d718aaa 100644
--- a/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java
+++ b/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java
@@ -172,6 +172,26 @@
     }
 
     @Test
+    public void testLoadDeferredClassConstant() throws Exception {
+        /*
+         * public static String call() {
+         *   Class clazz = Generated.class;
+         *   return clazz.getSimpleName();
+         * }
+         */
+        MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call");
+        Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
+        Local<Class> clazz = code.newLocal(TypeId.get(Class.class));
+        Local<String> retValue = code.newLocal(TypeId.STRING);
+        code.loadDeferredClassConstant(clazz, GENERATED);
+        MethodId<Class, String> getSimpleName = TypeId.get(Class.class).getMethod(TypeId.STRING, "getSimpleName");
+        code.invokeVirtual(getSimpleName, retValue, clazz);
+        code.returnValue(retValue);
+
+        assertEquals("Generated", getMethod().invoke(null));
+    }
+
+    @Test
     public void testCreateLocalMethodAsNull() throws Exception {
         /*
          * public void call(int value) {
@@ -481,6 +501,58 @@
     }
 
     @Test
+    public void testDeclareNativeMethod() throws Exception {
+        /*
+         * class Generated {
+         *   public Generated() {
+         *   }
+         *   public native void nativeMethod();
+         * }
+         */
+
+        addDefaultConstructor();
+        String nativeMethodName = "nativeMethod";
+        MethodId<?, Void> nativeMethodToGenerate = GENERATED.getMethod(TypeId.VOID, nativeMethodName);
+        dexMaker.declare(nativeMethodToGenerate, java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.NATIVE);
+
+        Class<?> generatedClass = generateAndLoad();
+        Object instance = generatedClass.getConstructor().newInstance();
+        Method nativeMethod = instance.getClass().getMethod(nativeMethodName);
+
+        assertTrue((nativeMethod.getModifiers() & NATIVE) != 0);
+        assertTrue((nativeMethod.getModifiers() & PUBLIC) != 0);
+        assertEquals(void.class, nativeMethod.getReturnType());
+        assertEquals(nativeMethodName, nativeMethod.getName());
+        assertEquals(nativeMethod.getParameterTypes().length, 0);
+    }
+
+    @Test
+    public void testDeclareAbstractClassWithAbstractMethod() throws Exception {
+        /*
+         * public abstract class AbstractClass {
+         *   public abstract void abstractMethod();
+         * }
+         */
+
+        dexMaker = new DexMaker();
+        dexMaker.declare(GENERATED, "AbstractClass.java", PUBLIC, TypeId.OBJECT);
+
+        String abstractMethodName = "abstractMethod";
+        MethodId<?, Void> nativeMethodToGenerate = GENERATED.getMethod(TypeId.VOID, abstractMethodName);
+        dexMaker.declare(nativeMethodToGenerate, java.lang.reflect.Modifier.PUBLIC | ABSTRACT);
+
+        Class<?> generatedClass = generateAndLoad();
+        Method nativeMethod = generatedClass.getMethod(abstractMethodName);
+
+        assertTrue((nativeMethod.getModifiers() & ABSTRACT) != 0);
+        assertTrue((nativeMethod.getModifiers() & PUBLIC) != 0);
+        assertEquals(void.class, nativeMethod.getReturnType());
+        assertEquals(abstractMethodName, nativeMethod.getName());
+        assertEquals(nativeMethod.getParameterTypes().length, 0);
+
+    }
+
+    @Test
     public void testReturnType() throws Exception {
         testReturnType(boolean.class, true);
         testReturnType(byte.class, (byte) 5);
@@ -1956,26 +2028,6 @@
     }
 
     @Test
-    public void testAbstractMethodsAreUnsupported() {
-        MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
-        try {
-            dexMaker.declare(methodId, ABSTRACT);
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-    }
-
-    @Test
-    public void testNativeMethodsAreUnsupported() {
-        MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
-        try {
-            dexMaker.declare(methodId, NATIVE);
-            fail();
-        } catch (IllegalArgumentException expected) {
-        }
-    }
-
-    @Test
     public void testSynchronizedFieldsAreUnsupported() {
         try {
             FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "synchronizedField");
@@ -2005,7 +2057,6 @@
     // TODO: don't generate multiple times (?)
     // TODO: test array types
     // TODO: test generating an interface
-    // TODO: declare native method or abstract method
     // TODO: get a thrown exception 'e' into a local
     // TODO: move a primitive or reference
 
diff --git a/dexmaker/build.gradle b/dexmaker/build.gradle
index 7e8762f..01b0bdb 100644
--- a/dexmaker/build.gradle
+++ b/dexmaker/build.gradle
@@ -1,32 +1,28 @@
-buildscript {
-    repositories {
-        maven {
-            url "https://plugins.gradle.org/m2/"
-        }
-    }
-    dependencies {
-        classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
-    }
+plugins {
+    id("net.ltgt.errorprone") version "1.3.0"
 }
 
-apply plugin: "net.ltgt.errorprone"
+description = "A utility for doing compile or runtime code generation targeting Android's Dalvik VM"
+
 apply plugin: 'java'
 apply from: "$rootDir/gradle/publishing.gradle"
 
-version = VERSION_NAME
-description = "A utility for doing compile or runtime code generation targeting Android's Dalvik VM"
-
 targetCompatibility = '1.7'
 sourceCompatibility = '1.7'
 
-repositories {
-    jcenter()
+tasks.withType(JavaCompile) {
+    options.errorprone {
+        disable("StringSplitter")
+    }
 }
 
-tasks.withType(JavaCompile) {
-    options.compilerArgs += ["-Xep:StringSplitter:OFF"]
+javadoc {
+    options.addBooleanOption("Xdoclint:-html", true)
 }
 
 dependencies {
+    errorprone "com.google.errorprone:error_prone_core:2.5.1"
+    errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
+
     implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
 }
diff --git a/dexmaker/src/main/java/com/android/dx/Code.java b/dexmaker/src/main/java/com/android/dx/Code.java
index 715d2b4..c0f2759 100644
--- a/dexmaker/src/main/java/com/android/dx/Code.java
+++ b/dexmaker/src/main/java/com/android/dx/Code.java
@@ -476,15 +476,27 @@
      * must be a primitive, String, Class, TypeId, or null.
      */
     public <T> void loadConstant(Local<T> target, T value) {
+        loadConstantInternal(target, value);
+    }
+
+    /**
+     * Copies a class type in {@code target}. The benefit to using this method vs {@link Code#loadConstant(Local, Object)}
+     * is that the {@code value} can itself be a generated type - {@link TypeId} allows for deferred referencing of class types.
+     */
+    public void loadDeferredClassConstant(Local<Class> target, TypeId value) {
+        loadConstantInternal(target, value);
+    }
+
+    private void loadConstantInternal(Local target, Object value) {
         Rop rop = value == null
-                ? Rops.CONST_OBJECT_NOTHROW
-                : Rops.opConst(target.type.ropType);
+                  ? Rops.CONST_OBJECT_NOTHROW
+                  : Rops.opConst(target.type.ropType);
         if (rop.getBranchingness() == BRANCH_NONE) {
             addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(),
-                    RegisterSpecList.EMPTY, Constants.getConstant(value)));
+                                            RegisterSpecList.EMPTY, Constants.getConstant(value)));
         } else {
             addInstruction(new ThrowingCstInsn(rop, sourcePosition,
-                    RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
+                                               RegisterSpecList.EMPTY, catches, Constants.getConstant(value)));
             moveResult(target, true);
         }
     }
diff --git a/dexmaker/src/main/java/com/android/dx/DexMaker.java b/dexmaker/src/main/java/com/android/dx/DexMaker.java
index e45ead0..b672c53 100644
--- a/dexmaker/src/main/java/com/android/dx/DexMaker.java
+++ b/dexmaker/src/main/java/com/android/dx/DexMaker.java
@@ -32,6 +32,7 @@
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.StdTypeList;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -46,8 +47,8 @@
 import java.util.jar.JarOutputStream;
 
 import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR;
-import static java.lang.reflect.Modifier.PRIVATE;
-import static java.lang.reflect.Modifier.STATIC;
+import static java.lang.reflect.Modifier.*;
+;
 
 /**
  * Generates a <strong>D</strong>alvik <strong>EX</strong>ecutable (dex)
@@ -230,7 +231,7 @@
      *     Modifier#FINAL} and {@link Modifier#ABSTRACT}.
      */
     public void declare(TypeId<?> type, String sourceFile, int flags,
-            TypeId<?> supertype, TypeId<?>... interfaces) {
+                        TypeId<?> supertype, TypeId<?>... interfaces) {
         TypeDeclaration declaration = getTypeDeclaration(type);
         int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT
                 | AccessFlags.ACC_SYNTHETIC;
@@ -265,7 +266,7 @@
             throw new IllegalStateException("already declared: " + method);
         }
 
-        int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED
+        int supportedFlags = Modifier.ABSTRACT | Modifier.NATIVE | Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED
                 | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED
                 | AccessFlags.ACC_SYNTHETIC | AccessFlags.ACC_BRIDGE;
         if ((flags & ~supportedFlags) != 0) {
@@ -515,7 +516,12 @@
         // Check that the file exists. If it does, return a DexClassLoader and skip all
         // the dex bytecode generation.
         if (result.exists()) {
-            return generateClassLoader(result, dexCache, parent);
+            if (!result.canWrite()) {
+                return generateClassLoader(result, dexCache, parent);
+            } else {
+                // Old writable files should be ignored and re-generated
+                result.delete();
+            }
         }
 
         byte[] dex = generate();
@@ -527,14 +533,23 @@
          *
          * TODO: load the dex from memory where supported.
          */
-        result.createNewFile();
-        JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result));
-        JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME);
-        entry.setSize(dex.length);
-        jarOut.putNextEntry(entry);
-        jarOut.write(dex);
-        jarOut.closeEntry();
-        jarOut.close();
+
+        JarOutputStream jarOut =
+                new JarOutputStream(new BufferedOutputStream(new FileOutputStream(result)));
+        result.setReadOnly();
+        try {
+            JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME);
+            entry.setSize(dex.length);
+            jarOut.putNextEntry(entry);
+            try {
+                jarOut.write(dex);
+            } finally {
+                jarOut.closeEntry();
+            }
+        } finally {
+            jarOut.close();
+        }
+
         return generateClassLoader(result, dexCache, parent);
     }
 
@@ -645,6 +660,10 @@
         }
 
         EncodedMethod toEncodedMethod(DexOptions dexOptions) {
+            if((flags & ABSTRACT) != 0 || (flags & NATIVE) != 0){
+                return new EncodedMethod(method.constant, flags, null, StdTypeList.EMPTY);
+            }
+
             RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0);
             LocalVariableInfo locals = null;
             DalvCode dalvCode = RopTranslator.translate(