Merge "Add PagedList#getConfig" into oc-mr1-support-27.0-dev
diff --git a/compat/src/main/java/android/support/v4/content/res/ResourcesCompat.java b/compat/src/main/java/android/support/v4/content/res/ResourcesCompat.java
index 4c70ce9..15b8ce9 100644
--- a/compat/src/main/java/android/support/v4/content/res/ResourcesCompat.java
+++ b/compat/src/main/java/android/support/v4/content/res/ResourcesCompat.java
@@ -307,11 +307,11 @@
      */
     @RestrictTo(LIBRARY_GROUP)
     public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,
-            int style) throws NotFoundException {
+            int style, @Nullable FontCallback fontCallback) throws NotFoundException {
         if (context.isRestricted()) {
             return null;
         }
-        return loadFont(context, id, value, style, null /* callback */, null /* handler */,
+        return loadFont(context, id, value, style, fontCallback, null /* handler */,
                 true /* isXmlRequest */);
     }
 
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt
index 10180a6..2f300e7 100644
--- a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/ErrorMessages.kt
@@ -16,6 +16,9 @@
 
 package android.arch.lifecycle
 
+import android.arch.lifecycle.model.EventMethod
+import javax.lang.model.element.TypeElement
+
 object ErrorMessages {
     const val TOO_MANY_ARGS = "callback method cannot have more than 2 parameters"
     const val TOO_MANY_ARGS_NOT_ON_ANY = "only callback annotated with ON_ANY " +
@@ -33,4 +36,10 @@
     const val INVALID_ENCLOSING_ELEMENT =
             "Parent of OnLifecycleEvent should be a class or interface"
     const val INVALID_ANNOTATED_ELEMENT = "OnLifecycleEvent can only be added to methods"
+
+    fun failedToGenerateAdapter(type: TypeElement, failureReason: EventMethod) =
+            """
+             Failed to generate an Adapter for $type, because it needs to be able to access to
+             package private method ${failureReason.method.name()} from ${failureReason.type}
+            """.trim()
 }
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt
index 43368936..e31b0e9 100644
--- a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/input_collector.kt
@@ -39,17 +39,22 @@
                           roundEnv: RoundEnvironment): Map<TypeElement, LifecycleObserverInfo> {
     val validator = Validator(processingEnv)
     val worldCollector = ObserversCollector(processingEnv)
-    roundEnv.getElementsAnnotatedWith(OnLifecycleEvent::class.java).forEach { elem ->
+    val roots = roundEnv.getElementsAnnotatedWith(OnLifecycleEvent::class.java).map { elem ->
         if (elem.kind != ElementKind.METHOD) {
             validator.printErrorMessage(ErrorMessages.INVALID_ANNOTATED_ELEMENT, elem)
+            null
         } else {
             val enclosingElement = elem.enclosingElement
             if (validator.validateClass(enclosingElement)) {
-                worldCollector.collect(MoreElements.asType(enclosingElement))
+                MoreElements.asType(enclosingElement)
+            } else {
+                null
             }
         }
-    }
-    return worldCollector.observers
+    }.filterNotNull().toSet()
+    roots.forEach { worldCollector.collect(it) }
+    // filter out everything that arrived from jars
+    return worldCollector.observers.filterKeys { k -> k in roots }
 }
 
 class ObserversCollector(processingEnv: ProcessingEnvironment) {
diff --git a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt
index 16c3d81..efe1ed7 100644
--- a/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt
+++ b/lifecycle/compiler/src/main/kotlin/android/arch/lifecycle/transformation.kt
@@ -78,18 +78,34 @@
     return flattened.values.toList()
 }
 
