From 3513bfa6d643578666902479b36d6a43247b3adb Mon Sep 17 00:00:00 2001 From: Dimitris Dafnis <68849116+jim-daf@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:08:17 +0200 Subject: [PATCH] fix(WebView): properly recover when the renderer process is gone (#5823) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #5823 Issue #5823 reports that line-style dashboard cards (graph card, apex-charts-card, mini-graph-card) appear permanently greyed-out on a Samsung Note Pro 12.2 running LineageOS 17.1 (Android 10) / WebView Dev 142.0.7418.5. The attached chromium logs show the Mali GPU dropping the WebView's GL context via EXT_robustness and an out-of-memory in the renderer: SharedContextState context lost via EXT_robustness RasterDecoderImpl: Context lost during MakeCurrent GLES-MALI [tgid] : OoM error When this happens Android delivers WebViewClient.onRenderProcessGone to us. The current handler logs and then calls reload() — but per the android.webkit.WebView contract a WebView whose render process is gone is unusable and any further call on it is a no-op. The user is left staring at the broken dashboard until they kill the app. Detach + destroy the dead WebView and call Activity.recreate() so onCreate runs again with a fresh WebView and the last URL is re-loaded through the normal path. This is the recovery pattern the framework documentation recommends. Returning true keeps the hosting app process alive across the renderer crash. --- .../android/webview/WebViewActivity.kt | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt index 9795c36e8c2..77a8ad83342 100644 --- a/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -581,12 +581,32 @@ class WebViewActivity : } override fun onRenderProcessGone(view: WebView?, handler: RenderProcessGoneDetail?): Boolean { - Timber.e("onRenderProcessGone: webView crashed") - view?.let { - reload() - lifecycleScope.launch { - webViewAddJavascriptInterface() + val didCrash = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + handler?.didCrash() == true + } else { + // On API < 26 didCrash() is unavailable; treat as a crash so we still recover. + true + } + Timber.e( + "onRenderProcessGone: WebView renderer is gone (didCrash=$didCrash). " + + "This is commonly triggered by a GPU context loss or out-of-memory in the " + + "renderer process (see #5823 — Mali GPU + EXT_robustness on Android 10), " + + "which can leave dashboard cards rendering as blank/greyed-out surfaces. " + + "Recreating the activity to recover with a fresh WebView.", + ) + // Per the android.webkit.WebView contract, once the render process is gone the + // WebView instance is unusable and must not be touched again — calling reload() + // on the dead WebView is a no-op and leaves the user looking at greyed-out + // cards forever. Detach and destroy the dead WebView so its native resources + // are released, then recreate the activity to rebuild a fresh WebView and + // reload the last URL through the normal onCreate path. Returning `true` tells + // the system we have handled the crash so the hosting app process is not killed. + if (!isFinishing && !isRelaunching) { + view?.let { dead -> + (dead.parent as? android.view.ViewGroup)?.removeView(dead) + dead.destroy() } + recreate() } return true