Merge "Use jarjar on preferences.proto to solve the issue with diamond dependencies on proto_lite runtime." into androidx-master-dev
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index a9d468c..554dd59 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -120,6 +120,8 @@
 const val PROTOBUF_COMPILER = "com.google.protobuf:protoc:3.10.0"
 const val PROTOBUF_LITE = "com.google.protobuf:protobuf-javalite:3.10.0"
 
+const val JARJAR = "org.anarres.jarjar:jarjar-gradle:1.0.1"
+
 // The following versions change depending on whether we are in the main or ui project - the
 // specific versions are configured in build_dependencies.gradle as they are needed during
 // buildSrc configuration. They are then set here in AndroidXPlugin when configuring the root
diff --git a/datastore/datastore-preferences/build.gradle b/datastore/datastore-preferences/build.gradle
index 4954606..a4ba37e 100644
--- a/datastore/datastore-preferences/build.gradle
+++ b/datastore/datastore-preferences/build.gradle
@@ -16,22 +16,39 @@
 
 import static androidx.build.dependencies.DependenciesKt.*
 import androidx.build.LibraryGroups
-import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
+buildscript {
+    dependencies {
+        classpath JARJAR
+    }
+}
+
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
-    id("com.google.protobuf")
+    id("org.anarres.jarjar")
+}
+
+
+// Include :datastore-preferences-proto
+android.libraryVariants.all { variant ->
+    def variantName = variant.name
+    def suffix = variantName.capitalize()
+    def jarjarConfigName = "jarjar${suffix}"
+    def jarjarConf = configurations.register(jarjarConfigName)
+    // Treat datastore-preferences-proto as a local jar and package it inside the aar
+    dependencies.add(jarjarConfigName, project.dependencies.project(
+            path: ":datastore:datastore-preferences:datastore-preferences-proto",
+            configuration: jarjarConfigName))
+    dependencies.add("${variantName}Implementation", files(jarjarConf))
 }
 
 dependencies {
     api(KOTLIN_STDLIB)
     api(project(":datastore:datastore-core"))
 
-    implementation(PROTOBUF_LITE)
-
     testImplementation(JUNIT)
     testImplementation(KOTLIN_COROUTINES_TEST)
     testImplementation(KOTLIN_TEST)
@@ -43,26 +60,6 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
 }
 