+private fun needsSyntheticAccess(type: TypeElement, eventMethod: EventMethod): Boolean {
+    val executable = eventMethod.method
+    return type.getPackageQName() != eventMethod.packageName()
+            && (executable.isPackagePrivate() || executable.isProtected())
+}
+
 fun transformToOutput(processingEnv: ProcessingEnvironment,
                       world: Map<TypeElement, LifecycleObserverInfo>): List<AdapterClass> {
     val flatObservers = flattenObservers(processingEnv, world)
     val syntheticMethods = HashMultimap.create<TypeElement, EventMethodCall>()
-    flatObservers.filterNot(LifecycleObserverInfo::hasAdapter)
     val adapterCalls = flatObservers
-            .filterNot(LifecycleObserverInfo::hasAdapter)
+            // filter out everything that arrived from jars
+            .filter { (type) -> type in world }
+            // filter out if it needs synthetic access and we can't generate adapter for it
+            .filterNot { (type, methods) ->
+                methods.any { eventMethod ->
+                    val cantGenerate = needsSyntheticAccess(type, eventMethod)
+                            && (eventMethod.type !in world)
+                    if (cantGenerate) {
+                        processingEnv.messager.printMessage(Diagnostic.Kind.WARNING,
+                                ErrorMessages.failedToGenerateAdapter(type, eventMethod))
+                    }
+                    cantGenerate
+                }
+            }
             .map { (type, methods) ->
                 val calls = methods.map { eventMethod ->
-                    val executable = eventMethod.method
-                    if (type.getPackageQName() != eventMethod.packageName()
-                            && (executable.isPackagePrivate() || executable.isProtected())) {
+                    if (needsSyntheticAccess(type, eventMethod)) {
                         EventMethodCall(eventMethod, eventMethod.type)
                     } else {
                         EventMethodCall(eventMethod)
@@ -101,9 +117,10 @@
                 type to calls
             }.toMap()
 
-    return adapterCalls.map { (type, calls) ->
-        val methods = syntheticMethods.get(type) ?: setOf()
-        val synthetic = methods.map { eventMethod -> eventMethod!!.method.method }.toSet()
-        AdapterClass(type, calls, synthetic)
-    }
+    return adapterCalls
+            .map { (type, calls) ->
+                val methods = syntheticMethods.get(type) ?: emptySet()
+                val synthetic = methods.map { eventMethod -> eventMethod!!.method.method }.toSet()
+                AdapterClass(type, calls, synthetic)
+            }
 }
diff --git a/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt
index a4fda54..83d69e0 100644
--- a/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt
+++ b/lifecycle/compiler/src/tests/kotlin/android/arch/lifecycle/ValidCasesTest.kt
@@ -23,9 +23,10 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import javax.tools.StandardLocation
 import java.io.File
+import java.lang.Exception
 import java.net.URLClassLoader
+import javax.tools.StandardLocation
 
 @RunWith(JUnit4::class)
 class ValidCasesTest {
@@ -72,7 +73,7 @@
     }
 
     @Test
-    fun testInterface1(){
+    fun testInterface1() {
         processClass("foo.InterfaceOk1").compilesWithoutError()
     }
 
@@ -118,13 +119,37 @@
 
     @Test
     fun testJar() {
-        val jarUrl = File("src/tests/test-data/lib/test-library.jar").toURI().toURL()
-        val classLoader = URLClassLoader(arrayOf(jarUrl), this.javaClass.classLoader)
         JavaSourcesSubject.assertThat(load("foo.DerivedFromJar", ""))
-                .withClasspathFrom(classLoader)
+                .withClasspathFrom(libraryClassLoader())
                 .processedWith(LifecycleProcessor())
                 .compilesWithoutError().and()
-                .generatesSources(load("foo.DerivedFromJar_LifecycleAdapter", "expected")
-        )
+                .generatesSources(load("foo.DerivedFromJar_LifecycleAdapter", "expected"))
+    }
+
+    @Test
+    fun testExtendFromJarFailToGenerateAdapter() {
+        val compileTester = JavaSourcesSubject.assertThat(load("foo.DerivedFromJar1", ""))
+                .withClasspathFrom(libraryClassLoader())
+                .processedWith(LifecycleProcessor())
+                .compilesWithoutError()
+        compileTester.withWarningContaining("Failed to generate an Adapter for")
+        doesntGenerateClass(compileTester, "test.library", "ObserverNoAdapter_LifecycleAdapter")
+        doesntGenerateClass(compileTester, "foo", "DerivedFromJar1_LifecycleAdapter")
+    }
+
+    // compile-testing has fancy, but not always convenient API
+    private fun doesntGenerateClass(compile: CompileTester.SuccessfulCompilationClause,
+                                    packageName: String, className: String) {
+        try {
+            compile.and().generatesFileNamed(StandardLocation.CLASS_OUTPUT,
+                    packageName, "$className.class")
+            throw Exception("$packageName.$className shouldn't be generated")
+        } catch (e: AssertionError) {
+        }
+    }
+
+    private fun libraryClassLoader(): URLClassLoader {
+        val jarUrl = File("src/tests/test-data/lib/test-library.jar").toURI().toURL()
+        return URLClassLoader(arrayOf(jarUrl), this.javaClass.classLoader)
     }
 }
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateListener.java b/lifecycle/compiler/src/tests/test-data/DerivedFromJar1.java
similarity index 61%
rename from lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateListener.java
rename to lifecycle/compiler/src/tests/test-data/DerivedFromJar1.java
index 3acc622..efcfaff 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateListener.java
+++ b/lifecycle/compiler/src/tests/test-data/DerivedFromJar1.java
@@ -14,16 +14,15 @@
  * limitations under the License.
  */
 
-package android.arch.lifecycle.testapp;
+package foo;
 
-/**
- * Observer that can be set on {@link LifecycleObservableActivity} or
- * {@link LifecycleObservableFragment}  so that tests can be notified when certain events occur.
- */
-public interface OnSaveInstanceStateListener {
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
 
-    /**
-     * Called in onSaveInstanceState() after super.onSaveInstanceState().
-     */
-    void onSaveInstanceState();
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class DerivedFromJar1 extends test.library.PPObserverNoAdapter {
+    @OnLifecycleEvent(ON_START)
+    public void doAnother() {
+    }
 }
diff --git a/lifecycle/compiler/src/tests/test-data/expected/DerivedFromJar1_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/DerivedFromJar1_LifecycleAdapter.java
new file mode 100644
index 0000000..2ff2db2
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/DerivedFromJar1_LifecycleAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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 foo;
+
+import android.arch.lifecycle.GeneratedAdapter;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.MethodCallsLogger;
+import java.lang.Override;
+import javax.annotation.Generated;
+
+@Generated("android.arch.lifecycle.LifecycleProcessor")
+public class DerivedFromJar1_LifecycleAdapter implements GeneratedAdapter {
+    final DerivedFromJar1 mReceiver;
+
+    DerivedFromJar_LifecycleAdapter(DerivedFromJar1 receiver) {
+        this.mReceiver = receiver;
+    }
+
+    @Override
+    public void callMethods(LifecycleOwner owner, Lifecycle.Event event, boolean onAny,
+            MethodCallsLogger logger) {
+        boolean hasLogger = logger != null;
+        if (onAny) {
+            return;
+        }
+        if (event == Lifecycle.Event.ON_STOP) {
+            if (!hasLogger || logger.approveCall("doOnStop", 1)) {
+                mReceiver.doOnStop();
+            }
+            return;
+        }
+        if (event == Lifecycle.Event.ON_START) {
+            if (!hasLogger || logger.approveCall("doAnother", 1)) {
+                mReceiver.doAnother();
+            }
+            return;
+        }
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/expected/ObserverNoAdapter_LifecycleAdapter.java b/lifecycle/compiler/src/tests/test-data/expected/ObserverNoAdapter_LifecycleAdapter.java
new file mode 100644
index 0000000..c2dc1b6
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/expected/ObserverNoAdapter_LifecycleAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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 test.library;
+
+import android.arch.lifecycle.GeneratedAdapter;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.MethodCallsLogger;
+import java.lang.Override;
+import javax.annotation.Generated;
+
+@Generated("android.arch.lifecycle.LifecycleProcessor")
+public class ObserverNoAdapter_LifecycleAdapter implements GeneratedAdapter {
+    final ObserverNoAdapter mReceiver;
+
+    ObserverNoAdapter_LifecycleAdapter(ObserverNoAdapter receiver) {
+        this.mReceiver = receiver;
+    }
+
+    @Override
+    public void callMethods(LifecycleOwner owner, Lifecycle.Event event, boolean onAny,
+            MethodCallsLogger logger) {
+        boolean hasLogger = logger != null;
+        if (onAny) {
+            return;
+        }
+        if (event == Lifecycle.Event.ON_STOP) {
+            if (!hasLogger || logger.approveCall("doOnStop", 1)) {
+                mReceiver.doOnStop();
+            }
+            return;
+        }
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateListener.java b/lifecycle/compiler/src/tests/test-data/lib/src/test/library/ObserverNoAdapter.java
similarity index 60%
copy from lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateListener.java
copy to lifecycle/compiler/src/tests/test-data/lib/src/test/library/ObserverNoAdapter.java
index 3acc622..ea000f7 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateListener.java
+++ b/lifecycle/compiler/src/tests/test-data/lib/src/test/library/ObserverNoAdapter.java
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-package android.arch.lifecycle.testapp;
+package test.library;
 
-/**
- * Observer that can be set on {@link LifecycleObservableActivity} or
- * {@link LifecycleObservableFragment}  so that tests can be notified when certain events occur.
- */
-public interface OnSaveInstanceStateListener {
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
 
-    /**
-     * Called in onSaveInstanceState() after super.onSaveInstanceState().
-     */
-    void onSaveInstanceState();
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class ObserverNoAdapter implements LifecycleObserver {
+    @OnLifecycleEvent(ON_STOP)
+    public void doOnStop() {
+    }
 }
diff --git a/lifecycle/compiler/src/tests/test-data/lib/src/test/library/PPObserverNoAdapter.java b/lifecycle/compiler/src/tests/test-data/lib/src/test/library/PPObserverNoAdapter.java
new file mode 100644
index 0000000..06e10b5
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/lib/src/test/library/PPObserverNoAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 test.library;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class PPObserverNoAdapter implements LifecycleObserver {
+    @OnLifecycleEvent(ON_START)
+    protected void doOnStart() {
+    }
+
+    @OnLifecycleEvent(ON_STOP)
+    protected void doOnStop() {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/lib/src/test/library/PPObserverWithAdapter.java b/lifecycle/compiler/src/tests/test-data/lib/src/test/library/PPObserverWithAdapter.java
new file mode 100644
index 0000000..25a9b5d
--- /dev/null
+++ b/lifecycle/compiler/src/tests/test-data/lib/src/test/library/PPObserverWithAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 test.library;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.OnLifecycleEvent;
+
+public class PPObserverWithAdapter implements LifecycleObserver {
+    @OnLifecycleEvent(ON_START)
+    protected void doOnStart() {
+    }
+
+    @OnLifecycleEvent(ON_STOP)
+    protected void doOnStop() {
+    }
+}
diff --git a/lifecycle/compiler/src/tests/test-data/lib/test-library.jar b/lifecycle/compiler/src/tests/test-data/lib/test-library.jar
index f1156ad..71af37f 100644
--- a/lifecycle/compiler/src/tests/test-data/lib/test-library.jar
+++ b/lifecycle/compiler/src/tests/test-data/lib/test-library.jar
Binary files differ
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java
index 4c28567..647d5d7 100644
--- a/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java
+++ b/lifecycle/extensions/src/test/java/android/arch/lifecycle/LiveDataTest.java
@@ -585,30 +585,26 @@
 
         mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
 
-        // Marking state as CREATED should not call onInactive.
+        // Marking state as CREATED should call onInactive.
         reset(mActiveObserversChanged);
         mRegistry.markState(Lifecycle.State.CREATED);
-        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+        verify(mActiveObserversChanged).onCall(false);
+        reset(mActiveObserversChanged);
 
         // Setting a new value should trigger LiveData to realize the Lifecycle it is observing
         // is in a state where the LiveData should be inactive, so the LiveData will call onInactive
         // and the Observer shouldn't be affected.
         mLiveData.setValue("1");
-        verify(mActiveObserversChanged).onCall(false);
-        verify(observer, never()).onChanged(anyString());
 
-        // Sanity check.  Because we've only marked the state as CREATED, the LifecycleRegistry
-        // was internally never moved away from the StartedState, and thus sending the ON_START
-        // event again will have no affect on the LiveData.
-        reset(mActiveObserversChanged);
-        reset(observer);
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        // state is already CREATED so should not call again
         verify(mActiveObserversChanged, never()).onCall(anyBoolean());
         verify(observer, never()).onChanged(anyString());
 
-        // If the lifecycle moves back to a started/resumed state, the LiveData should again be made
-        // active, and therefore the observer should have been called with the new data.
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+        // Sanity check.  Because we've only marked the state as CREATED, sending ON_START
+        // should re-dispatch events.
+        reset(mActiveObserversChanged);
+        reset(observer);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
         verify(mActiveObserversChanged).onCall(true);
         verify(observer).onChanged("1");
     }
@@ -645,19 +641,19 @@
         verify(observer1, never()).onChanged(anyString());
         verify(observer2).onChanged("1");
 
-        // Now we set the other Lifecycle to be inactive, but the LiveData will still be active
-        // until it's value is updated.
+        // Now we set the other Lifecycle to be inactive, live data should become inactive.
         reset(observer1);
         reset(observer2);
         mRegistry2.markState(Lifecycle.State.CREATED);
-        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
+        verify(mActiveObserversChanged).onCall(false);
         verify(observer1, never()).onChanged(anyString());
         verify(observer2, never()).onChanged(anyString());
 
-        // Now we post another value, because both lifecycles are in the Created state, the LiveData
-        // will be made inactive, and neither of the observers will be called.
+        // Now we post another value, because both lifecycles are in the Created state, live data
+        // will not dispatch any values
+        reset(mActiveObserversChanged);
         mLiveData.setValue("2");
-        verify(mActiveObserversChanged).onCall(false);
+        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
         verify(observer1, never()).onChanged(anyString());
         verify(observer2, never()).onChanged(anyString());
 
@@ -665,6 +661,7 @@
         // be made active and it's associated Observer will be called with the new value, but the
         // Observer associated with the Lifecycle that is still in the Created state won't be
         // called.
+        reset(mActiveObserversChanged);
         mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
         verify(mActiveObserversChanged).onCall(true);
         verify(observer1).onChanged("2");
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java
index ee4e661..78dd015 100644
--- a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ActivityFullLifecycleTest.java
@@ -16,48 +16,43 @@
 
 package android.arch.lifecycle;
 
-import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
-import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
-import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
-import static android.arch.lifecycle.Lifecycle.Event.ON_START;
-import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
-import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
-import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.CREATE;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.DESTROY;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.PAUSE;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.RESUME;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.START;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.STOP;
+import static android.arch.lifecycle.TestUtils.flatMap;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 
 import android.app.Activity;
 import android.arch.lifecycle.Lifecycle.Event;
-import android.arch.lifecycle.testapp.CollectingActivity;
+import android.arch.lifecycle.testapp.CollectingLifecycleOwner;
+import android.arch.lifecycle.testapp.CollectingSupportActivity;
 import android.arch.lifecycle.testapp.FrameworkLifecycleRegistryActivity;
-import android.arch.lifecycle.testapp.FullLifecycleTestActivity;
-import android.arch.lifecycle.testapp.SupportLifecycleRegistryActivity;
 import android.arch.lifecycle.testapp.TestEvent;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
-import android.util.Pair;
+import android.support.v4.util.Pair;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import java.util.ArrayList;
 import java.util.List;
 
 @SmallTest
 @RunWith(Parameterized.class)
 public class ActivityFullLifecycleTest {
     @Rule
-    public ActivityTestRule activityTestRule =
-            new ActivityTestRule<>(FullLifecycleTestActivity.class);
+    public final ActivityTestRule<? extends CollectingLifecycleOwner> activityTestRule;
 
     @Parameterized.Parameters
     public static Class[] params() {
-        return new Class[]{FullLifecycleTestActivity.class,
-                SupportLifecycleRegistryActivity.class,
+        return new Class[]{CollectingSupportActivity.class,
                 FrameworkLifecycleRegistryActivity.class};
     }
 
@@ -68,28 +63,13 @@
 
 
     @Test
-    public void testFullLifecycle() throws InterruptedException {
-        Activity activity = activityTestRule.getActivity();
-        List<Pair<TestEvent, Event>> results = ((CollectingActivity) activity)
-                .waitForCollectedEvents();
+    public void testFullLifecycle() throws Throwable {
+        CollectingLifecycleOwner owner = activityTestRule.getActivity();
+        TestUtils.waitTillResumed(owner, activityTestRule);
+        activityTestRule.finishActivity();
 
-        Event[] expectedEvents =
-                new Event[]{ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY};
-
-        List<Pair<TestEvent, Event>> expected = new ArrayList<>();
-        boolean beforeResume = true;
-        for (Event i : expectedEvents) {
-            if (beforeResume) {
-                expected.add(new Pair<>(ACTIVITY_CALLBACK, i));
-                expected.add(new Pair<>(LIFECYCLE_EVENT, i));
-            } else {
-                expected.add(new Pair<>(LIFECYCLE_EVENT, i));
-                expected.add(new Pair<>(ACTIVITY_CALLBACK, i));
-            }
-            if (i == ON_RESUME) {
-                beforeResume = false;
-            }
-        }
-        assertThat(results, is(expected));
+        TestUtils.waitTillDestroyed(owner, activityTestRule);
+        List<Pair<TestEvent, Event>> results = owner.copyCollectedEvents();
+        assertThat(results, is(flatMap(CREATE, START, RESUME, PAUSE, STOP, DESTROY)));
     }
 }
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
index e1b4a28..836cfff 100644
--- a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
@@ -20,10 +20,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.app.Instrumentation;
-import android.arch.lifecycle.testapp.LifecycleObservableActivity;
-import android.arch.lifecycle.testapp.LifecycleObservableFragment;
+import android.arch.lifecycle.testapp.CollectingSupportActivity;
+import android.arch.lifecycle.testapp.CollectingSupportFragment;
 import android.arch.lifecycle.testapp.NavigationDialogActivity;
-import android.arch.lifecycle.testapp.OnSaveInstanceStateObservable;
 import android.content.Intent;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
@@ -37,84 +36,80 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class LiveDataOnSaveInstanceStateTest {
     @Rule
-    public ActivityTestRule<LifecycleObservableActivity> mActivityTestRule =
-            new ActivityTestRule<>(LifecycleObservableActivity.class);
+    public ActivityTestRule<CollectingSupportActivity> mActivityTestRule =
+            new ActivityTestRule<>(CollectingSupportActivity.class);
 
     @Test
     @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
     public void liveData_partiallyObscuredActivity_maxSdkM() throws Throwable {
-        LifecycleObservableActivity activity = mActivityTestRule.getActivity();
+        CollectingSupportActivity activity = mActivityTestRule.getActivity();
 
-        liveData_partiallyObscuredLifecycleOwner_maxSdkM(activity, activity, activity);
+        liveData_partiallyObscuredLifecycleOwner_maxSdkM(activity);
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
     public void liveData_partiallyObscuredActivityWithFragment_maxSdkM() throws Throwable {
-        LifecycleObservableActivity activity = mActivityTestRule.getActivity();
-        LifecycleObservableFragment fragment = new LifecycleObservableFragment();
+        CollectingSupportActivity activity = mActivityTestRule.getActivity();
+        CollectingSupportFragment fragment = new CollectingSupportFragment();
         mActivityTestRule.runOnUiThread(() -> activity.replaceFragment(fragment));
 
-        liveData_partiallyObscuredLifecycleOwner_maxSdkM(activity, fragment, fragment);
+        liveData_partiallyObscuredLifecycleOwner_maxSdkM(fragment);
     }
 
     @Test
     @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
     public void liveData_partiallyObscuredActivityFragmentInFragment_maxSdkM() throws Throwable {
-        LifecycleObservableActivity activity = mActivityTestRule.getActivity();
-        LifecycleObservableFragment fragment = new LifecycleObservableFragment();
-        LifecycleObservableFragment fragment2 = new LifecycleObservableFragment();
+        CollectingSupportActivity activity = mActivityTestRule.getActivity();
+        CollectingSupportFragment fragment = new CollectingSupportFragment();
+        CollectingSupportFragment fragment2 = new CollectingSupportFragment();
         mActivityTestRule.runOnUiThread(() -> {
             activity.replaceFragment(fragment);
             fragment.replaceFragment(fragment2);
         });
 
-        liveData_partiallyObscuredLifecycleOwner_maxSdkM(activity, fragment2, fragment2);
+        liveData_partiallyObscuredLifecycleOwner_maxSdkM(fragment2);
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
     public void liveData_partiallyObscuredActivity_minSdkN() throws Throwable {
-        LifecycleObservableActivity activity = mActivityTestRule.getActivity();
+        CollectingSupportActivity activity = mActivityTestRule.getActivity();
 
-        liveData_partiallyObscuredLifecycleOwner_minSdkN(activity, activity);
+        liveData_partiallyObscuredLifecycleOwner_minSdkN(activity);
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
     public void liveData_partiallyObscuredActivityWithFragment_minSdkN() throws Throwable {
-        LifecycleObservableActivity activity = mActivityTestRule.getActivity();
-        LifecycleObservableFragment fragment = new LifecycleObservableFragment();
+        CollectingSupportActivity activity = mActivityTestRule.getActivity();
+        CollectingSupportFragment fragment = new CollectingSupportFragment();
         mActivityTestRule.runOnUiThread(() -> activity.replaceFragment(fragment));
 
-        liveData_partiallyObscuredLifecycleOwner_minSdkN(activity, fragment);
+        liveData_partiallyObscuredLifecycleOwner_minSdkN(fragment);
     }
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
     public void liveData_partiallyObscuredActivityFragmentInFragment_minSdkN() throws Throwable {
-        LifecycleObservableActivity activity = mActivityTestRule.getActivity();
-        LifecycleObservableFragment fragment = new LifecycleObservableFragment();
-        LifecycleObservableFragment fragment2 = new LifecycleObservableFragment();
+        CollectingSupportActivity activity = mActivityTestRule.getActivity();
+        CollectingSupportFragment fragment = new CollectingSupportFragment();
+        CollectingSupportFragment fragment2 = new CollectingSupportFragment();
         mActivityTestRule.runOnUiThread(() -> {
             activity.replaceFragment(fragment);
             fragment.replaceFragment(fragment2);
         });
 
-        liveData_partiallyObscuredLifecycleOwner_minSdkN(activity, fragment2);
+        liveData_partiallyObscuredLifecycleOwner_minSdkN(fragment2);
     }
 
-    private void liveData_partiallyObscuredLifecycleOwner_maxSdkM(FragmentActivity activity,
-            LifecycleOwner lifecycleOwner,
-            OnSaveInstanceStateObservable onSaveInstanceStateObservable)
+    private void liveData_partiallyObscuredLifecycleOwner_maxSdkM(LifecycleOwner lifecycleOwner)
             throws Throwable {
         final AtomicInteger atomicInteger = new AtomicInteger(0);
         MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
@@ -124,27 +119,9 @@
 
         mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
 
-        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
-                NavigationDialogActivity.class.getCanonicalName(), null, false);
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.addMonitor(monitor);
+        final FragmentActivity dialogActivity = launchDialog();
 
-        // Launch the NavigationDialogActivity, partially obscuring the activity, and wait for the
-        // the onSaveInstanceStateObservable's onSaveInstanceState method to be called.  On API 23
-        // and below, this should be the final 'lifecycle' method called.
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-        onSaveInstanceStateObservable.setOnSaveInstanceStateListener(() -> {
-            countDownLatch.countDown();
-            onSaveInstanceStateObservable.setOnSaveInstanceStateListener(null);
-        });
-        Intent dummyIntent = new Intent(InstrumentationRegistry.getTargetContext(),
-                NavigationDialogActivity.class);
-        activity.startActivity(dummyIntent);
-        countDownLatch.await(1, TimeUnit.SECONDS);
-
-        // Sanity check.
-        assertThat(lifecycleOwner.getLifecycle().getCurrentState(),
-                is(Lifecycle.State.CREATED));
+        TestUtils.waitTillCreated(lifecycleOwner, mActivityTestRule);
 
         // Change the LiveData value and assert that the observer is not called given that the
         // lifecycle is in the CREATED state.
@@ -153,14 +130,12 @@
 
         // Finish the dialog Activity, wait for the main activity to be resumed, and assert that
         // the observer's onChanged method is called.
-        FragmentActivity dialogActivity = (FragmentActivity) monitor.waitForActivity();
         mActivityTestRule.runOnUiThread(dialogActivity::finish);
         TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
         assertThat(atomicInteger.get(), is(1));
     }
 
-    private void liveData_partiallyObscuredLifecycleOwner_minSdkN(FragmentActivity activity,
-            LifecycleOwner lifecycleOwner)
+    private void liveData_partiallyObscuredLifecycleOwner_minSdkN(LifecycleOwner lifecycleOwner)
             throws Throwable {
         final AtomicInteger atomicInteger = new AtomicInteger(0);
         MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
@@ -170,27 +145,33 @@
 
         mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
 
-        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
-                NavigationDialogActivity.class.getCanonicalName(), null, false);
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.addMonitor(monitor);
-
         // Launch the NavigationDialogActivity, partially obscuring the activity, and wait for the
         // lifecycleOwner to hit onPause (or enter the STARTED state).  On API 24 and above, this
         // onPause should be the last lifecycle method called (and the STARTED state should be the
         // final resting state).
-        Intent dummyIntent = new Intent(InstrumentationRegistry.getTargetContext(),
-                NavigationDialogActivity.class);
-        activity.startActivity(dummyIntent);
+        launchDialog();
         TestUtils.waitTillStarted(lifecycleOwner, mActivityTestRule);
 
-        // Sanity check, in a previous version of this test, we were still RESUMED at this point.
-        assertThat(lifecycleOwner.getLifecycle().getCurrentState(),
-                is(Lifecycle.State.STARTED));
-
         // Change the LiveData's value and verify that the observer's onChanged method is called
         // since we are in the STARTED state.
         mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(1));
         assertThat(atomicInteger.get(), is(1));
     }
+
+    private FragmentActivity launchDialog() throws Throwable {
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                NavigationDialogActivity.class.getCanonicalName(), null, false);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.addMonitor(monitor);
+
+        FragmentActivity activity = mActivityTestRule.getActivity();
+        // helps with less flaky API 16 tests
+        Intent intent = new Intent(activity, NavigationDialogActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        activity.startActivity(intent);
+        FragmentActivity fragmentActivity = (FragmentActivity) monitor.waitForActivity();
+        TestUtils.waitTillResumed(fragmentActivity, mActivityTestRule);
+        return fragmentActivity;
+    }
 }
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/PartiallyCoveredActivityTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/PartiallyCoveredActivityTest.java
new file mode 100644
index 0000000..07a9dc5
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/PartiallyCoveredActivityTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2017 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 android.arch.lifecycle;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.CREATE;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.DESTROY;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.PAUSE;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.RESUME;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.START;
+import static android.arch.lifecycle.TestUtils.OrderedTuples.STOP;
+import static android.arch.lifecycle.TestUtils.flatMap;
+import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
+import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+
+import android.app.Instrumentation;
+import android.arch.lifecycle.testapp.CollectingLifecycleOwner;
+import android.arch.lifecycle.testapp.CollectingSupportActivity;
+import android.arch.lifecycle.testapp.CollectingSupportFragment;
+import android.arch.lifecycle.testapp.NavigationDialogActivity;
+import android.arch.lifecycle.testapp.TestEvent;
+import android.content.Intent;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.util.Pair;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Runs tests about the state when an activity is partially covered by another activity. Pre
+ * API 24, framework behavior changes so the test rely on whether state is saved or not and makes
+ * assertions accordingly.
+ */
+@SuppressWarnings("unchecked")
+@RunWith(Parameterized.class)
+@LargeTest
+public class PartiallyCoveredActivityTest {
+    private static final List[] IF_SAVED = new List[]{
+            // when overlaid
+            flatMap(CREATE, START, RESUME, PAUSE,
+                    singletonList(new Pair<>(LIFECYCLE_EVENT, ON_STOP))),
+            // post dialog dismiss
+            asList(new Pair<>(OWNER_CALLBACK, ON_RESUME),
+                    new Pair<>(LIFECYCLE_EVENT, ON_START),
+                    new Pair<>(LIFECYCLE_EVENT, ON_RESUME)),
+            // post finish
+            flatMap(PAUSE, STOP, DESTROY)};
+
+    private static final List[] IF_NOT_SAVED = new List[]{
+            // when overlaid
+            flatMap(CREATE, START, RESUME, PAUSE),
+            // post dialog dismiss
+            flatMap(RESUME),
+            // post finish
+            flatMap(PAUSE, STOP, DESTROY)};
+
+    private static final boolean sShouldSave = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;
+    private static final List<Pair<TestEvent, Lifecycle.Event>>[] EXPECTED =
+            sShouldSave ? IF_SAVED : IF_NOT_SAVED;
+
+    @Rule
+    public ActivityTestRule<CollectingSupportActivity> activityRule =
+            new ActivityTestRule<CollectingSupportActivity>(
+                    CollectingSupportActivity.class) {
+                @Override
+                protected Intent getActivityIntent() {
+                    // helps with less flaky API 16 tests
+                    Intent intent = new Intent(InstrumentationRegistry.getTargetContext(),
+                            CollectingSupportActivity.class);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                    return intent;
+                }
+            };
+    private final boolean mDismissDialog;
+
+    @Parameterized.Parameters(name = "dismissDialog_{0}")
+    public static List<Boolean> dismissDialog() {
+        return asList(true, false);
+    }
+
+    public PartiallyCoveredActivityTest(boolean dismissDialog) {
+        mDismissDialog = dismissDialog;
+    }
+
+    @Test
+    public void coveredWithDialog_activity() throws Throwable {
+        final CollectingSupportActivity activity = activityRule.getActivity();
+        runTest(activity);
+    }
+
+    @Test
+    public void coveredWithDialog_fragment() throws Throwable {
+        CollectingSupportFragment fragment = new CollectingSupportFragment();
+        activityRule.runOnUiThread(() -> activityRule.getActivity().replaceFragment(fragment));
+        runTest(fragment);
+    }
+
+    @Test
+    public void coveredWithDialog_childFragment() throws Throwable {
+        CollectingSupportFragment parentFragment = new CollectingSupportFragment();
+        CollectingSupportFragment childFragment = new CollectingSupportFragment();
+        activityRule.runOnUiThread(() -> {
+            activityRule.getActivity().replaceFragment(parentFragment);
+            parentFragment.replaceFragment(childFragment);
+        });
+        runTest(childFragment);
+    }
+
+    private void runTest(CollectingLifecycleOwner owner) throws Throwable {
+        TestUtils.waitTillResumed(owner, activityRule);
+        FragmentActivity dialog = launchDialog();
+        assertStateSaving();
+        waitForIdle();
+        assertThat(owner.copyCollectedEvents(), is(EXPECTED[0]));
+        List<Pair<TestEvent, Lifecycle.Event>> expected;
+        if (mDismissDialog) {
+            dialog.finish();
+            TestUtils.waitTillResumed(activityRule.getActivity(), activityRule);
+            assertThat(owner.copyCollectedEvents(), is(flatMap(EXPECTED[0], EXPECTED[1])));
+            expected = flatMap(EXPECTED[0], EXPECTED[1], EXPECTED[2]);
+        } else {
+            expected = flatMap(CREATE, START, RESUME, PAUSE, STOP, DESTROY);
+        }
+        CollectingSupportActivity activity = activityRule.getActivity();
+        activityRule.finishActivity();
+        TestUtils.waitTillDestroyed(activity, activityRule);
+        assertThat(owner.copyCollectedEvents(), is(expected));
+    }
+
+    // test sanity
+    private void assertStateSaving() throws ExecutionException, InterruptedException {
+        final CollectingSupportActivity activity = activityRule.getActivity();
+        if (sShouldSave) {
+            // state should be saved. wait for it to be saved
+            assertThat("test sanity",
+                    activity.waitForStateSave(20), is(true));
+            assertThat("test sanity", activity.getSupportFragmentManager()
+                    .isStateSaved(), is(true));
+        } else {
+            // should should not be saved
+            assertThat("test sanity", activity.getSupportFragmentManager()
+                    .isStateSaved(), is(false));
+        }
+    }
+
+    private void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    private FragmentActivity launchDialog() throws Throwable {
+        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+                NavigationDialogActivity.class.getCanonicalName(), null, false);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        instrumentation.addMonitor(monitor);
+
+        FragmentActivity activity = activityRule.getActivity();
+
+        Intent intent = new Intent(activity, NavigationDialogActivity.class);
+        // disabling animations helps with less flaky API 16 tests
+        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        activity.startActivity(intent);
+        FragmentActivity fragmentActivity = (FragmentActivity) monitor.waitForActivity();
+        TestUtils.waitTillResumed(fragmentActivity, activityRule);
+        return fragmentActivity;
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java
index ac35a59..f7f9bbe 100644
--- a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/TestUtils.java
@@ -16,15 +16,33 @@
 
 package android.arch.lifecycle;
 
+import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
+import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.arch.lifecycle.Lifecycle.State.CREATED;
+import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
 import static android.arch.lifecycle.Lifecycle.State.RESUMED;
 import static android.arch.lifecycle.Lifecycle.State.STARTED;
+import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
+import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
+import android.arch.lifecycle.testapp.TestEvent;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
+import android.support.v4.util.Pair;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -62,9 +80,9 @@
         return result;
     }
 
-    static void waitTillResumed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
+    static void waitTillCreated(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
             throws Throwable {
-        waitTillState(owner, activityRule, RESUMED);
+        waitTillState(owner, activityRule, CREATED);
     }
 
     static void waitTillStarted(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
@@ -72,12 +90,23 @@
         waitTillState(owner, activityRule, STARTED);
     }
 
-    private static void waitTillState(final LifecycleOwner owner, ActivityTestRule<?> activityRule,
+    static void waitTillResumed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
+            throws Throwable {
+        waitTillState(owner, activityRule, RESUMED);
+    }
+
+    static void waitTillDestroyed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
+            throws Throwable {
+        waitTillState(owner, activityRule, DESTROYED);
+    }
+
+    static void waitTillState(final LifecycleOwner owner, ActivityTestRule<?> activityRule,
             Lifecycle.State state)
             throws Throwable {
         final CountDownLatch latch = new CountDownLatch(1);
         activityRule.runOnUiThread(() -> {
-            if (owner.getLifecycle().getCurrentState() == state) {
+            Lifecycle.State currentState = owner.getLifecycle().getCurrentState();
+            if (currentState == state) {
                 latch.countDown();
             } else {
                 owner.getLifecycle().addObserver(new LifecycleObserver() {
@@ -91,7 +120,48 @@
                 });
             }
         });
-        latch.await(1, TimeUnit.SECONDS);
+        boolean latchResult = latch.await(1, TimeUnit.MINUTES);
+        assertThat("expected " + state + " never happened. Current state:"
+                        + owner.getLifecycle().getCurrentState(), latchResult, is(true));
+
+        // wait for another loop to ensure all observers are called
+        activityRule.runOnUiThread(() -> {
+            // do nothing
+        });
     }
 
+    @SafeVarargs
+    static <T> List<T> flatMap(List<T>... items) {
+        ArrayList<T> result = new ArrayList<>();
+        for (List<T> item : items) {
+            result.addAll(item);
+        }
+        return result;
+    }
+
+    /**
+     * Event tuples of {@link TestEvent} and {@link Lifecycle.Event}
+     * in the order they should arrive.
+     */
+    @SuppressWarnings("unchecked")
+    static class OrderedTuples {
+        static final List<Pair<TestEvent, Lifecycle.Event>> CREATE =
+                Arrays.asList(new Pair(OWNER_CALLBACK, ON_CREATE),
+                        new Pair(LIFECYCLE_EVENT, ON_CREATE));
+        static final List<Pair<TestEvent, Lifecycle.Event>> START =
+                Arrays.asList(new Pair(OWNER_CALLBACK, ON_START),
+                        new Pair(LIFECYCLE_EVENT, ON_START));
+        static final List<Pair<TestEvent, Lifecycle.Event>> RESUME =
+                Arrays.asList(new Pair(OWNER_CALLBACK, ON_RESUME),
+                        new Pair(LIFECYCLE_EVENT, ON_RESUME));
+        static final List<Pair<TestEvent, Lifecycle.Event>> PAUSE =
+                Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_PAUSE),
+                        new Pair(OWNER_CALLBACK, ON_PAUSE));
+        static final List<Pair<TestEvent, Lifecycle.Event>> STOP =
+                Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_STOP),
+                        new Pair(OWNER_CALLBACK, ON_STOP));
+        static final List<Pair<TestEvent, Lifecycle.Event>> DESTROY =
+                Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_DESTROY),
+                        new Pair(OWNER_CALLBACK, ON_DESTROY));
+    }
 }
diff --git a/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml b/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
index a4508b7..5e1d0a0 100644
--- a/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -15,12 +15,6 @@
 
     <application android:allowBackup="true" android:label="Test App" android:supportsRtl="true"
         tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
-        <activity android:name="android.arch.lifecycle.testapp.LifecycleObservableActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
 
         <activity android:name="android.arch.lifecycle.testapp.LifecycleTestActivity">
             <intent-filter>
@@ -28,13 +22,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name="android.arch.lifecycle.testapp.FullLifecycleTestActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-        <activity android:name="android.arch.lifecycle.testapp.SupportLifecycleRegistryActivity">
+        <activity android:name="android.arch.lifecycle.testapp.CollectingSupportActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java
similarity index 76%
rename from lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingActivity.java
rename to lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java
index 6e243b6..4213cab 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingActivity.java
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java
@@ -17,21 +17,20 @@
 package android.arch.lifecycle.testapp;
 
 import android.arch.lifecycle.Lifecycle;
-import android.util.Pair;
+import android.arch.lifecycle.LifecycleOwner;
+import android.support.v4.util.Pair;
 
 import java.util.List;
 
 /**
  * For activities that collect their events.
  */
-public interface CollectingActivity {
-    long TIMEOUT = 5;
-
+public interface CollectingLifecycleOwner extends LifecycleOwner {
     /**
-     * Return collected events
+     * Return a copy of currently collected events
      *
      * @return The list of collected events.
      * @throws InterruptedException
      */
-    List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents() throws InterruptedException;
+    List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents();
 }
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingSupportActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingSupportActivity.java
new file mode 100644
index 0000000..f38d422
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingSupportActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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 android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
+
+import android.arch.lifecycle.Lifecycle.Event;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.util.Pair;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * LifecycleRegistryOwner that extends FragmentActivity.
+ */
+public class CollectingSupportActivity extends FragmentActivity implements
+        CollectingLifecycleOwner {
+
+    private final List<Pair<TestEvent, Event>> mCollectedEvents = new ArrayList<>();
+    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+    private CountDownLatch mSavedStateLatch = new CountDownLatch(1);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        FrameLayout layout = new FrameLayout(this);
+        layout.setId(R.id.fragment_container);
+        setContentView(layout);
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_CREATE));
+        getLifecycle().addObserver(mTestObserver);
+    }
+
+    /**
+     * replaces the main content fragment w/ the given fragment.
+     */
+    public void replaceFragment(Fragment fragment) {
+        getSupportFragmentManager()
+                .beginTransaction()
+                .add(R.id.fragment_container, fragment)
+                .commitNow();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_START));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_RESUME));
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_DESTROY));
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_STOP));
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_PAUSE));
+        // helps with less flaky API 16 tests.
+        overridePendingTransition(0, 0);
+    }
+
+    @Override
+    public List<Pair<TestEvent, Event>> copyCollectedEvents() {
+        return new ArrayList<>(mCollectedEvents);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mSavedStateLatch.countDown();
+    }
+
+    /**
+     * Waits for onSaveInstanceState to be called.
+     */
+    public boolean waitForStateSave(@SuppressWarnings("SameParameterValue") int seconds)
+            throws InterruptedException {
+        return mSavedStateLatch.await(seconds, TimeUnit.SECONDS);
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingSupportFragment.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingSupportFragment.java
new file mode 100644
index 0000000..9bbbe16
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/CollectingSupportFragment.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 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 android.arch.lifecycle.testapp;
+
+import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
+
+import android.annotation.SuppressLint;
+import android.arch.lifecycle.Lifecycle;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A support fragment that collects all of its events.
+ */
+@SuppressLint("ValidFragment")
+public class CollectingSupportFragment extends Fragment implements CollectingLifecycleOwner {
+    private final List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents =
+            new ArrayList<>();
+    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_CREATE));
+        getLifecycle().addObserver(mTestObserver);
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        //noinspection ConstantConditions
+        FrameLayout layout = new FrameLayout(container.getContext());
+        layout.setId(R.id.child_fragment_container);
+        return layout;
+    }
+
+    /**
+     * Runs a replace fragment transaction with 'fragment' on this Fragment.
+     */
+    public void replaceFragment(Fragment fragment) {
+        getChildFragmentManager()
+                .beginTransaction()
+                .add(R.id.child_fragment_container, fragment)
+                .commitNow();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_START));
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_RESUME));
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_DESTROY));
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_STOP));
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_PAUSE));
+    }
+
+    @Override
+    public List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents() {
+        return new ArrayList<>(mCollectedEvents);
+    }
+}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
index d8f4fb3..cdf577c 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
@@ -16,27 +16,29 @@
 
 package android.arch.lifecycle.testapp;
 
