Ensure Renderer.onDestroy is called

In the scenario where the renderer has been created but WF init has
not completed and Engine.onDestroy is called,  we need to call
Renderer.onDestroy. This patch makes that happen.

Bug: 203045029
Test: Added a new test
Change-Id: I620dbcc14a80b52a71cc78c9a0da1c42b143322c
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index d814da5..e0bec05 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -1094,6 +1094,8 @@
 
         @UiThread
         override fun onDestroy(): Unit = TraceEvent("EngineWrapper.onDestroy").use {
+            super.onDestroy()
+
             destroyed = true
             backgroundThreadCoroutineScope.cancel()
             quitBackgroundThreadIfCreated()
@@ -1101,14 +1103,31 @@
             if (this::choreographer.isInitialized) {
                 choreographer.removeFrameCallback(frameCallback)
             }
-            // The WatchFaceImpl is created on the UiThread so if we get here and it's not crated we
-            // can be sure it'll never be created hence we don't need to destroy.
-            getWatchFaceImplOrNull()?.onDestroy()
             if (this::interactiveInstanceId.isInitialized) {
                 InteractiveInstanceManager.deleteInstance(interactiveInstanceId)
             }
 
-            super.onDestroy()
+            // NB user code could throw an exception so do this last.
+            runBlocking {
+                try {
+                    // The WatchFaceImpl is created on the UiThread so if we get here and it's not
+                    // created we can be sure it'll never be created hence we don't need to destroy
+                    // it.
+                    if (deferredWatchFaceImpl.isCompleted) {
+                        deferredWatchFaceImpl.await().onDestroy()
+                    } else if (deferredRendererAndComplicationManager.isCompleted) {
+                        // However we should destroy the renderer if its been created.
+                        deferredRendererAndComplicationManager.await().renderer.onDestroy()
+                    }
+                } catch (e: Exception) {
+                    // Throwing an exception here leads to a cascade of errors, log instead.
+                    Log.e(
+                        TAG,
+                        "WatchFace exception observed in onDestroy (may have occurred during init)",
+                        e
+                    )
+                }
+            }
         }
 
         override fun onSurfaceDestroyed(holder: SurfaceHolder) {
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index f8a1c6d..046af9b 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -78,6 +78,7 @@
 import androidx.wear.watchface.style.WatchFaceLayer
 import com.google.common.truth.Truth.assertThat
 import com.nhaarman.mockitokotlin2.mock
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.android.asCoroutineDispatcher
@@ -3305,6 +3306,83 @@
         assertThat(unparceled.isInitiallyEnabled).isTrue()
     }
 
+    @Test
+    public fun renderer_onDestroy_called_even_if_init_not_complete() {
+        val initDeferred = CompletableDeferred<Unit>()
+        var onDestroyCalled = false
+        testWatchFaceService = TestWatchFaceService(
+            WatchFaceType.DIGITAL,
+            emptyList(),
+            { _, currentUserStyleRepository, watchState ->
+                renderer = object : TestRenderer(
+                    surfaceHolder,
+                    currentUserStyleRepository,
+                    watchState,
+                    INTERACTIVE_UPDATE_RATE_MS
+                ) {
+                    // Prevent initialization until initDeferred completes.
+                    override suspend fun init() {
+                        super.init()
+                        initDeferred.await()
+                    }
+
+                    override fun onDestroy() {
+                        super.onDestroy()
+                        onDestroyCalled = true
+                    }
+                }
+                renderer
+            },
+            UserStyleSchema(emptyList()),
+            watchState,
+            handler,
+            null,
+            false,
+            null,
+            choreographer
+        )
+
+        InteractiveInstanceManager
+            .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
+                InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
+                    WallpaperInteractiveWatchFaceInstanceParams(
+                        "TestID",
+                        DeviceConfig(
+                            false,
+                            false,
+                            0,
+                            0
+                        ),
+                        WatchUiState(false, 0),
+                        UserStyle(emptyMap()).toWireFormat(),
+                        emptyList()
+                    ),
+                    object : IPendingInteractiveWatchFace.Stub() {
+                        override fun getApiVersion() =
+                            IPendingInteractiveWatchFace.API_VERSION
+
+                        override fun onInteractiveWatchFaceCreated(
+                            iInteractiveWatchFace: IInteractiveWatchFace
+                        ) {
+                            interactiveWatchFaceInstance = iInteractiveWatchFace
+                        }
+
+                        override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
+                            fail("WatchFace crashed: $exception")
+                        }
+                    }
+                )
+            )
+
+        engineWrapper = testWatchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
+        engineWrapper.onCreate(surfaceHolder)
+        engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+        assertThat(engineWrapper.deferredWatchFaceImpl.isCompleted).isFalse()
+
+        engineWrapper.onDestroy()
+        assertThat(onDestroyCalled).isTrue()
+    }
+
     @SuppressLint("NewApi")
     @Suppress("DEPRECATION")
     private fun getChinWindowInsetsApi25(@Px chinHeight: Int): WindowInsets =