Merge "Fix flaky tests due to time unit quantization errors." into androidx-master-dev
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
index 5224667..1653a0e 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
@@ -30,6 +30,12 @@
     fun insertAll(t: List<T>)
 
     @Insert
+    fun insertAllSet(t: Set<T>): List<Long>
+
+    @Insert
+    fun insertAllCollection(t: Collection<T>): Array<Long>
+
+    @Insert
     fun insertAllArg(vararg t: T)
 
     @Update
diff --git a/room/runtime/api/restricted_2.2.0-alpha02.txt b/room/runtime/api/restricted_2.2.0-alpha02.txt
index f3a1d00..8111b31 100644
--- a/room/runtime/api/restricted_2.2.0-alpha02.txt
+++ b/room/runtime/api/restricted_2.2.0-alpha02.txt
@@ -38,12 +38,12 @@
     method public final void insert(T![]!);
     method public final void insert(Iterable<? extends T>!);
     method public final long insertAndReturnId(T!);
-    method public final long[]! insertAndReturnIdsArray(java.util.Collection<T!>!);
+    method public final long[]! insertAndReturnIdsArray(java.util.Collection<? extends T>!);
     method public final long[]! insertAndReturnIdsArray(T![]!);
-    method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<T!>!);
+    method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<? extends T>!);
     method public final Long![]! insertAndReturnIdsArrayBox(T![]!);
     method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(T![]!);
-    method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<T!>!);
+    method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<? extends T>!);
   }
 
   public class InvalidationTracker {
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index f3a1d00..8111b31 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -38,12 +38,12 @@
     method public final void insert(T![]!);
     method public final void insert(Iterable<? extends T>!);
     method public final long insertAndReturnId(T!);
-    method public final long[]! insertAndReturnIdsArray(java.util.Collection<T!>!);
+    method public final long[]! insertAndReturnIdsArray(java.util.Collection<? extends T>!);
     method public final long[]! insertAndReturnIdsArray(T![]!);
-    method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<T!>!);
+    method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<? extends T>!);
     method public final Long![]! insertAndReturnIdsArrayBox(T![]!);
     method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(T![]!);