-import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
+import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
 
 import android.app.Activity;
 import android.arch.lifecycle.Lifecycle;
 import android.arch.lifecycle.LifecycleRegistry;
 import android.arch.lifecycle.LifecycleRegistryOwner;
 import android.os.Bundle;
-import android.util.Pair;
+import android.support.annotation.NonNull;
+import android.support.v4.util.Pair;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * LifecycleRegistryOwner that extends framework activity.
  */
+@SuppressWarnings("deprecation")
 public class FrameworkLifecycleRegistryActivity extends Activity implements
-        LifecycleRegistryOwner, CollectingActivity {
+        LifecycleRegistryOwner, CollectingLifecycleOwner {
     private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
 
+    @NonNull
     @Override
     public LifecycleRegistry getLifecycle() {
         return mLifecycleRegistry;
@@ -49,49 +51,43 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_CREATE));
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_CREATE));
         getLifecycle().addObserver(mTestObserver);
     }
 
     @Override
     protected void onStart() {
         super.onStart();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_START));
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_START));
     }
 
     @Override
     protected void onResume() {
         super.onResume();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_RESUME));
-        finish();
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_RESUME));
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_DESTROY));
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_DESTROY));
         mLatch.countDown();
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_STOP));
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_STOP));
     }
 
     @Override
     protected void onPause() {
         super.onPause();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_PAUSE));
