Fix clearing sequence from ViewModel closeables

* Set clearing sequence as `keyToCloseable + closeable`.
* Include a unit test to ensure the closing ordering is always respected.
* The clearing sequence was wrongly changed to `closeable + keyToCloseable` by aosp/2997876.

Test: ViewModelTest
Fixes: 333573506
Change-Id: I87ee7a0649cf9702790d7e6a0e31558584c826d0
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModel.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModel.kt
index cb1bb1a..10913aa 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModel.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/ViewModel.kt
@@ -145,10 +145,7 @@
      * prevent a memory leak, as the subscriptions might hold a reference to the [ViewModel] even
      * after it is no longer needed.
      *
-     * **Clearing Sequence:**
-     * 1. [AutoCloseable.close] resources added **without** a key via [addCloseable].
-     * 2. [AutoCloseable.close] resources added **with** a key via [addCloseable].
-     * 3. Invoke the [onCleared] callback.
+     * For specifics about the clearing sequence, refer to the [clear] method.
      */
     protected open fun onCleared()
 
@@ -159,9 +156,10 @@
      * immediately closed.
      *
      * **Clearing Sequence:**
-     * 1. [AutoCloseable.close] resources added **without** a key via [addCloseable].
-     * 2. [AutoCloseable.close] resources added **with** a key via [addCloseable].
-     * 3. Invoke the [onCleared] callback.
+     * 1. [Close][AutoCloseable.close] resources added **with** a key via [addCloseable].
+     * 2. [Close][AutoCloseable.close] resources added via `constructor`.
+     * 3. [Close][AutoCloseable.close] resources added **without** a key via [addCloseable].
+     * 4. Invoke the [onCleared] callback.
      */
     @MainThread
     internal fun clear()
diff --git a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelImpl.kt b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelImpl.kt
index dd79e0c..00efd4b 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelImpl.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonMain/kotlin/androidx/lifecycle/viewmodel/internal/ViewModelImpl.kt
@@ -42,13 +42,10 @@
      * The associated resources will be [AutoCloseable.close] right before the [ViewModel.onCleared]
      * is called. This provides automatic resource cleanup upon [ViewModel] release.
      *
-     * The clearing order is:
-     * 1. [keyToCloseables][AutoCloseable.close]
-     * 2. [closeables][AutoCloseable.close]
-     * 3. [ViewModel.onCleared]
+     * For specifics about the clearing sequence, refer to the [ViewModel.clear] method.
      *
-     * **Note:** Manually [SynchronizedObject] is necessary to prevent issues on Android API 21 and 22.
-     * This avoids potential problems found in older versions of `ConcurrentHashMap`.
+     * **Note:** Manually [SynchronizedObject] is necessary to prevent issues on Android API 21
+     * and 22. This avoids potential problems found in older versions of `ConcurrentHashMap`.
      *
      * @see <a href="https://issuetracker.google.com/37042460">b/37042460</a>
      */
@@ -84,9 +81,10 @@
 
         isCleared = true
         synchronized(lock) {
-            // 1. Closes resources added without a key.
-            // 2. Closes resources added with a key.
-            for (closeable in closeables + keyToCloseables.values) {
+            for (closeable in keyToCloseables.values) {
+                closeWithRuntimeException(closeable)
+            }
+            for (closeable in closeables) {
                 closeWithRuntimeException(closeable)
             }
             // Clear only resources without keys to prevent accidental recreation of resources.
diff --git a/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelTest.kt b/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelTest.kt
index 570bf8e..fc82ddb 100644
--- a/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelTest.kt
+++ b/lifecycle/lifecycle-viewmodel/src/commonTest/kotlin/androidx/lifecycle/ViewModelTest.kt
@@ -154,12 +154,50 @@
     }
     //endregion
 
+    @Test
+    fun clear_closesResources_inCleaningSequenceOrder() {
+        val clearedInOrderCloseables = mutableListOf<AutoCloseable>()
+        val closeableInConstructor1 = CloseableResource { clearedInOrderCloseables += this }
+        val closeableInConstructor2 = CloseableResource { clearedInOrderCloseables += this }
+        val closeableWithoutKey1 = CloseableResource { clearedInOrderCloseables += this }
+        val closeableWithoutKey2 = CloseableResource { clearedInOrderCloseables += this }
+        val closeableWithKey1 = CloseableResource { clearedInOrderCloseables += this }
+        val closeableWithKey2 = CloseableResource { clearedInOrderCloseables += this }
+
+        val viewModel = TestViewModel(closeableInConstructor1, closeableInConstructor2)
+        viewModel.addCloseable(closeableWithoutKey1)
+        viewModel.addCloseable(key = "customKey1", closeableWithKey1)
+        viewModel.addCloseable(closeableWithoutKey2)
+        viewModel.addCloseable(key = "customKey2", closeableWithKey2)
+        viewModel.clear()
+
+        // The clearing order is:
+        val expectedCloseables = listOf(
+            // 1. Resources added **with** a key via `addCloseable`.
+            closeableWithKey1,
+            closeableWithKey2,
+
+            // 2. Resources added **without** a key via `constructor`.
+            closeableInConstructor1,
+            closeableInConstructor2,
+
+            // 3. Resources added **without** a key via `addCloseable`.
+            closeableWithoutKey1,
+            closeableWithoutKey2,
+        )
+        assertThat(clearedInOrderCloseables).isEqualTo(expectedCloseables)
+    }
+
     //region test helpers
     private class TestViewModel(vararg closeables: AutoCloseable) : ViewModel(*closeables)
 
-    private class CloseableResource(var isClosed: Boolean = false) : AutoCloseable {
+    private class CloseableResource(
+        var isClosed: Boolean = false,
+        val onClose: CloseableResource.() -> Unit = {},
+    ) : AutoCloseable {
         override fun close() {
             isClosed = true
+            onClose(this)
         }
     }
     //endregion