-protobuf {
-    protoc {
-        artifact = PROTOBUF_COMPILER
-    }
-
-    // Generates the java proto-lite code for the protos in this project. See
-    // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
-    // for more information.
-    generateProtoTasks {
-        all().each { task ->
-            task.builtins {
-                java {
-                    option 'lite'
-                }
-            }
-        }
-    }
-}
-
-
 androidx {
     name = "Android Preferences DataStore"
     publish = Publish.SNAPSHOT_AND_RELEASE
diff --git a/datastore/datastore-preferences/datastore-preferences-proto/build.gradle b/datastore/datastore-preferences/datastore-preferences-proto/build.gradle
new file mode 100644
index 0000000..d6e367d
--- /dev/null
+++ b/datastore/datastore-preferences/datastore-preferences-proto/build.gradle
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.anarres.gradle.plugin.jarjar.JarjarTask
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.Publish
+
+
+buildscript {
+    dependencies {
+        classpath 'org.anarres.jarjar:jarjar-gradle:1.0.1'
+    }
+}
+
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+    id("com.google.protobuf")
+}
+
+apply plugin: 'org.anarres.jarjar'
+
+
+dependencies {
+    implementation(PROTOBUF_LITE)
+    api(project(":datastore:datastore-core"))
+}
+
+protobuf {
+    protoc {
+        artifact = PROTOBUF_COMPILER
+    }
+
+    // Generates the java proto-lite code for the protos in this project. See
+    // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
+    // for more information.
+    generateProtoTasks {
+        all().each { task ->
+            task.builtins {
+                java {
+                    option 'lite'
+                }
+            }
+        }
+    }
+}
+
+android.libraryVariants.all { variant ->
+    def variantName = variant.name
+    def suffix = variantName.capitalize()
+    def jarjarTask = tasks.register("jarjar${suffix}", JarjarTask) {
+        destinationName "datastore-preferences-proto-${variantName}-jarjar.jar"
+        dependsOn protoLiteJarWithoutProtoFiles
+        from(files(protoLiteJarWithoutProtoFiles.archiveFile.get().getAsFile()))
+
+        from files(variant.javaCompileProvider.get().destinationDir)
+        dependsOn variant.javaCompileProvider.get()
+
+        if (suffix == "Debug") {
+            from(files(compileDebugKotlin.outputs.files))
+        } else if (suffix == "Release") {
+            from(files(compileReleaseKotlin.outputs.files))
+        } else {
+            throw IllegalStateException("Expected either debug or release variant.")
+        }
+        classRename 'com.google.protobuf.**', 'androidx.datastore.preferences.protobuf.@1'
+    }
+
+    def jarjarConf = configurations.register("jarjar${suffix}")
+    artifacts.add("${jarjarConf.name}", jarjarTask.get().destinationPath) {
+        name "datastore-preferences-proto-${variantName}-jarjar"
+        type 'jar'
+        builtBy jarjarTask
+    }
+}
+
+// The proto-lite dependency includes .proto files, which are not used by icing. When apps depend on
+// appsearch as well as proto-lite directly, these files conflict since jarjar only renames the java
+// classes. Remove them here since they are unused.
+tasks.register("protoLiteJarWithoutProtoFiles", Jar) {
+    // Get proto lite jar as a file.
+    def jarFile = configurations.detachedConfiguration(dependencies.create
+            (PROTOBUF_LITE)).getSingleFile()
+
+    // Expand the jar and remove any .proto files.
+    from(zipTree(jarFile)) {
+        exclude("**/*.proto")
+    }
+
+
+    into 'datastore-preferences-proto-lite-dep'
+}
+
+androidx {
+    name = "Android Preferences DataStore Proto"
+    publish = Publish.NONE
+    mavenGroup = LibraryGroups.DATASTORE
+    inceptionYear = "2020"
+    description = "Jarjar the generated proto and proto-lite dependency for use by " +
+            "datastore-preferences."
+}
diff --git a/datastore/datastore-preferences/datastore-preferences-proto/src/main/AndroidManifest.xml b/datastore/datastore-preferences/datastore-preferences-proto/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..384c619
--- /dev/null
+++ b/datastore/datastore-preferences/datastore-preferences-proto/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.datastore.preferences.proto">
+
+</manifest>
\ No newline at end of file
diff --git a/datastore/datastore-preferences/datastore-preferences-proto/src/main/java/androidx/datastore/preferences/PreferencesMapCompat.kt b/datastore/datastore-preferences/datastore-preferences-proto/src/main/java/androidx/datastore/preferences/PreferencesMapCompat.kt
new file mode 100644
index 0000000..b7a00c6
--- /dev/null
+++ b/datastore/datastore-preferences/datastore-preferences-proto/src/main/java/androidx/datastore/preferences/PreferencesMapCompat.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.datastore.preferences
+
+import androidx.datastore.CorruptionException
+import com.google.protobuf.InvalidProtocolBufferException
+import java.io.InputStream
+
+/**
+ * Read PreferenceMap proto but convert InvalidProtocolBufferExceptions to CorruptionExceptions.
+ * @hide
+ */
+class PreferencesMapCompat {
+    companion object {
+        fun readFrom(input: InputStream): PreferencesProto.PreferenceMap {
+            return try {
+                PreferencesProto.PreferenceMap.parseFrom(input)
+            } catch (ipbe: InvalidProtocolBufferException) {
+                throw CorruptionException("Unable to parse preferences proto.", ipbe)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-preferences/src/main/proto/preferences.proto b/datastore/datastore-preferences/datastore-preferences-proto/src/main/proto/preferences.proto
similarity index 100%
rename from datastore/datastore-preferences/src/main/proto/preferences.proto
rename to datastore/datastore-preferences/datastore-preferences-proto/src/main/proto/preferences.proto
diff --git a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferencesSerializer.kt b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferencesSerializer.kt
index 44949c2..2f4fa2c 100644
--- a/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferencesSerializer.kt
+++ b/datastore/datastore-preferences/src/main/java/androidx/datastore/preferences/PreferencesSerializer.kt
@@ -21,7 +21,6 @@
 import androidx.datastore.preferences.PreferencesProto.Value
 import androidx.datastore.preferences.PreferencesProto.StringSet
 import androidx.datastore.Serializer
-import com.google.protobuf.InvalidProtocolBufferException
 import java.io.IOException
 import java.io.InputStream
 import java.io.OutputStream
@@ -37,14 +36,7 @@
 
     @Throws(IOException::class, CorruptionException::class)
     override fun readFrom(input: InputStream): Preferences {
-        val preferencesProto = try {
-            PreferenceMap.parseFrom(input)
-        } catch (invalidProtocolBufferException: InvalidProtocolBufferException) {
-            throw CorruptionException(
-                "Unable to parse preferences proto.",
-                invalidProtocolBufferException
-            )
-        }
+        val preferencesProto = PreferencesMapCompat.readFrom(input)
 
         val preferencesMap = preferencesProto.preferencesMap.mapValues {
             convertProtoToObject(it.value)
diff --git a/settings.gradle b/settings.gradle
index 9945728..ff95a5c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -141,6 +141,8 @@
 includeProject(":customview:customview", "customview/customview")
 includeProject(":datastore:datastore-core", "datastore/datastore-core")
 includeProject(":datastore:datastore-preferences", "datastore/datastore-preferences")
+includeProject(":datastore:datastore-preferences:datastore-preferences-proto",
+        "datastore/datastore-preferences/datastore-preferences-proto")
 includeProject(":datastore:datastore-proto", "datastore/datastore-proto")
 includeProject(":datastore:datastore-sampleapp", "datastore/datastore-sampleapp")
 includeProject(":documentfile:documentfile", "documentfile/documentfile")
@@ -210,7 +212,7 @@
 includeProject(":lifecycle:lifecycle-service", "lifecycle/lifecycle-service")
 includeProject(":lifecycle:lifecycle-viewmodel", "lifecycle/lifecycle-viewmodel")
 includeProject(":lifecycle:lifecycle-viewmodel-ktx", "lifecycle/lifecycle-viewmodel-ktx")
-includeProject(":lifecycle:lifecycle-viewmodel-savedstate","lifecycle/lifecycle-viewmodel-savedstate")
+includeProject(":lifecycle:lifecycle-viewmodel-savedstate", "lifecycle/lifecycle-viewmodel-savedstate")
 includeProject(":lint-demos:lint-demo-appcompat", "lint-demos/lint-demo-appcompat")
 includeProject(":loader:loader", "loader/loader")
 includeProject(":loader:loader-ktx", "loader/loader-ktx")