+        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_PAUSE));
     }
 
-    /**
-     * awaits for all events and returns them.
-     */
     @Override
-    public List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents()
-            throws InterruptedException {
-        mLatch.await(TIMEOUT, TimeUnit.SECONDS);
-        return mCollectedEvents;
+    public List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents() {
+        return new ArrayList<>(mCollectedEvents);
     }
 }
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
deleted file mode 100644
index 5f33c28..0000000
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/FullLifecycleTestActivity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
-
-import android.arch.lifecycle.Lifecycle;
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-import android.util.Pair;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Activity for testing full lifecycle
- */
-public class FullLifecycleTestActivity extends FragmentActivity implements CollectingActivity {
-
-    private List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents = new ArrayList<>();
-    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
-    private CountDownLatch mLatch = new CountDownLatch(1);
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_CREATE));
-        getLifecycle().addObserver(mTestObserver);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_START));
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_RESUME));
-        finish();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_DESTROY));
-        mLatch.countDown();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_STOP));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Lifecycle.Event.ON_PAUSE));
-    }
-
-    /**
-     * awaits for all events and returns them.
-     */
-    @Override
-    public List<Pair<TestEvent, Lifecycle.Event>> waitForCollectedEvents()
-            throws InterruptedException {
-        mLatch.await(TIMEOUT, TimeUnit.SECONDS);
-        return mCollectedEvents;
-    }
-}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleObservableActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleObservableActivity.java
deleted file mode 100644
index 970b926..0000000
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleObservableActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.arch.lifecycle.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-
-/**
- * Simple test activity
- */
-public class LifecycleObservableActivity
-        extends FragmentActivity
-        implements OnSaveInstanceStateObservable {
-
-    private OnSaveInstanceStateListener mOnSaveInstanceStateListener;
-
-    /**
-     * Runs a replace fragment transaction with 'fragment' on this Activity.
-     */
-    public void replaceFragment(Fragment fragment) {
-        getSupportFragmentManager()
-                .beginTransaction()
-                .add(R.id.activityFrameLayout, fragment)
-                .commitNow();
-    }
-
-    @Override
-    public void setOnSaveInstanceStateListener(
-            OnSaveInstanceStateListener onSaveInstanceStateListener) {
-        mOnSaveInstanceStateListener = onSaveInstanceStateListener;
-    }
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        if (mOnSaveInstanceStateListener != null) {
-            mOnSaveInstanceStateListener.onSaveInstanceState();
-        }
-    }
-}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleObservableFragment.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleObservableFragment.java
deleted file mode 100644
index 04c0604..0000000
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/LifecycleObservableFragment.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.arch.lifecycle.testapp;
-
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Simple test activity
- */
-public class LifecycleObservableFragment
-        extends Fragment
-        implements OnSaveInstanceStateObservable {
-
-    private OnSaveInstanceStateListener mOnSaveInstanceStateListener;
-
-    /**
-     * Runs a replace fragment transaction with 'fragment' on this Fragment.
-     */
-    public void replaceFragment(Fragment fragment) {
-        getChildFragmentManager()
-                .beginTransaction()
-                .add(R.id.fragmentFrameLayout, fragment)
-                .commitNow();
-    }
-
-    @Override
-    public void setOnSaveInstanceStateListener(
-            OnSaveInstanceStateListener onSaveInstanceStateListener) {
-        mOnSaveInstanceStateListener = onSaveInstanceStateListener;
-    }
-
-    @Nullable
-    @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
-        return inflater.inflate(R.layout.fragment, container, false);
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        if (mOnSaveInstanceStateListener != null) {
-            mOnSaveInstanceStateListener.onSaveInstanceState();
-        }
-    }
-}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java
index 0ae9403..7d53528 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NavigationDialogActivity.java
@@ -22,4 +22,10 @@
  *  an activity with Dialog theme.
  */
 public class NavigationDialogActivity extends FragmentActivity {
+    @Override
+    protected void onPause() {
+        super.onPause();
+        // helps with less flaky API 16 tests
+        overridePendingTransition(0, 0);
+    }
 }
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateObservable.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateObservable.java
deleted file mode 100644
index 3eaa2d8..0000000
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/OnSaveInstanceStateObservable.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.arch.lifecycle.testapp;
-
-/**
- * Interface for LifecycleOwners that track their own state internally for test purposes.
- */
-public interface OnSaveInstanceStateObservable {
-
-    /**
-     * Sets a OnSaveInstanceStateListener on the OnSaveInstanceStateObservable.
-     */
-    void setOnSaveInstanceStateListener(OnSaveInstanceStateListener onSaveInstanceStateListener);
-}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java
deleted file mode 100644
index c46c6d3..0000000
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/SupportLifecycleRegistryActivity.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.ACTIVITY_CALLBACK;
-
-import android.arch.lifecycle.Lifecycle.Event;
-import android.arch.lifecycle.LifecycleRegistry;
-import android.arch.lifecycle.LifecycleRegistryOwner;
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-import android.util.Pair;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * LifecycleRegistryOwner that extends FragmentActivity.
- */
-public class SupportLifecycleRegistryActivity extends FragmentActivity implements
-        LifecycleRegistryOwner, CollectingActivity {
-    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
-    @Override
-    public LifecycleRegistry getLifecycle() {
-        return mLifecycleRegistry;
-    }
-
-    private List<Pair<TestEvent, Event>> mCollectedEvents = new ArrayList<>();
-    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
-    private CountDownLatch mLatch = new CountDownLatch(1);
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_CREATE));
-        getLifecycle().addObserver(mTestObserver);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_START));
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_RESUME));
-        finish();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_DESTROY));
-        mLatch.countDown();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_STOP));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mCollectedEvents.add(new Pair<>(ACTIVITY_CALLBACK, Event.ON_PAUSE));
-    }
-
-    /**
-     * awaits for all events and returns them.
-     */
-    @Override
-    public List<Pair<TestEvent, Event>> waitForCollectedEvents() throws InterruptedException {
-        mLatch.await(TIMEOUT, TimeUnit.SECONDS);
-        return mCollectedEvents;
-    }
-}
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java
index 0929f84..788045a 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestEvent.java
@@ -17,6 +17,6 @@
 package android.arch.lifecycle.testapp;
 
 public enum TestEvent {
-    ACTIVITY_CALLBACK,
-    LIFECYCLE_EVENT
+    OWNER_CALLBACK,
+    LIFECYCLE_EVENT,
 }
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java
index c611239..00b8e16 100644
--- a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/TestObserver.java
@@ -28,7 +28,7 @@
 import android.arch.lifecycle.LifecycleObserver;
 import android.arch.lifecycle.LifecycleOwner;
 import android.arch.lifecycle.OnLifecycleEvent;