-    method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<T!>!);
+    method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<? extends T>!);
   }
 
   public class InvalidationTracker {
diff --git a/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java b/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java
index 8572f9e..3046d6c 100644
--- a/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java
+++ b/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java
@@ -123,7 +123,7 @@
      * @param entities Entities to insert
      * @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
      */
-    public final long[] insertAndReturnIdsArray(Collection<T> entities) {
+    public final long[] insertAndReturnIdsArray(Collection<? extends T> entities) {
         final SupportSQLiteStatement stmt = acquire();
         try {
             final long[] result = new long[entities.size()];
@@ -167,7 +167,7 @@
      * @param entities Entities to insert
      * @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
      */
-    public final Long[] insertAndReturnIdsArrayBox(Collection<T> entities) {
+    public final Long[] insertAndReturnIdsArrayBox(Collection<? extends T> entities) {
         final SupportSQLiteStatement stmt = acquire();
         try {
             final Long[] result = new Long[entities.size()];
@@ -233,7 +233,7 @@
      * @param entities Entities to insert
      * @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
      */
-    public final List<Long> insertAndReturnIdsList(Collection<T> entities) {
+    public final List<Long> insertAndReturnIdsList(Collection<? extends T> entities) {
         final SupportSQLiteStatement stmt = acquire();
         try {
             final List<Long> result = new ArrayList<>(entities.size());
diff --git a/test/screenshot/build.gradle b/test/screenshot/build.gradle
index 2335202..a740b0b 100644
--- a/test/screenshot/build.gradle
+++ b/test/screenshot/build.gradle
@@ -25,6 +25,8 @@
 }
 
 dependencies {
+    implementation(ANDROIDX_TEST_RUNNER)
+
     androidTestImplementation(JUNIT)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt b/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt
index 05e0ebd..6eef598 100644
--- a/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt
+++ b/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt
@@ -19,15 +19,18 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import org.junit.Assert.assertTrue
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 class ScreenshotTestTest {
 
+    @get:Rule
+    val screenshotTestRule = ScreenshotTestRule()
+
     @Test
     @SmallTest
-    @ScreenshotTest
     fun screenshotTestTest_dummy() {
         assertTrue(true)
     }
diff --git a/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTest.java b/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTest.java
deleted file mode 100644
index ecffe28..0000000
--- a/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2019 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.test.screenshot;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to assign a screenshot test qualifier to a test. This annotation can be used at a
- * method or class level.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface ScreenshotTest {
-}
-
diff --git a/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.java b/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.java
new file mode 100644
index 0000000..56c40a5
--- /dev/null
+++ b/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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.test.screenshot;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.Assume;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class ScreenshotTestRule implements TestRule {
+
+    @Override
+    public @NonNull Statement apply(@NonNull Statement base, @Nullable Description description) {
+        return new ScreenshotTestStatement(base);
+    }
+
+    static class ScreenshotTestStatement extends Statement {
+
+        final Statement mBase;
+
+        ScreenshotTestStatement(Statement base) {
+            super();
+            mBase = base;
+        }
+
+        @Override
+        public void evaluate() throws Throwable {
+            Assume.assumeTrue(android.os.Build.MODEL.contains("Cuttlefish"));
+            mBase.evaluate();
+        }
+    }
+}
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt b/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
index 68eb8839..9c3a3e4 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
@@ -44,6 +44,7 @@
     val maxHeight: IntPx = IntPx.Infinity
 ) {
     init {
+        // TODO(mount/popam): This verification is costly. Can we avoid it sometimes or at least on production?
         require(minWidth.isFinite()) { "Constraints#minWidth should be finite" }
         require(minHeight.isFinite()) { "Constraints#minHeight should be finite" }
         require(minWidth <= maxWidth) {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
index d48e272..e8f57e0 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
@@ -70,17 +70,17 @@
     Layout(
         childrenArray = arrayOf(header, content, footer),
         layoutBlock = { measurables, constraints ->
-            val headerPlaceable = measurables[header as () -> Unit].first().measure(
+            val headerPlaceable = measurables[header].first().measure(
                 Constraints.tightConstraints(constraints.maxWidth, 100.ipx)
             )
             val footerPadding = 50.ipx
-            val footerPlaceable = measurables[footer as () -> Unit].first().measure(
+            val footerPlaceable = measurables[footer].first().measure(
                 Constraints.tightConstraints(constraints.maxWidth - footerPadding * 2, 100.ipx)
             )
             val itemHeight =
                 (constraints.maxHeight - headerPlaceable.height - footerPlaceable.height) /
-                        measurables[content as () -> Unit].size
-            val contentPlaceables = measurables[content as () -> Unit].map { measurable ->
+                        measurables[content].size
+            val contentPlaceables = measurables[content].map { measurable ->
                 measurable.measure(Constraints.tightConstraints(constraints.maxWidth, itemHeight))
             }
 
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
index cb22af9..27ace8d 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
@@ -454,11 +454,11 @@
                         measurables.forEachIndexed { index, measurable ->
                             measurable.measure(childConstraints[index])
                         }
-                        assertEquals(headerChildrenCount, measurables[header as () -> Unit].size)
-                        assertSame(measurables[0], measurables[header as () -> Unit][0])
-                        assertEquals(footerChildrenCount, measurables[footer as () -> Unit].size)
-                        assertSame(measurables[1], measurables[footer as () -> Unit][0])
-                        assertSame(measurables[2], measurables[footer as () -> Unit][1])
+                        assertEquals(headerChildrenCount, measurables[header].size)
+                        assertSame(measurables[0], measurables[header][0])
+                        assertEquals(footerChildrenCount, measurables[footer].size)
+                        assertSame(measurables[1], measurables[footer][0])
+                        assertSame(measurables[2], measurables[footer][1])
                     }
                 }
             }
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
index a62f5bb..3938fa1 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
@@ -66,8 +66,9 @@
     internal val layoutNode: LayoutNode
         get() = layoutNodeRef.value!!
 
-    internal val childrenMeasurables: List<Measurable> get() =
-        ComplexLayoutStateMeasurablesList(layoutNode.childrenLayouts().map { it as Measurable })
+    // TODO(mount/popam): This is inefficient
+    internal val childrenMeasurables: List<Measurable>
+        get() = ComplexLayoutStateMeasurablesList(layoutNode)
 
     private var measureIteration = 0L
 
@@ -77,7 +78,8 @@
     }
 
     fun measure(constraints: Constraints): Placeable {
-        val iteration = layoutNode.owner?.measureIteration ?: 0L
+        val owner = layoutNode.owner
+        val iteration = if (owner == null) 0L else owner.measureIteration
         if (measureIteration == iteration) {
             throw IllegalStateException("measure() may not be called multiple times " +
                     "on the same Measurable")
@@ -170,8 +172,32 @@
 }
 
 internal class ComplexLayoutStateMeasurablesList(
-    internal val measurables: List<Measurable>
-) : List<Measurable> by (measurables.filter { it.parentData !is ChildrenEndParentData })
+    layoutNode: LayoutNode
+) : MutableList<Measurable> by mutableListOf() {
+    private val composableRanges = mutableMapOf<@Composable() () -> Unit, IntRange>()
+    fun composables(composable: @Composable() () -> Unit): List<Measurable> {
+        val range = composableRanges[composable] ?: return emptyList()
+        return subList(range.first, range.last)
+    }
+
+    init {
+        // var start = 0 doesn't work because of IR
+        val start = intArrayOf(0)
+        layoutNode.visitLayoutChildren { child ->
+            val layout = child.layout
+            if (layout != null) {
+                layout as Measurable
+                val parentData = layout.parentData
+                if (parentData is ChildrenEndParentData) {
+                    composableRanges[parentData.children] = IntRange(start[0], size)
+                    start[0] = size
+                } else {
+                    this += layout
+                }
+            }
+        }
+    }
+}
 
 /**
  * Receiver scope for the [ComplexLayout] lambda.
@@ -436,17 +462,10 @@
      * Returns all the [Measurable]s emitted for a particular children lambda.
      * TODO(popam): finding measurables for each individual composable is O(n^2), consider improving
      */
-    operator fun List<Measurable>.get(children: () -> Unit): List<Measurable> {
+    operator fun List<Measurable>.get(children: @Composable() () -> Unit): List<Measurable> {
         if (this !is ComplexLayoutStateMeasurablesList) error("Invalid list of measurables")
 
-        val childrenMeasurablesEnd = measurables.indexOfFirst {
-            it.parentData is ChildrenEndParentData &&
-                    (it.parentData as ChildrenEndParentData).children == children
-        }
-        val childrenMeasurablesStart = measurables.take(childrenMeasurablesEnd).indexOfLast {
-            it.parentData is ChildrenEndParentData
-        } + 1
-        return measurables.subList(childrenMeasurablesStart, childrenMeasurablesEnd)
+        return composables(children)
     }
     /**
      * Measure the child [Measurable] with a specific set of [Constraints]. The result
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
index 3b6ab5a..c238d48 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
@@ -115,14 +115,14 @@
             val width = placeable.width
             val height = placeable.height
             val start =
-                measurables[startHandle as () -> Unit].first().measure(
+                measurables[startHandle].first().measure(
                     Constraints.tightConstraints(
                         HANDLE_WIDTH.round(),
                         HANDLE_HEIGHT.round()
                     )
                 )
             val end =
-                measurables[endHandle as () -> Unit].first().measure(
+                measurables[endHandle].first().measure(
                     Constraints.tightConstraints(
                         HANDLE_WIDTH.round(),
                         HANDLE_HEIGHT.round()
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
index c4175ec0..c0c90f7 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
@@ -409,16 +409,16 @@
  * Box [Constraints], but which abstract away width and height in favor of main axis and cross axis.
  */
 private data class OrientationIndependentConstraints(
-    var mainAxisMin: IntPx,
-    var mainAxisMax: IntPx,
-    var crossAxisMin: IntPx,
-    var crossAxisMax: IntPx
+    val mainAxisMin: IntPx,
+    val mainAxisMax: IntPx,
+    val crossAxisMin: IntPx,
+    val crossAxisMax: IntPx
 ) {
     constructor(c: Constraints, orientation: FlexOrientation) : this(
-        if (orientation == FlexOrientation.Horizontal) c.minWidth else c.minHeight,
-        if (orientation == FlexOrientation.Horizontal) c.maxWidth else c.maxHeight,
-        if (orientation == FlexOrientation.Horizontal) c.minHeight else c.minWidth,
-        if (orientation == FlexOrientation.Horizontal) c.maxHeight else c.maxWidth
+        if (orientation === FlexOrientation.Horizontal) c.minWidth else c.minHeight,
+        if (orientation === FlexOrientation.Horizontal) c.maxWidth else c.maxHeight,
+        if (orientation === FlexOrientation.Horizontal) c.minHeight else c.minWidth,
+        if (orientation === FlexOrientation.Horizontal) c.maxHeight else c.maxWidth
     )
 
     // Creates a new instance with the same cross axis constraints and unbounded main axis.
@@ -436,7 +436,7 @@
 
     // Given an orientation, resolves the current instance to traditional constraints.
     fun toBoxConstraints(orientation: FlexOrientation) =
-        if (orientation == FlexOrientation.Horizontal) {
+        if (orientation === FlexOrientation.Horizontal) {
             Constraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)
         } else {
             Constraints(crossAxisMin, crossAxisMax, mainAxisMin, mainAxisMax)
@@ -444,7 +444,7 @@
 
     // Given an orientation, resolves the max width constraint this instance represents.
     fun maxWidth(orientation: FlexOrientation) =
-        if (orientation == FlexOrientation.Horizontal) {
+        if (orientation === FlexOrientation.Horizontal) {
             mainAxisMax
         } else {
             crossAxisMax
@@ -452,7 +452,7 @@
 
     // Given an orientation, resolves the max height constraint this instance represents.
     fun maxHeight(orientation: FlexOrientation) =
-        if (orientation == FlexOrientation.Horizontal) {
+        if (orientation === FlexOrientation.Horizontal) {
             crossAxisMax
         } else {
             mainAxisMax
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
index 5dfa08e..1bdfbc3 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -167,7 +167,9 @@
      * Execute [block] on all children of this ComponentNode.
      */
     fun visitChildren(block: (ComponentNode) -> Unit) {
-        children.forEach(block)
+        for (i in 0 until children.size) {
+            block(children[i])
+        }
     }
 
     /**
diff --git a/webkit/api/1.1.0-alpha02.txt b/webkit/api/1.1.0-alpha02.txt
index 9190ab6..6ef8368 100644
--- a/webkit/api/1.1.0-alpha02.txt
+++ b/webkit/api/1.1.0-alpha02.txt
@@ -134,10 +134,10 @@
 
   public static final class WebViewAssetLoader.Builder {
     ctor public WebViewAssetLoader.Builder();
-    method public androidx.webkit.WebViewAssetLoader.Builder allowHttp(boolean);
+    method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
     method public androidx.webkit.WebViewAssetLoader build();
-    method public androidx.webkit.WebViewAssetLoader.Builder onDomain(String);
-    method public androidx.webkit.WebViewAssetLoader.Builder register(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+    method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+    method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
   }
 
   public static interface WebViewAssetLoader.PathHandler {
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
index 9190ab6..6ef8368 100644
--- a/webkit/api/current.txt
+++ b/webkit/api/current.txt
@@ -134,10 +134,10 @@
 
   public static final class WebViewAssetLoader.Builder {
     ctor public WebViewAssetLoader.Builder();
-    method public androidx.webkit.WebViewAssetLoader.Builder allowHttp(boolean);
+    method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
     method public androidx.webkit.WebViewAssetLoader build();
-    method public androidx.webkit.WebViewAssetLoader.Builder onDomain(String);
-    method public androidx.webkit.WebViewAssetLoader.Builder register(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+    method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+    method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
   }
 
   public static interface WebViewAssetLoader.PathHandler {
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
index 461cae7..7a18003 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
@@ -72,11 +72,11 @@
         // The developer should ALWAYS use a domain which they are in control of or use
         // the default androidplatform.net reserved by Google for this purpose.
         mAssetLoader = new WebViewAssetLoader.Builder()
-                .onDomain("example.com") // use "example.com" instead of the default domain
+                .setDomain("example.com") // use "example.com" instead of the default domain
                 // Host app resources ... under https://example.com/androidx_webkit/example/res/...
-                .register("/androidx_webkit/example/res/", new ResourcesPathHandler(this))
+                .addPathHandler("/androidx_webkit/example/res/", new ResourcesPathHandler(this))
                 // Host app assets under https://example.com/androidx_webkit/example/assets/...
-                .register("/androidx_webkit/example/assets/", new AssetsPathHandler(this))
+                .addPathHandler("/androidx_webkit/example/assets/", new AssetsPathHandler(this))
                 .build();
 
         mWebView = findViewById(R.id.webview_asset_loader_webview);
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java
index 9c0d16b..b36b986 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java
@@ -70,7 +70,7 @@
 
         // Host application assets under http://appassets.androidplatform.net/assets/...
         mAssetLoader = new WebViewAssetLoader.Builder()
-                .register("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
+                .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
                 .build();
         Uri path = new Uri.Builder()
                 .scheme("https")
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
index 742ffc8..70a2b11 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
@@ -84,7 +84,7 @@
         final WebViewTestActivity activity = mActivityRule.getActivity();
 
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                .register("/assets/", new AssetsPathHandler(activity))
+                .addPathHandler("/assets/", new AssetsPathHandler(activity))
                 .build();
 
         mOnUiThread.setWebViewClient(new AssetLoadingWebViewClient(mOnUiThread, assetLoader));
@@ -108,7 +108,7 @@
         final WebViewTestActivity activity = mActivityRule.getActivity();
 
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                .register("/res/", new ResourcesPathHandler(activity))
+                .addPathHandler("/res/", new ResourcesPathHandler(activity))
                 .build();
 
         mOnUiThread.setWebViewClient(new AssetLoadingWebViewClient(mOnUiThread, assetLoader));
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
index c16b058..029425a 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
@@ -97,7 +97,7 @@
     public void testCustomPathHandler() throws Throwable {
         PathHandler pathHandler = new TestPathHandler();
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                                                .register("/test/", pathHandler)
+                                                .addPathHandler("/test/", pathHandler)
                                                 .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -113,15 +113,15 @@
     public void testCustomDomain() throws Throwable {
         PathHandler pathHandler = new TestPathHandler();
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                                                .onDomain("test.myDomain.net")
-                                                .register("/test/", pathHandler)
+                                                .setDomain("test.myDomain.net")
+                                                .addPathHandler("/test/", pathHandler)
                                                 .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
                 Uri.parse("https://test.myDomain.net/test/"));
         assertResponse(response, CONTENTS);
 
-        Assert.assertNull("non-registered URL should return null response",
+        Assert.assertNull("non-addPathHandlered URL should return null response",
                 assetLoader.shouldInterceptRequest(
                         Uri.parse("https://appassets.androidplatform.net/test/")));
     }
@@ -131,8 +131,8 @@
     public void testAllowingHttp() throws Throwable {
         PathHandler pathHandler = new TestPathHandler();
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                                                .allowHttp(true)
-                                                .register("/test/", pathHandler)
+                                                .setHttpAllowed(true)
+                                                .addPathHandler("/test/", pathHandler)
                                                 .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -149,7 +149,7 @@
     public void testDisallowingHttp() throws Throwable {
         PathHandler pathHandler = new TestPathHandler();
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                                                .register("/test/", pathHandler)
+                                                .addPathHandler("/test/", pathHandler)
                                                 .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -180,7 +180,7 @@
             }
         });
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                                                      .register("/assets/", assetsPathHandler)
+                                                      .addPathHandler("/assets/", assetsPathHandler)
                                                       .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -207,7 +207,7 @@
             }
         });
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                                                      .register("/res/", resourcesPathHandler)
+                                                      .addPathHandler("/res/", resourcesPathHandler)
                                                       .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -221,7 +221,7 @@
         WebViewAssetLoader.Builder builder = new WebViewAssetLoader.Builder();
         for (int i = 1; i <= 5; ++i) {
             final String testContent = CONTENTS + Integer.toString(i);
-            builder.register("/test_path_" + Integer.toString(i) + "/", new PathHandler() {
+            builder.addPathHandler("/test_path_" + Integer.toString(i) + "/", new PathHandler() {
                 @Override
                 public WebResourceResponse handle(@NonNull String path) {
                     try {
@@ -283,8 +283,8 @@
     @SmallTest
     public void testMultiplePathHandlersOnTheSamePath() throws Throwable {
         WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
-                .register("/test_path/", new FakeZipPathHandler())
-                .register("/test_path/", new FakeTextPathHandler())
+                .addPathHandler("/test_path/", new FakeZipPathHandler())
+                .addPathHandler("/test_path/", new FakeTextPathHandler())
                 .build();
 
         WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -301,8 +301,8 @@
 
         // Register in reverse order to make sure it works regardless of order.
         assetLoader = new WebViewAssetLoader.Builder()
-                .register("/test_path/", new FakeTextPathHandler())
-                .register("/test_path/", new FakeZipPathHandler())
+                .addPathHandler("/test_path/", new FakeTextPathHandler())
+                .addPathHandler("/test_path/", new FakeZipPathHandler())
                 .build();
 
         response = assetLoader.shouldInterceptRequest(
diff --git a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
index ccfe8da..fa064fc 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
@@ -56,8 +56,8 @@
  * A typical usage would be like:
  * <pre class="prettyprint">
  *     final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- *              .register("/assets/", new AssetsPathHandler(this))
- *              .register("/res/", new ResourcesPathHandler(this))
+ *              .addPathHandler("/assets/", new AssetsPathHandler(this))
+ *              .addPathHandler("/res/", new ResourcesPathHandler(this))
  *              .build();
  *
  *     webView.setWebViewClient(new WebViewClient() {
@@ -304,12 +304,12 @@
      * A builder class for constructing {@link WebViewAssetLoader} objects.
      */
     public static final class Builder {
-        private boolean mAllowHttp;
+        private boolean mHttpAllowed;
         private String mDomain;
         @NonNull private List<PathMatcher> mBuilderMatcherList;
 
         public Builder() {
-            mAllowHttp = false;
+            mHttpAllowed = false;
             mDomain = DEFAULT_DOMAIN;
             mBuilderMatcherList = new ArrayList<>();
         }
@@ -322,7 +322,7 @@
          * @return {@link Builder} object.
          */
         @NonNull
-        public Builder onDomain(@NonNull String domain) {
+        public Builder setDomain(@NonNull String domain) {
             mDomain = domain;
             return this;
         }
@@ -334,8 +334,8 @@
          * @return {@link Builder} object.
          */
         @NonNull
-        public Builder allowHttp(boolean allowHttp) {
-            mAllowHttp = allowHttp;
+        public Builder setHttpAllowed(boolean httpAllowed) {
+            mHttpAllowed = httpAllowed;
             return this;
         }
 
@@ -351,8 +351,8 @@
          * @throws IllegalArgumentException if the path is invalid.
          */
         @NonNull
-        public Builder register(@NonNull String path, @NonNull PathHandler handler) {
-            mBuilderMatcherList.add(new PathMatcher(mDomain, path, mAllowHttp, handler));
+        public Builder addPathHandler(@NonNull String path, @NonNull PathHandler handler) {
+            mBuilderMatcherList.add(new PathMatcher(mDomain, path, mHttpAllowed, handler));
             return this;
         }
 
diff --git a/work/workmanager/api/2.3.0-alpha01.txt b/work/workmanager/api/2.3.0-alpha01.txt
index f0bff1a..8d3b024 100644
--- a/work/workmanager/api/2.3.0-alpha01.txt
+++ b/work/workmanager/api/2.3.0-alpha01.txt
@@ -13,6 +13,7 @@
 
   public final class Configuration {
     method public java.util.concurrent.Executor getExecutor();
+    method public androidx.work.InputMergerFactory getInputMergerFactory();
     method public int getMaxJobSchedulerId();
     method public int getMinJobSchedulerId();
     method public java.util.concurrent.Executor getTaskExecutor();
@@ -24,6 +25,7 @@
     ctor public Configuration.Builder();
     method public androidx.work.Configuration build();
     method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
     method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
     method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
     method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
@@ -124,6 +126,11 @@
     method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
   }
 
+  public abstract class InputMergerFactory {
+    ctor public InputMergerFactory();
+    method public abstract androidx.work.InputMerger? createInputMerger(String);
+  }
+
   public abstract class ListenableWorker {
     ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
     method public final android.content.Context getApplicationContext();
diff --git a/work/workmanager/api/current.txt b/work/workmanager/api/current.txt
index f0bff1a..8d3b024 100644
--- a/work/workmanager/api/current.txt
+++ b/work/workmanager/api/current.txt
@@ -13,6 +13,7 @@
 
   public final class Configuration {
     method public java.util.concurrent.Executor getExecutor();
+    method public androidx.work.InputMergerFactory getInputMergerFactory();
     method public int getMaxJobSchedulerId();
     method public int getMinJobSchedulerId();
     method public java.util.concurrent.Executor getTaskExecutor();
@@ -24,6 +25,7 @@
     ctor public Configuration.Builder();
     method public androidx.work.Configuration build();
     method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
     method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
     method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
     method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
@@ -124,6 +126,11 @@
     method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
   }
 
+  public abstract class InputMergerFactory {
+    ctor public InputMergerFactory();
+    method public abstract androidx.work.InputMerger? createInputMerger(String);
+  }
+
   public abstract class ListenableWorker {
     ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
     method public final android.content.Context getApplicationContext();
diff --git a/work/workmanager/src/androidTest/java/androidx/work/InputMergerFactoryTest.kt b/work/workmanager/src/androidTest/java/androidx/work/InputMergerFactoryTest.kt
new file mode 100644
index 0000000..8104d5f
--- /dev/null
+++ b/work/workmanager/src/androidTest/java/androidx/work/InputMergerFactoryTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 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.work
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.`is`
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class InputMergerFactoryTest {
+    private lateinit var context: Context
+    private lateinit var factory: InputMergerFactory
+    private lateinit var configuration: Configuration
+
+    @Before
+    fun setUp() {
+        context = ApplicationProvider.getApplicationContext()
+        factory = mock(InputMergerFactory::class.java)
+        configuration = Configuration.Builder()
+            .setInputMergerFactory(factory)
+            .build()
+    }
+
+    @Test
+    fun testInputMergerFactory() {
+        val name = ArrayCreatingInputMerger::class.java.name
+        val merger = configuration.inputMergerFactory.createInputMergerWithDefaultFallback(name)
+        assertThat(merger, notNullValue())
+        verify(factory, times(1)).createInputMerger(name)
+    }
+
+    @Test
+    fun testInputMergerFactory2() {
+        factory = object : InputMergerFactory() {
+            override fun createInputMerger(className: String): InputMerger? {
+                return OverwritingInputMerger()
+            }
+        }
+        configuration = Configuration.Builder()
+            .setInputMergerFactory(factory)
+            .build()
+
+        val name = ArrayCreatingInputMerger::class.java.name
+        val merger = configuration.inputMergerFactory.createInputMergerWithDefaultFallback(name)
+        val instanceCheck = merger is OverwritingInputMerger
+        assertThat(merger, notNullValue())
+        assertThat(instanceCheck, `is`(true))
+    }
+}
diff --git a/work/workmanager/src/main/java/androidx/work/Configuration.java b/work/workmanager/src/main/java/androidx/work/Configuration.java
index 9246dcce..c24f643 100644
--- a/work/workmanager/src/main/java/androidx/work/Configuration.java
+++ b/work/workmanager/src/main/java/androidx/work/Configuration.java
@@ -51,6 +51,7 @@
     private final @NonNull Executor mExecutor;
     private final @NonNull Executor mTaskExecutor;
     private final @NonNull WorkerFactory mWorkerFactory;
+    private final @NonNull InputMergerFactory mInputMergerFactory;
     private final int mLoggingLevel;
     private final int mMinJobSchedulerId;
     private final int mMaxJobSchedulerId;
@@ -78,6 +79,12 @@
             mWorkerFactory = builder.mWorkerFactory;
         }
 
+        if (builder.mInputMergerFactory == null) {
+            mInputMergerFactory = InputMergerFactory.getDefaultInputMergerFactory();
+        } else {
+            mInputMergerFactory = builder.mInputMergerFactory;
+        }
+
         mLoggingLevel = builder.mLoggingLevel;
         mMinJobSchedulerId = builder.mMinJobSchedulerId;
         mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
@@ -115,6 +122,14 @@
     }
 
     /**
+     * @return The {@link InputMergerFactory} used by {@link WorkManager} to create instances of
+     * {@link InputMerger}s.
+     */
+    public @NonNull InputMergerFactory getInputMergerFactory() {
+        return mInputMergerFactory;
+    }
+
+    /**
      * Gets the minimum logging level for {@link WorkManager}.
      *
      * @return The minimum logging level, corresponding to the constants found in
@@ -186,6 +201,7 @@
 
         Executor mExecutor;
         WorkerFactory mWorkerFactory;
+        InputMergerFactory mInputMergerFactory;
         Executor mTaskExecutor;
 
         int mLoggingLevel = Log.INFO;
@@ -205,6 +221,17 @@
         }
 
         /**
+         * Specifies a custom {@link InputMergerFactory} for WorkManager.
+         * @param inputMergerFactory A {@link InputMergerFactory} for creating {@link InputMerger}s
+         * @return This {@link Builder} instance
+         */
+        @NonNull
+        public Builder setInputMergerFactory(@NonNull InputMergerFactory inputMergerFactory) {
+            mInputMergerFactory = inputMergerFactory;
+            return this;
+        }
+
+        /**
          * Specifies a custom {@link Executor} for WorkManager.
          *
          * @param executor An {@link Executor} for running {@link Worker}s
diff --git a/work/workmanager/src/main/java/androidx/work/InputMergerFactory.java b/work/workmanager/src/main/java/androidx/work/InputMergerFactory.java
new file mode 100644
index 0000000..ac30624
--- /dev/null
+++ b/work/workmanager/src/main/java/androidx/work/InputMergerFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 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.work;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+/**
+ * A factory object that creates {@link InputMerger} instances. The factory is invoked every
+ * time a work runs. You can override the default implementation of this factory by manually
+ * initializing {@link WorkManager} (see {@link WorkManager#initialize(Context, Configuration)}
+ * and specifying a new {@link InputMergerFactory} in
+ * {@link Configuration.Builder#setInputMergerFactory(InputMergerFactory)}.
+ */
+public abstract class InputMergerFactory {
+    /**
+     * Override this method to create an instance of a {@link InputMerger} given its fully
+     * qualified class name.
+     * <p></p>
+     * Throwing an {@link Exception} here will crash the application. If an
+     * {@link InputMergerFactory} is unable to create an instance of a {@link InputMerger}, it
+     * should return {@code null} so it can delegate to the default {@link InputMergerFactory}.
+     *
+     * @param className The fully qualified class name for the {@link InputMerger}
+     * @return an instance of {@link InputMerger}
+     */
+    @Nullable
+    public abstract InputMerger createInputMerger(@NonNull String className);
+
+    /**
+     * Creates an instance of a {@link InputMerger} given its fully
+     * qualified class name with the correct fallback behavior.
+     *
+     * @param className The fully qualified class name for the {@link InputMerger}
+     * @return an instance of {@link InputMerger}
+     *
+     * @hide
+     */
+    @Nullable
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public final InputMerger createInputMergerWithDefaultFallback(@NonNull String className) {
+        InputMerger inputMerger = createInputMerger(className);
+        if (inputMerger == null) {
+            inputMerger = InputMerger.fromClassName(className);
+        }
+        return inputMerger;
+    }
+
+    /**
+     * @return A default {@link InputMergerFactory} with no custom behavior.
+     *
+     * @hide
+     */
+    @NonNull
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static InputMergerFactory getDefaultInputMergerFactory() {
+        return new InputMergerFactory() {
+            @Nullable
+            @Override
+            public InputMerger createInputMerger(@NonNull String className) {
+                return null;
+            }
+        };
+    }
+}
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java b/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
index efa3b4c..de5b1c5 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
@@ -35,6 +35,7 @@
 import androidx.work.Configuration;
 import androidx.work.Data;
 import androidx.work.InputMerger;
+import androidx.work.InputMergerFactory;
 import androidx.work.ListenableWorker;
 import androidx.work.Logger;
 import androidx.work.WorkInfo;
@@ -199,7 +200,10 @@
         if (mWorkSpec.isPeriodic()) {
             input = mWorkSpec.input;
         } else {
-            InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);
+            InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
+            String inputMergerClassName = mWorkSpec.inputMergerClassName;
+            InputMerger inputMerger =
+                    inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
             if (inputMerger == null) {
                 Logger.get().error(TAG, String.format("Could not create Input Merger %s",
                         mWorkSpec.inputMergerClassName));