Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b93f325 authored by Chris Göllner's avatar Chris Göllner Committed by Chris Göllner
Browse files

PrivacyDotWindowController: prevent crash when removing views from WM

When a display is removed right after being added, there was a crash
in PrivacyDotWindowController when trying to remove the views from
WindowManager.

To prevent this crash, we are now doing two things:
1. Ensure the stop/removal code runs on the same ui thread as the code
   that starts/adds views to WM.
2. Wrap the removal with try/catch for the worst case scenario.

Test: PrivacyDotWindowControllerTest
Fixes: 424739989
Flag: com.android.systemui.shared.status_bar_connected_displays
Change-Id: I06bc7dab742bda35125c9769a0ab6662eb02de86
parent 73bb4a52
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -265,6 +265,21 @@ class PrivacyDotWindowControllerTest : SysuiTestCase() {
        assertThat(windowManager.addedViews).isEmpty()
    }

    @Test
    fun onStop_removingWindowViewsThrows_codeDoesNotCrash() {
        underTest.start()
        executor.runAllReady()

        viewController.showingListener?.onPrivacyDotShown(viewController.topLeft)
        executor.runAllReady()

        // Simulate removing a view from window manager outside of our code
        windowManager.addedViews.clear()
        // Ensure no crash when stopping and trying to remove an already detached view
        underTest.stop()
        executor.runAllReady()
    }

    // Helper functions: Note that paramsForView needs to find the *root* view (FrameLayout)
    // that was added to the window manager, not the inner dotView.
    private fun paramsForView(dotView: View): WindowManager.LayoutParams {
+12 −2
Original line number Diff line number Diff line
@@ -96,7 +96,7 @@ constructor(
                    val dotViewContainer = dotViewContainersByView[v]
                    val windowView = dotWindowViewsByCorner.remove(dotViewContainer?.corner)
                    if (windowView != null) {
                        windowManager.removeView(windowView)
                        windowManager.removeViewSafely(windowView)
                    }
                }
            }
@@ -143,7 +143,9 @@ constructor(
    }

    fun stop() {
        dotWindowViewsByCorner.forEach { windowManager.removeView(it.value) }
        uiExecutor.execute {
            dotWindowViewsByCorner.forEach { windowManager.removeViewSafely(it.value) }
        }
    }

    private data class DotViewContainer(
@@ -162,6 +164,14 @@ constructor(
        ): PrivacyDotWindowController
    }

    private fun WindowManager.removeViewSafely(view: View) {
        try {
            removeView(view)
        } catch (e: IllegalArgumentException) {
            Log.e(TAG, "Failed to remove view from window manager.")
        }
    }

    private companion object {
        const val TAG = "PrivacyDotWindowController"
    }
+2 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ class FakeWindowManager(private val context: Context) : WindowManager {

    override fun removeView(view: View) {
        addedViews.remove(view)
            ?: throw IllegalArgumentException("$view not attached to window manager")
    }

    override fun updateViewLayout(view: View, params: ViewGroup.LayoutParams) {
@@ -49,7 +50,7 @@ class FakeWindowManager(private val context: Context) : WindowManager {
    }

    override fun removeViewImmediate(view: View) {
        addedViews.remove(view)
        removeView(view)
    }

    override fun requestAppKeyboardShortcuts(