-import android.util.Pair;
+import android.support.v4.util.Pair;
 
 import java.util.List;
 
diff --git a/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml b/lifecycle/integration-tests/testapp/src/main/res/values/values.xml
similarity index 76%
rename from lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml
rename to lifecycle/integration-tests/testapp/src/main/res/values/values.xml
index 64ddf2e..5041a76 100644
--- a/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml
+++ b/lifecycle/integration-tests/testapp/src/main/res/values/values.xml
@@ -14,8 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/activityFrameLayout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
+
+<resources>
+    <item name="child_fragment_container" type="id"/>
+</resources>
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
index 83609ac..bf8aff7 100644
--- a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistry.java
@@ -29,6 +29,7 @@
 import static android.arch.lifecycle.Lifecycle.State.STARTED;
 
 import android.arch.core.internal.FastSafeIterableMap;
+import android.support.annotation.MainThread;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.util.Log;
@@ -99,14 +100,14 @@
     }
 
     /**
-     * Only marks the current state as the given value. It doesn't dispatch any event to its
-     * listeners.
+     * Moves the Lifecycle to the given state and dispatches necessary events to the observers.
      *
      * @param state new state
      */
     @SuppressWarnings("WeakerAccess")
+    @MainThread
     public void markState(@NonNull State state) {
-        mState = state;
+        moveToState(state);
     }
 
     /**
@@ -118,7 +119,15 @@
      * @param event The event that was received
      */
     public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
