Add Cronet to main build

Building now requires an Android SDK unless you specify
-PskipAndroid=true
diff --git a/.travis.yml b/.travis.yml
index c80ca70..0dc7590 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,6 +18,7 @@
   - echo "checkstyle.ignoreFailures=false" >> $HOME/.gradle/gradle.properties
   - echo "failOnWarnings=true" >> $HOME/.gradle/gradle.properties
   - echo "errorProne=true" >> $HOME/.gradle/gradle.properties
+  - echo "skipAndroid=true" >> $HOME/.gradle/gradle.properties
 
 install:
   - ./gradlew assemble syncGeneratedSources publishToMavenLocal
diff --git a/COMPILING.md b/COMPILING.md
index 486fb7b..a7d9c4e 100644
--- a/COMPILING.md
+++ b/COMPILING.md
@@ -11,6 +11,11 @@
 codegen, the build can skip it. To skip, create the file
 `<project-root>/gradle.properties` and add `skipCodegen=true`.
 
+Some parts of grpc-java depend on Android. Since many Java developers don't have
+the Android SDK installed and don't need to run or modify the Android
+components, the build can skip it. To skip, create the file
+`<project-root>/gradle.properties` and add `skipAndroid=true`.
+
 Then, to build, run:
 ```
 $ ./gradlew build
diff --git a/build.gradle b/build.gradle
index 38f2797..320441d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,5 @@
 plugins {
+    id "com.android.library" apply false // Necessary for Android plugin to find its classes
     id "com.google.osdetector" apply false
     id "me.champeau.gradle.japicmp" apply false
     id "net.ltgt.errorprone" apply false
@@ -114,6 +115,8 @@
             android_annotations: "com.google.android:annotations:4.1.1.4",
             animalsniffer_annotations: "org.codehaus.mojo:animal-sniffer-annotations:1.18",
             errorprone: "com.google.errorprone:error_prone_annotations:2.3.4",
+            cronet_api: 'org.chromium.net:cronet-api:76.3809.111',
+            cronet_embedded: 'org.chromium.net:cronet-embedded:66.3359.158',
             gson: "com.google.code.gson:gson:2.8.6",
             guava: "com.google.guava:guava:${guavaVersion}",
             hpack: 'com.twitter:hpack:0.10.1',
@@ -154,6 +157,7 @@
             mockito: 'org.mockito:mockito-core:2.28.2',
             truth: 'com.google.truth:truth:1.0',
             guava_testlib: "com.google.guava:guava-testlib:${guavaVersion}",
+            robolectric: "org.robolectric:robolectric:3.5.1",
 
             // Benchmark dependencies
             hdrhistogram: 'org.hdrhistogram:HdrHistogram:2.1.10',
@@ -208,6 +212,22 @@
         }
     }
 
+    if (rootProject.properties.get('errorProne', true)) {
+        dependencies {
+            errorprone 'com.google.errorprone:error_prone_core:2.3.4'
+            errorproneJavac 'com.google.errorprone:javac:9+181-r4173-1'
+        }
+    } else {
+        // Disable Error Prone
+        allprojects {
+            afterEvaluate { project ->
+                project.tasks.withType(JavaCompile) {
+                    options.errorprone.enabled = false
+                }
+            }
+        }
+    }
+
     plugins.withId("java") {
         sourceCompatibility = 1.7
         targetCompatibility = 1.7
@@ -263,20 +283,8 @@
 
         if (rootProject.properties.get('errorProne', true)) {
             dependencies {
-                errorprone 'com.google.errorprone:error_prone_core:2.3.4'
-                errorproneJavac 'com.google.errorprone:javac:9+181-r4173-1'
-
                 annotationProcessor 'com.google.guava:guava-beta-checker:1.0'
             }
-        } else {
-            // Disable Error Prone
-            allprojects {
-                afterEvaluate { project ->
-                    project.tasks.withType(JavaCompile) {
-                        options.errorprone.enabled = false
-                    }
-                }
-            }
         }
 
         compileJava {
diff --git a/buildscripts/kokoro/android.sh b/buildscripts/kokoro/android.sh
index 358db09..909c446 100755
--- a/buildscripts/kokoro/android.sh
+++ b/buildscripts/kokoro/android.sh
@@ -10,12 +10,18 @@
 
 cd "$BASE_DIR/github/grpc-java"
 
-export GRADLE_OPTS=-Xmx512m
 export LDFLAGS=-L/tmp/protobuf/lib
 export CXXFLAGS=-I/tmp/protobuf/include
 export LD_LIBRARY_PATH=/tmp/protobuf/lib
 export OS_NAME=$(uname)
 
+cat <<EOF >> gradle.properties
+# defaults to -Xmx512m -XX:MaxMetaspaceSize=256m
+# https://docs.gradle.org/current/userguide/build_environment.html#sec:configuring_jvm_memory
+# Increased due to java.lang.OutOfMemoryError: Metaspace failures
+org.gradle.jvmargs=-Xmx512m -XX:MaxMetaspaceSize=512m
+EOF
+
 echo y | ${ANDROID_HOME}/tools/bin/sdkmanager "build-tools;28.0.3"
 
 # Proto deps
diff --git a/buildscripts/kokoro/gae-interop.sh b/buildscripts/kokoro/gae-interop.sh
index cbdff2d..b397303 100755
--- a/buildscripts/kokoro/gae-interop.sh
+++ b/buildscripts/kokoro/gae-interop.sh
@@ -28,7 +28,7 @@
 ##
 ## Deploy the dummy 'default' version of the service
 ##
-GRADLE_FLAGS="--stacktrace -DgaeStopPreviousVersion=false -PskipCodegen=true"
+GRADLE_FLAGS="--stacktrace -DgaeStopPreviousVersion=false -PskipCodegen=true -PskipAndroid=true"
 
 # Deploy the dummy 'default' version. We only require that it exists when cleanup() is called.
 # It ok if we race with another run and fail here, because the end result is idempotent.
diff --git a/buildscripts/kokoro/linux_artifacts.sh b/buildscripts/kokoro/linux_artifacts.sh
index cdf3607..ed448ff 100755
--- a/buildscripts/kokoro/linux_artifacts.sh
+++ b/buildscripts/kokoro/linux_artifacts.sh
@@ -24,7 +24,6 @@
 
 pushd "$GRPC_JAVA_DIR/cronet"
 ../gradlew publish \
-  --include-build "$GRPC_JAVA_DIR" \
   -Dorg.gradle.parallel=false \
   -PskipCodegen=true \
   -PrepositoryDir="$LOCAL_MVN_TEMP"
diff --git a/buildscripts/kokoro/unix.sh b/buildscripts/kokoro/unix.sh
index c64bc7e..343b1ae 100755
--- a/buildscripts/kokoro/unix.sh
+++ b/buildscripts/kokoro/unix.sh
@@ -36,6 +36,7 @@
 GRADLE_FLAGS+=" -Pcheckstyle.ignoreFailures=false"
 GRADLE_FLAGS+=" -PfailOnWarnings=true"
 GRADLE_FLAGS+=" -PerrorProne=true"
+GRADLE_FLAGS+=" -PskipAndroid=true"
 GRADLE_FLAGS+=" -Dorg.gradle.parallel=true"
 export GRADLE_OPTS="-Xmx512m"
 
diff --git a/buildscripts/kokoro/windows32.bat b/buildscripts/kokoro/windows32.bat
index ca4549d..5a87b77 100644
--- a/buildscripts/kokoro/windows32.bat
+++ b/buildscripts/kokoro/windows32.bat
@@ -28,7 +28,7 @@
 SET FAIL_ON_WARNINGS=true
 SET VC_PROTOBUF_LIBS=%ESCWORKSPACE%\\grpc-java-helper32\\protobuf-%PROTOBUF_VER%\\cmake\\build\\Release
 SET VC_PROTOBUF_INCLUDE=%ESCWORKSPACE%\\grpc-java-helper32\\protobuf-%PROTOBUF_VER%\\cmake\\build\\include
-SET GRADLE_FLAGS=-PtargetArch=%TARGET_ARCH% -PfailOnWarnings=%FAIL_ON_WARNINGS% -PvcProtobufLibs=%VC_PROTOBUF_LIBS% -PvcProtobufInclude=%VC_PROTOBUF_INCLUDE%
+SET GRADLE_FLAGS=-PtargetArch=%TARGET_ARCH% -PfailOnWarnings=%FAIL_ON_WARNINGS% -PvcProtobufLibs=%VC_PROTOBUF_LIBS% -PvcProtobufInclude=%VC_PROTOBUF_INCLUDE% -PskipAndroid=true
 
 cmd.exe /C "%WORKSPACE%\gradlew.bat %GRADLE_FLAGS% build"
 set GRADLEEXIT=%ERRORLEVEL%
diff --git a/buildscripts/kokoro/windows64.bat b/buildscripts/kokoro/windows64.bat
index ae021a7..edcb421 100644
--- a/buildscripts/kokoro/windows64.bat
+++ b/buildscripts/kokoro/windows64.bat
@@ -27,7 +27,7 @@
 SET FAIL_ON_WARNINGS=true
 SET VC_PROTOBUF_LIBS=%ESCWORKSPACE%\\grpc-java-helper64\\protobuf-%PROTOBUF_VER%\\cmake\\build\\Release
 SET VC_PROTOBUF_INCLUDE=%ESCWORKSPACE%\\grpc-java-helper64\\protobuf-%PROTOBUF_VER%\\cmake\\build\\include
-SET GRADLE_FLAGS=-PtargetArch=%TARGET_ARCH% -PfailOnWarnings=%FAIL_ON_WARNINGS% -PvcProtobufLibs=%VC_PROTOBUF_LIBS% -PvcProtobufInclude=%VC_PROTOBUF_INCLUDE%
+SET GRADLE_FLAGS=-PtargetArch=%TARGET_ARCH% -PfailOnWarnings=%FAIL_ON_WARNINGS% -PvcProtobufLibs=%VC_PROTOBUF_LIBS% -PvcProtobufInclude=%VC_PROTOBUF_INCLUDE% -PskipAndroid=true
 
 @rem make sure no daemons have any files open
 cmd.exe /C "%WORKSPACE%\gradlew.bat --stop"
diff --git a/cronet/build.gradle b/cronet/build.gradle
index 4130d3a..aac89f0 100644
--- a/cronet/build.gradle
+++ b/cronet/build.gradle
@@ -1,33 +1,15 @@
-apply plugin: 'com.android.library'
+plugins {
+    id "maven-publish"
 
-group = "io.grpc"
-version = "1.27.0-SNAPSHOT" // CURRENT_GRPC_VERSION
-description = "gRPC: Cronet Android"
-
-buildscript {
-    repositories {
-        google()
-        jcenter()
-        mavenCentral()
-        maven { url "https://plugins.gradle.org/m2/" }
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:3.3.0'
-        classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.8.1'
-        classpath 'digital.wup:android-maven-publish:3.6.2'
-    }
+    id "com.android.library"
+    id "digital.wup.android-maven-publish"
 }
 
-apply plugin: "maven-publish"
-apply plugin: "net.ltgt.errorprone"
-apply plugin: "digital.wup.android-maven-publish"
-apply plugin: "signing"
+description = "gRPC: Cronet Android"
 
 repositories {
     google()
     jcenter()
-    mavenCentral()
-    mavenLocal()
 }
 
 android {
@@ -51,18 +33,15 @@
 }
 
 dependencies {
-    errorprone 'com.google.errorprone:error_prone_core:2.3.4'
-    errorproneJavac 'com.google.errorprone:javac:9+181-r4173-1'
+    implementation project(':grpc-core')
+    testImplementation project(':grpc-testing')
 
-    implementation 'io.grpc:grpc-core:1.27.0-SNAPSHOT' // CURRENT_GRPC_VERSION
-    testImplementation 'io.grpc:grpc-testing:1.27.0-SNAPSHOT' // CURRENT_GRPC_VERSION
+    implementation libraries.cronet_api
+    testImplementation libraries.cronet_embedded
 
-    implementation 'org.chromium.net:cronet-api:76.3809.111'
-    testImplementation 'org.chromium.net:cronet-embedded:66.3359.158'
-
-    testImplementation 'junit:junit:4.12'
-    testImplementation 'org.mockito:mockito-core:2.28.2'
-    testImplementation "org.robolectric:robolectric:3.5.1"
+    testImplementation libraries.junit
+    testImplementation libraries.mockito
+    testImplementation libraries.robolectric
 }
 
 task javadocs(type: Javadoc) {
@@ -94,81 +73,11 @@
 
 publishing {
     publications {
-        maven(MavenPublication) {
+        maven {
             from components.android
 
             artifact javadocJar
             artifact sourcesJar
-
-            pom {
-                name = project.group + ":" + project.name
-                url = 'https://github.com/grpc/grpc-java'
-                afterEvaluate {
-                    // description is not available until evaluated.
-                    description = project.description
-                }
-
-                scm {
-                    connection = 'scm:git:https://github.com/grpc/grpc-java.git'
-                    developerConnection = 'scm:git:[email protected]:grpc/grpc-java.git'
-                    url = 'https://github.com/grpc/grpc-java'
-                }
-
-                licenses {
-                    license {
-                        name = 'Apache 2.0'
-                        url = 'https://opensource.org/licenses/Apache-2.0'
-                    }
-                }
-
-                developers {
-                    developer {
-                        id = "grpc.io"
-                        name = "gRPC Contributors"
-                        email = "[email protected]"
-                        url = "https://grpc.io/"
-                        organization = "gRPC Authors"
-                        organizationUrl = "https://www.google.com"
-                    }
-                }
-
-                withXml {
-                    asNode().dependencies.'*'.findAll() { dep ->
-                        dep.artifactId.text() in ['grpc-api', 'grpc-core']
-                    }.each() { core ->
-                        core.version*.value = "[" + core.version.text() + "]"
-                    }
-                }
-            }
         }
     }
-    repositories {
-        maven {
-            if (rootProject.hasProperty('repositoryDir')) {
-                url = new File(rootProject.repositoryDir).toURI()
-            } else {
-                String stagingUrl
-                if (rootProject.hasProperty('repositoryId')) {
-                    stagingUrl = 'https://oss.sonatype.org/service/local/staging/deployByRepositoryId/' +
-                            rootProject.repositoryId
-                } else {
-                    stagingUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
-                }
-                credentials {
-                    if (rootProject.hasProperty('ossrhUsername') && rootProject.hasProperty('ossrhPassword')) {
-                        username = rootProject.ossrhUsername
-                        password = rootProject.ossrhPassword
-                    }
-                }
-                def releaseUrl = stagingUrl
-                def snapshotUrl = 'https://oss.sonatype.org/content/repositories/snapshots/'
-                url = version.endsWith('SNAPSHOT') ? snapshotUrl : releaseUrl
-            }
-        }
-    }
-}
-
-signing {
-    required false
-    sign publishing.publications.maven
 }
diff --git a/cronet/settings.gradle b/cronet/settings.gradle
deleted file mode 100644
index ca2b16c..0000000
--- a/cronet/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-rootProject.name = 'grpc-cronet'
diff --git a/cronet/src/main/java/io/grpc/cronet/CronetClientStream.java b/cronet/src/main/java/io/grpc/cronet/CronetClientStream.java
index 721432f..99c5454 100644
--- a/cronet/src/main/java/io/grpc/cronet/CronetClientStream.java
+++ b/cronet/src/main/java/io/grpc/cronet/CronetClientStream.java
@@ -47,10 +47,8 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
 import java.util.concurrent.Executor;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.GuardedBy;
@@ -258,7 +256,7 @@
   class TransportState extends Http2ClientStreamTransportState {
     private final Object lock;
     @GuardedBy("lock")
-    private Queue<PendingData> pendingData = new LinkedList<PendingData>();
+    private Collection<PendingData> pendingData = new ArrayList<PendingData>();
     @GuardedBy("lock")
     private boolean streamReady;
     @GuardedBy("lock")
diff --git a/cronet/src/test/java/io/grpc/cronet/CronetClientStreamTest.java b/cronet/src/test/java/io/grpc/cronet/CronetClientStreamTest.java
index 6ff9d1e..34a8b60 100644
--- a/cronet/src/test/java/io/grpc/cronet/CronetClientStreamTest.java
+++ b/cronet/src/test/java/io/grpc/cronet/CronetClientStreamTest.java
@@ -257,7 +257,7 @@
     callback.onReadCompleted(
         cronetStream,
         info,
-        (ByteBuffer) createMessageFrame(new String("response1").getBytes(Charset.forName("UTF-8"))),
+        createMessageFrame(new String("response1").getBytes(Charset.forName("UTF-8"))),
         false);
     // Haven't request any message, so no callback is called here.
     verify(clientListener, times(0)).messagesAvailable(isA(MessageProducer.class));
@@ -308,7 +308,7 @@
     callback.onReadCompleted(
         cronetStream,
         info,
-        (ByteBuffer) createMessageFrame(new String("response").getBytes(Charset.forName("UTF-8"))),
+        createMessageFrame(new String("response").getBytes(Charset.forName("UTF-8"))),
         false);
     verify(clientListener, times(1)).messagesAvailable(isA(MessageProducer.class));
     verify(cronetStream, times(2)).read(isA(ByteBuffer.class));
@@ -573,6 +573,7 @@
     assertEquals(Status.UNAUTHENTICATED.getCode(), status.getCode());
   }
 
+  @SuppressWarnings("deprecation")
   @Test
   public void addCronetRequestAnnotation_deprecated() {
     Object annotation = new Object();
diff --git a/settings.gradle b/settings.gradle
index 3f9a7e6..0ce56b2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,14 +1,27 @@
 pluginManagement {
     plugins {
+        id "com.android.library" version "3.3.0"
         id "com.github.johnrengelman.shadow" version "2.0.4"
         id "com.github.kt3k.coveralls" version "2.0.1"
         id "com.google.osdetector" version "1.4.0"
         id "com.google.protobuf" version "0.8.8"
+        id "digital.wup.android-maven-publish" version "3.6.2"
         id "me.champeau.gradle.japicmp" version "0.2.5"
         id "me.champeau.gradle.jmh" version "0.4.5"
         id "net.ltgt.errorprone" version "0.8.1"
         id "ru.vyarus.animalsniffer" version "1.5.0"
     }
+    resolutionStrategy {
+        eachPlugin {
+            if (target.id.namespace == "com.android") {
+                useModule("com.android.tools.build:gradle:${target.version}")
+            }
+        }
+    }
+    repositories {
+        gradlePluginPortal()
+        google()
+    }
 }
 
 rootProject.name = "grpc"
@@ -62,3 +75,11 @@
     include ":grpc-compiler"
     project(':grpc-compiler').projectDir = "$rootDir/compiler" as File
 }
+
+if (settings.hasProperty('skipAndroid') && skipAndroid.toBoolean()) {
+    println '  * Skipping the build of Android projects because skipAndroid=true'
+} else {
+    println '*** Android SDK is required. To avoid building Android projects, set -PskipAndroid=true'
+    include ":grpc-cronet"
+    project(':grpc-cronet').projectDir = "$rootDir/cronet" as File
+}