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

Commit 6f9a9525 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Fix work profile reinflations when shade window moves between displays

The problem happens if both the following conditions are true:
- Shade is on the default display
- App that triggers the notification is in an external display

When the above is true, the notification was inflated with the wrong
display context, resulting in wrong sizing (e.g. notification on the
default display with dimensions for the external one)

Why was this happening though, as with the
enablePerDisplayPackageContextCacheInStatusbarNotif we already used a
per-display context cache in creating the context for each notification?

It turns out the cache is used for both status bar icons and
notification row view inflations.
However, for the default display the statusbar context is the
SystemUIApplication one, so it's not a WindowContext and does not have a
 mToken associated (see ContextImpl.java for more details on mToken, but
  essentially it's needed to create new resources associated to the
  correct display).

The root cause of the issue here is that when a package context was
created from the application context (from StatusBarNotification
.getPackageContext), even if the application context was associated with
 the default display, it caused the resulting context to have resources
 for the display associated with the app (so the external display in
 this case). The result was a context with wrong resources for the
 default display, that was causing inflations with wrong dimensions (as
 the configuration was wrong)

This is now fixed by always using a STATUS_BAR WindowContext for the
statusbar, instead of the application context. This causes all the
package context created to have resources for the correct display.

You might also ask: why are we using the @Main/@StatusBarMain context in
 IconBuilder, instead of a display specific one? It's wrong, and that
 class should be refactored to avoid using the default context at all.
 If it was already using the correct WindowContext there, the creation
 of the new package context cached would have been correct already. This
  should be fixed as part of another refactor.

Bug: 414788860
Flag: com.android.systemui.shade_window_goes_around
Test: DisplayWindowPropertiesRepositoryImplTest
Change-Id: Ie836c13864c8f77dc4d1babafe8d44337a70f218
parent 4d1f3b8e
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -63,8 +63,6 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() {
        DisplayWindowPropertiesRepositoryImpl(
            kosmos.applicationCoroutineScope,
            applicationContext,
            applicationWindowManager,
            kosmos.layoutInflater,
            fakeDisplayRepository,
        )
    }
@@ -98,12 +96,12 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() {
    }

    @Test
    fun get_defaultDisplayId_returnsDefaultProperties() =
    fun get_defaultDisplayId_doesNotReturnDefaultProperties() =
        testScope.runTest {
            val displayContext = repo.get(DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)

            assertThat(displayContext)
                .isEqualTo(
                .isNotEqualTo(
                    DisplayWindowProperties(
                        displayId = DEFAULT_DISPLAY_ID,
                        windowType = WINDOW_TYPE_FOO,
+17 −25
Original line number Diff line number Diff line
@@ -58,8 +58,6 @@ class DisplayWindowPropertiesRepositoryImpl
constructor(
    @Background private val backgroundApplicationScope: CoroutineScope,
    private val globalContext: Context,
    private val globalWindowManager: WindowManager,
    private val globalLayoutInflater: LayoutInflater,
    private val displayRepository: DisplayRepository,
) : DisplayWindowPropertiesRepository, CoreStartable {

@@ -95,17 +93,6 @@ constructor(

    private fun create(display: Display, windowType: Int): DisplayWindowProperties? {
        val displayId = display.displayId
        return if (displayId == Display.DEFAULT_DISPLAY) {
            // For the default display, we can just reuse the global/application properties.
            // Creating a window context is expensive, therefore we avoid it.
            DisplayWindowProperties(
                displayId = displayId,
                windowType = windowType,
                context = globalContext,
                windowManager = globalWindowManager,
                layoutInflater = globalLayoutInflater,
            )
        } else {
        val context = createWindowContext(display, windowType)
        if (context.displayId != display.displayId) {
            Log.e(
@@ -118,8 +105,13 @@ constructor(
        @SuppressLint("NonInjectedService") // Need to manually get the service
        val windowManager = WindowManagerUtils.getWindowManager(context)
        val layoutInflater = LayoutInflater.from(context)
            DisplayWindowProperties(displayId, windowType, context, windowManager, layoutInflater)
        }
        return DisplayWindowProperties(
            displayId,
            windowType,
            context,
            windowManager,
            layoutInflater,
        )
    }

    private fun createWindowContext(display: Display, windowType: Int): Context =
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.systemui.statusbar.dagger

import javax.inject.Qualifier

/** Qualifier that annotates instances related to the default display statusbar. */
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class StatusBarMain
+18 −0
Original line number Diff line number Diff line
@@ -17,11 +17,15 @@
package com.android.systemui.statusbar.dagger

import android.content.Context
import android.view.Display
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
import com.android.systemui.CameraProtectionLoader
import com.android.systemui.CoreStartable
import com.android.systemui.SysUICutoutProvider
import com.android.systemui.SysUICutoutProviderImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
@@ -203,5 +207,19 @@ interface StatusBarModule {
        ): StatusBarContentInsetsViewModel {
            return StatusBarContentInsetsViewModel(insetsProvider)
        }

        @Provides
        @StatusBarMain
        fun provideDefaultStatusBarContext(
            repoLazy: Lazy<DisplayWindowPropertiesRepositoryImpl>,
            @Main appContext: Context,
        ): Context {
            return if (StatusBarConnectedDisplays.isEnabled) {
                return repoLazy.get().get(Display.DEFAULT_DISPLAY, TYPE_STATUS_BAR)?.context
                    ?: appContext
            } else {
                appContext
            }
        }
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -18,18 +18,19 @@ package com.android.systemui.statusbar.notification.icon

import android.app.Notification
import android.content.Context
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.dagger.StatusBarMain
import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.contentDescForNotification
import javax.inject.Inject

/** Testable wrapper around Context. */
class IconBuilder @Inject constructor(@Main private val context: Context) {
class IconBuilder @Inject constructor(@StatusBarMain private val context: Context) {
    @JvmOverloads
    fun createIconView(
        entry: NotificationEntry,
        // TODO b/362720336: remove all usages of this without a provided context.
        context: Context = this.context,
    ): StatusBarIconView {
        return StatusBarIconView(