-        mState = getStateAfter(event);
+        State next = getStateAfter(event);
+        moveToState(next);
+    }
+
+    private void moveToState(State next) {
+        if (mState == next) {
+            return;
+        }
+        mState = next;
         if (mHandlingEvent || mAddingObserverCounter != 0) {
             mNewEventOccurred = true;
             // we will figure out what to do on upper level.
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java
index 38eeb6d..0c67fef 100644
--- a/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/LifecycleRegistryOwner.java
@@ -16,6 +16,8 @@
 
 package android.arch.lifecycle;
 
+import android.support.annotation.NonNull;
+
 /**
  * @deprecated Use {@code android.support.v7.app.AppCompatActivity}
  * which extends {@link LifecycleOwner}, so there are no use cases for this class.
@@ -23,6 +25,7 @@
 @SuppressWarnings({"WeakerAccess", "unused"})
 @Deprecated
 public interface LifecycleRegistryOwner extends LifecycleOwner {
+    @NonNull
     @Override
     LifecycleRegistry getLifecycle();
 }
diff --git a/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java b/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java
index 3e4ece8..16a89ce 100644
--- a/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java
+++ b/lifecycle/runtime/src/main/java/android/arch/lifecycle/ReportFragment.java
@@ -28,7 +28,6 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class ReportFragment extends Fragment {
-
     private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
             + ".LifecycleDispatcher.report_fragment_tag";
 
diff --git a/paging/runtime/src/androidTest/java/android/arch/paging/PagedListAdapterHelperTest.java b/paging/runtime/src/androidTest/java/android/arch/paging/PagedListAdapterHelperTest.java
index 3518540..963d047 100644
--- a/paging/runtime/src/androidTest/java/android/arch/paging/PagedListAdapterHelperTest.java
+++ b/paging/runtime/src/androidTest/java/android/arch/paging/PagedListAdapterHelperTest.java
@@ -21,6 +21,7 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -289,6 +290,64 @@
         assertFalse(helper.getCurrentList().isImmutable());
     }
 
+    @Test
+    public void itemCountUpdatedBeforeListUpdateCallbacks() {
+        // verify that itemCount is updated in the helper before dispatching ListUpdateCallbacks
+
+        final int[] expectedCount = new int[] { 0 };
+        // provides access to helper, which must be constructed after callback
+        final PagedListAdapterHelper[] helperAccessor = new PagedListAdapterHelper[] { null };
+
+        ListUpdateCallback callback = new ListUpdateCallback() {
+            @Override
+            public void onInserted(int position, int count) {
+                assertEquals(expectedCount[0], helperAccessor[0].getItemCount());
+            }
+
+            @Override
+            public void onRemoved(int position, int count) {
+                assertEquals(expectedCount[0], helperAccessor[0].getItemCount());
+            }
+
+            @Override
+            public void onMoved(int fromPosition, int toPosition) {
+                fail("not expected");
+            }
+
+            @Override
+            public void onChanged(int position, int count, Object payload) {
+                fail("not expected");
+            }
+        };
+
+        PagedListAdapterHelper<String> helper = createHelper(callback, STRING_DIFF_CALLBACK);
+        helperAccessor[0] = helper;
+
+        PagedList.Config config = new PagedList.Config.Builder()
+                .setPageSize(20)
+                .build();
+
+
+        // in the fast-add case...
+        expectedCount[0] = 5;
+        assertEquals(0, helper.getItemCount());
+        helper.setList(createPagedListFromListAndPos(config, ALPHABET_LIST.subList(0, 5), 0));
+        assertEquals(5, helper.getItemCount());
+
+        // in the slow, diff on BG thread case...
+        expectedCount[0] = 10;
+        assertEquals(5, helper.getItemCount());
+        helper.setList(createPagedListFromListAndPos(config, ALPHABET_LIST.subList(0, 10), 0));
+        drain();
+        assertEquals(10, helper.getItemCount());
+
+        // and in the fast-remove case
+        expectedCount[0] = 0;
+        assertEquals(10, helper.getItemCount());
+        helper.setList(null);
+        assertEquals(0, helper.getItemCount());
+    }
+
     private void drainExceptDiffThread() {
         boolean executed;
         do {
diff --git a/paging/runtime/src/main/java/android/arch/paging/PagedListAdapterHelper.java b/paging/runtime/src/main/java/android/arch/paging/PagedListAdapterHelper.java
index 0007a2e..abcff41 100644
--- a/paging/runtime/src/main/java/android/arch/paging/PagedListAdapterHelper.java
+++ b/paging/runtime/src/main/java/android/arch/paging/PagedListAdapterHelper.java
@@ -118,6 +118,8 @@
  * @param <T> Type of the PagedLists this helper will receive.
  */
 public class PagedListAdapterHelper<T> {
+    // updateCallback notifications must only be notified *after* new data and item count are stored
+    // this ensures Adapter#notifyItemRangeInserted etc are accessing the new data
     private final ListUpdateCallback mUpdateCallback;
     private final ListAdapterConfig<T> mConfig;
 
@@ -236,21 +238,25 @@
         final int runGeneration = ++mMaxScheduledGeneration;
 
         if (pagedList == null) {
-            mUpdateCallback.onRemoved(0, getItemCount());
+            int removedCount = getItemCount();
             if (mPagedList != null) {
                 mPagedList.removeWeakCallback(mPagedListCallback);
                 mPagedList = null;
             } else if (mSnapshot != null) {
                 mSnapshot = null;
             }
+            // dispatch update callback after updating mPagedList/mSnapshot
+            mUpdateCallback.onRemoved(0, removedCount);
             return;
         }
 
         if (mPagedList == null && mSnapshot == null) {
             // fast simple first insert
-            mUpdateCallback.onInserted(0, pagedList.size());
             mPagedList = pagedList;
             pagedList.addWeakCallback(null, mPagedListCallback);
+
+            // dispatch update callback after updating mPagedList/mSnapshot
+            mUpdateCallback.onInserted(0, pagedList.size());
             return;
         }
 
@@ -296,10 +302,14 @@
             throw new IllegalStateException("must be in snapshot state to apply diff");
         }
 
-        PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
-                mSnapshot.mStorage, newList.mStorage, diffResult);
+        PagedList<T> previousSnapshot = mSnapshot;
         mPagedList = newList;
         mSnapshot = null;
+
+        // dispatch update callback after updating mPagedList/mSnapshot
+        PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
+                previousSnapshot.mStorage, newList.mStorage, diffResult);
+
         newList.addWeakCallback(diffSnapshot, mPagedListCallback);
     }
 
diff --git a/room/common/src/main/java/android/arch/persistence/room/Relation.java b/room/common/src/main/java/android/arch/persistence/room/Relation.java
index 7206699..d55bbfe 100644
--- a/room/common/src/main/java/android/arch/persistence/room/Relation.java
+++ b/room/common/src/main/java/android/arch/persistence/room/Relation.java
@@ -28,6 +28,8 @@
  * <pre>
  * {@literal @}Entity
  * public class Pet {
+ *     {@literal @} PrimaryKey
+ *     int id;
  *     int userId;
  *     String name;
  *     // other fields
@@ -41,8 +43,8 @@
  *
  * {@literal @}Dao
  * public interface UserPetDao {
- *     {@literal @}Query("SELECT id, name from User WHERE age &gt; :minAge")
- *     public List&lt;UserNameAndAllPets&gt; loadUserAndPets(int minAge);
+ *     {@literal @}Query("SELECT id, name from User")
+ *     public List&lt;UserNameAndAllPets&gt; loadUserAndPets();
  * }
  * </pre>
  * <p>
@@ -63,16 +65,16 @@
  *   {@literal @}Embedded
  *   public User user;
  *   {@literal @}Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class)
- *   public List<PetNameAndId> pets;
+ *   public List&lt;PetNameAndId&gt; pets;
  * }
  * {@literal @}Dao
  * public interface UserPetDao {
- *     {@literal @}Query("SELECT * from User WHERE age &gt; :minAge")
- *     public List&lt;UserAllPets&gt; loadUserAndPets(int minAge);
+ *     {@literal @}Query("SELECT * from User")
+ *     public List&lt;UserAllPets&gt; loadUserAndPets();
  * }
  * </pre>
  * <p>
- * In the example above, {@code PetNameAndId} is a regular but all of fields are fetched
+ * In the example above, {@code PetNameAndId} is a regular Pojo but all of fields are fetched
  * from the {@code entity} defined in the {@code @Relation} annotation (<i>Pet</i>).
  * {@code PetNameAndId} could also define its own relations all of which would also be fetched
  * automatically.
@@ -85,7 +87,7 @@
  *   public User user;
  *   {@literal @}Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class,
  *           projection = {"name"})
- *   public List<String> petNames;
+ *   public List&lt;String&gt; petNames;
  * }
  * </pre>
  * <p>
@@ -93,7 +95,7 @@
  * cannot have relations. This is a design decision to avoid common pitfalls in {@link Entity}
  * setups. You can read more about it in the main Room documentation. When loading data, you can
  * simply work around this limitation by creating Pojo classes that extend the {@link Entity}.
- *
+ * <p>
  * Note that the {@code @Relation} annotated field cannot be a constructor parameter, it must be
  * public or have a public setter.
  */
diff --git a/samples/Support7Demos/src/main/res/layout/appcompat_widgets_buttons.xml b/samples/Support7Demos/src/main/res/layout/appcompat_widgets_buttons.xml
index 517f4fa..ba4c045 100644
--- a/samples/Support7Demos/src/main/res/layout/appcompat_widgets_buttons.xml
+++ b/samples/Support7Demos/src/main/res/layout/appcompat_widgets_buttons.xml
@@ -73,40 +73,82 @@
         </RadioGroup>
 
         <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button"/>
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Button"/>
 
         <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button (small)"
-                style="@style/Widget.AppCompat.Button.Small"/>
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Button disabled"/>
 
         <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button (borderless)"
-                style="@style/Widget.AppCompat.Button.Borderless"/>
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Button (small)"
+            style="@style/Widget.AppCompat.Button.Small"/>
 
         <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button (borderless + colored)"
-                style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Button (small) disabled"
+            style="@style/Widget.AppCompat.Button.Small"/>
 
         <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button (colored)"
-                style="@style/Widget.AppCompat.Button.Colored"/>
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Button (borderless)"
+            style="@style/Widget.AppCompat.Button.Borderless"/>
 
         <Button
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="Button (colored + tinted)"
-                app:backgroundTint="#00FF00"
-                style="@style/Widget.AppCompat.Button.Colored"/>
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Button (borderless) disabled"
+            style="@style/Widget.AppCompat.Button.Borderless"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Button (borderless + colored)"
+            style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Button (borderless + colored) disabled"
+            style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Button (colored)"
+            style="@style/Widget.AppCompat.Button.Colored"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Button (colored) disabled"
+            style="@style/Widget.AppCompat.Button.Colored"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Button (colored + tinted)"
+            app:backgroundTint="#00FF00"
+            style="@style/Widget.AppCompat.Button.Colored"/>
+
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Button (colored + tinted) disabled"
+            app:backgroundTint="#00FF00"
+            style="@style/Widget.AppCompat.Button.Colored"/>
 
         <RatingBar
                 android:layout_width="wrap_content"
diff --git a/samples/SupportWearDemos/src/main/java/com/example/android/support/wear/app/AmbientModeDemo.java b/samples/SupportWearDemos/src/main/java/com/example/android/support/wear/app/AmbientModeDemo.java
index 70270d2..0e7d3de 100644
--- a/samples/SupportWearDemos/src/main/java/com/example/android/support/wear/app/AmbientModeDemo.java
+++ b/samples/SupportWearDemos/src/main/java/com/example/android/support/wear/app/AmbientModeDemo.java
@@ -37,7 +37,6 @@
         setContentView(R.layout.ambient_demo);
         mStateTextView = findViewById(R.id.ambient_text);
         AmbientMode.AmbientController controller = AmbientMode.attachAmbientSupport(this);
-        controller.setAutoResumeEnabled(true);
     }
 
     @Override
diff --git a/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml b/v7/appcompat/res/color-v21/abc_btn_colored_borderless_text_material.xml
similarity index 69%
copy from lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml
copy to v7/appcompat/res/color-v21/abc_btn_colored_borderless_text_material.xml
index 64ddf2e..f5585e3 100644
--- a/lifecycle/integration-tests/testapp/src/main/res/layout/activity.xml
+++ b/v7/appcompat/res/color-v21/abc_btn_colored_borderless_text_material.xml
@@ -14,8 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/activityFrameLayout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
+
+<!-- Used for the text of a borderless colored button. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="?android:attr/textColorSecondary"/>
+    <item android:color="?attr/colorAccent"/>
+</selector>
\ No newline at end of file
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextHelper.java b/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextHelper.java
index 51510aa..fa6196f 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextHelper.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/AppCompatTextHelper.java
@@ -29,6 +29,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
+import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.widget.TextViewCompat;
 import android.support.v7.appcompat.R;
 import android.text.method.PasswordTransformationMethod;
@@ -36,6 +37,8 @@
 import android.util.TypedValue;
 import android.widget.TextView;
 
+import java.lang.ref.WeakReference;
+
 @RequiresApi(9)
 class AppCompatTextHelper {
 
@@ -63,6 +66,7 @@
 
     private int mStyle = Typeface.NORMAL;
     private Typeface mFontTypeface;
+    private boolean mAsyncFontPending;
 
     AppCompatTextHelper(TextView view) {
         mView = view;
@@ -213,8 +217,23 @@
                     ? R.styleable.TextAppearance_android_fontFamily
                     : R.styleable.TextAppearance_fontFamily;
             if (!context.isRestricted()) {
+                final WeakReference<TextView> textViewWeak = new WeakReference<>(mView);
+                ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() {
+                    @Override
+                    public void onFontRetrieved(@NonNull Typeface typeface) {
+                        onAsyncTypefaceReceived(textViewWeak, typeface);
+                    }
+
+                    @Override
+                    public void onFontRetrievalFailed(int reason) {
+                        // Do nothing.
+                    }
+                };
                 try {
-                    mFontTypeface = a.getFont(fontFamilyId, mStyle);
+                    // Note the callback will be triggered on the UI thread.
+                    mFontTypeface = a.getFont(fontFamilyId, mStyle, replyCallback);
+                    // If this call gave us an immediate result, ignore any pending callbacks.
+                    mAsyncFontPending = mFontTypeface == null;
                 } catch (UnsupportedOperationException | Resources.NotFoundException e) {
                     // Expected if it is not a font resource.
                 }
@@ -222,12 +241,16 @@
             if (mFontTypeface == null) {
                 // Try with String. This is done by TextView JB+, but fails in ICS
                 String fontFamilyName = a.getString(fontFamilyId);
-                mFontTypeface = Typeface.create(fontFamilyName, mStyle);
+                if (fontFamilyName != null) {
+                    mFontTypeface = Typeface.create(fontFamilyName, mStyle);
+                }
             }
             return;
         }
 
         if (a.hasValue(R.styleable.TextAppearance_android_typeface)) {
+            // Ignore previous pending fonts
+            mAsyncFontPending = false;
             int typefaceIndex = a.getInt(R.styleable.TextAppearance_android_typeface, SANS);
             switch (typefaceIndex) {
                 case SANS:
@@ -245,6 +268,16 @@
         }
     }
 
+    private void onAsyncTypefaceReceived(WeakReference<TextView> textViewWeak, Typeface typeface) {
+        if (mAsyncFontPending) {
+            mFontTypeface = typeface;
+            final TextView textView = textViewWeak.get();
+            if (textView != null) {
+                textView.setTypeface(typeface, mStyle);
+            }
+        }
+    }
+
     void onSetTextAppearance(Context context, int resId) {
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
                 resId, R.styleable.TextAppearance);
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/TintTypedArray.java b/v7/appcompat/src/main/java/android/support/v7/widget/TintTypedArray.java
index 2270955..384c461 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/TintTypedArray.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/TintTypedArray.java
@@ -106,7 +106,8 @@
      *         not a font resource.
      */
     @Nullable
-    public Typeface getFont(@StyleableRes int index, int style) {
+    public Typeface getFont(@StyleableRes int index, int style,
+            @Nullable ResourcesCompat.FontCallback fontCallback) {
         final int resourceId = mWrapped.getResourceId(index, 0);
         if (resourceId == 0) {
             return null;
@@ -114,7 +115,7 @@
         if (mTypedValue == null) {
             mTypedValue = new TypedValue();
         }
-        return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style);
+        return ResourcesCompat.getFont(mContext, resourceId, mTypedValue, style, fontCallback);
     }
 
     public int length() {
diff --git a/wear/api/27.0.0.txt b/wear/api/27.0.0.txt
index e397eb3..e9b7d86 100644
--- a/wear/api/27.0.0.txt
+++ b/wear/api/27.0.0.txt
@@ -21,7 +21,6 @@
 
   public final class AmbientMode.AmbientController {
     method public boolean isAmbient();
-    method public void setAutoResumeEnabled(boolean);
   }
 
 }
diff --git a/wear/src/main/java/android/support/wear/ambient/AmbientMode.java b/wear/src/main/java/android/support/wear/ambient/AmbientMode.java
index 7fbbbb3..db53dfc 100644
--- a/wear/src/main/java/android/support/wear/ambient/AmbientMode.java
+++ b/wear/src/main/java/android/support/wear/ambient/AmbientMode.java
@@ -22,7 +22,6 @@
 import android.os.Bundle;
 import android.support.annotation.CallSuper;
 import android.support.annotation.VisibleForTesting;
-import android.util.Log;
 
 import com.google.android.wearable.compat.WearableActivityController;
 
@@ -263,20 +262,6 @@
         AmbientController() {}
 
         /**
-         * Sets whether this activity's task should be moved to the front when the system exits
-         * ambient mode. If true, the activity's task may be moved to the front if it was the last
-         * activity to be running when ambient started, depending on how much time the system spent
-         * in ambient mode.
-         */
-        public void setAutoResumeEnabled(boolean enabled) {
-            if (mDelegate != null) {
-                mDelegate.setAutoResumeEnabled(enabled);
-            } else {
-                Log.w(TAG, "The fragment is not yet fully initialized, this call is a no-op");
-            }
-        }
-
-        /**
          * @return {@code true} if the activity is currently in ambient.
          */
         public boolean isAmbient() {
diff --git a/wear/tests/src/android/support/wear/ambient/AmbientModeTest.java b/wear/tests/src/android/support/wear/ambient/AmbientModeTest.java
index 155622d..301c513 100644
--- a/wear/tests/src/android/support/wear/ambient/AmbientModeTest.java
+++ b/wear/tests/src/android/support/wear/ambient/AmbientModeTest.java
@@ -76,17 +76,6 @@
     }
 
     @Test
-    public void testControllerSetAutoResumeEnabled() {
-        AmbientModeTestActivity activity = mActivityRule.getActivity();
-
-        activity.getAmbientController().setAutoResumeEnabled(true);
-        assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
-
-        activity.getAmbientController().setAutoResumeEnabled(false);
-        assertFalse(WearableActivityController.getLastInstance().isAutoResumeEnabled());
-    }
-
-    @Test
     public void testCallsControllerIsAmbient() {
         AmbientModeTestActivity activity = mActivityRule.getActivity();