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

Commit 22192060 authored by helen cheuk's avatar helen cheuk
Browse files

Cache the projected mode state

Cached the projected mode state to avoid triggering binder calls on
every isProjectedMode method

Bug: 432013219
Test: DesktopStateImplTest
Flag: EXEMPT bug fix

Change-Id: Ifc6839b26e93ecb8fe5f490656c25a6ef509f88d
parent 59f35e58
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -116,5 +116,9 @@ interface DesktopState {
        /** Creates a new [DesktopState] from a context. */
        @JvmStatic
        fun fromContext(context: Context): DesktopState = DesktopStateImpl(context)

        /** Get a [DesktopState] singleton. This method should not be used if Dagger is used to inject the singleton. */
        @JvmStatic
        fun getInstance(context: Context): DesktopState = DesktopStateImpl.getInstance(context)
    }
}
+20 −13
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ class DesktopStateImpl(context: Context) : DesktopState {
    private val windowManager = context.getSystemService(WindowManager::class.java)
    private val displayManager = context.getSystemService(DisplayManager::class.java)

    private val projectedModeState by lazy { ProjectedModeState(context, this) }

    private val enforceDeviceRestrictions =
        SystemProperties.getBoolean(ENFORCE_DEVICE_RESTRICTIONS_SYS_PROP, true)

@@ -122,19 +124,7 @@ class DesktopStateImpl(context: Context) : DesktopState {
        return windowManager?.isEligibleForDesktopMode(display.displayId) ?: false
    }

    override fun isProjectedMode(): Boolean {
        if (!DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue) {
            return false
        }

        if (isDesktopModeSupportedOnDisplay(Display.DEFAULT_DISPLAY)) {
            return false
        }

        return displayManager?.displays
            ?.any { display -> isDesktopModeSupportedOnDisplay(display)
            } ?: false
    }
    override fun isProjectedMode(): Boolean = projectedModeState.isProjectedMode

    private val deviceHasLargeScreen =
        displayManager?.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
@@ -170,5 +160,22 @@ class DesktopStateImpl(context: Context) : DesktopState {
        @VisibleForTesting
        const val ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAY_SYS_PROP =
            "persist.wm.debug.enter_desktop_by_default_on_freeform_display"

        @Volatile
        private var instance: DesktopState? = null

        /**
         * Get or create the [DesktopState] singleton.
         *
         * This method should not be used if Dagger is used to inject the singleton.
         */
        fun getInstance(context: Context): DesktopState {
            return instance ?: synchronized(this) {
                if (instance == null) {
                    instance = DesktopStateImpl(context)
                }
                instance!!
            }
        }
    }
}
+81 −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.wm.shell.shared.desktopmode

import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.view.Display
import android.view.IDisplayWindowListener
import android.view.WindowManagerGlobal
import android.window.DesktopExperienceFlags

/** Caches and manages the projected mode state. */
class ProjectedModeState(context: Context, val desktopState: DesktopState) {
    private val displayManager = context.getSystemService(DisplayManager::class.java)

    var isProjectedMode: Boolean = getProjectedMode()
        private set

    val callback =
        object : IDisplayWindowListener.Stub() {
            override fun onDisplayAddSystemDecorations(displayId: Int) {}

            override fun onDisplayRemoveSystemDecorations(displayId: Int) {}

            override fun onDesktopModeEligibleChanged(displayId: Int) {
                isProjectedMode = getProjectedMode()
            }

            override fun onDisplayAdded(displayId: Int) {}

            override fun onDisplayConfigurationChanged(displayId: Int, newConfig: Configuration?) {}

            override fun onDisplayRemoved(displayId: Int) {}

            override fun onFixedRotationStarted(displayId: Int, newRotation: Int) {}

            override fun onFixedRotationFinished(displayId: Int) {}

            override fun onKeepClearAreasChanged(
                displayId: Int,
                restricted: MutableList<Rect>?,
                unrestricted: MutableList<Rect>?,
            ) {}

            override fun onDisplayAnimationsDisabledChanged(displayId: Int, disabled: Boolean) {}
        }

    init {
        WindowManagerGlobal.getWindowManagerService()?.registerDisplayWindowListener(callback)
    }

    private fun getProjectedMode(): Boolean {
        if (!DesktopExperienceFlags.ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE.isTrue) {
            return false
        }

        if (desktopState.isDesktopModeSupportedOnDisplay(Display.DEFAULT_DISPLAY)) {
            return false
        }

        return displayManager?.displays?.any { display: Display ->
            desktopState.isDesktopModeSupportedOnDisplay(display)
        } ?: false
    }
}
+34 −0
Original line number Diff line number Diff line
@@ -22,7 +22,10 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.view.Display
import android.view.IDisplayWindowListener
import android.view.IWindowManager
import android.view.WindowManager
import android.view.WindowManagerGlobal
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import androidx.test.filters.SmallTest
@@ -38,7 +41,9 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -63,6 +68,7 @@ class DesktopStateImplTest : ShellTestCase() {
            ExtendedMockito.mockitoSession()
                .strictness(Strictness.LENIENT)
                .mockStatic(SystemProperties::class.java)
                .mockStatic(WindowManagerGlobal::class.java)
                .startMocking()

        val resources = mContext.getOrCreateTestableResources()
@@ -370,7 +376,35 @@ class DesktopStateImplTest : ShellTestCase() {
        resources.addOverride(R.bool.config_canInternalDisplayHostDesktops, false)
        whenever(windowManager.isEligibleForDesktopMode(anyInt())).thenReturn(true)
        val desktopState = DesktopStateImpl(context)
        assertThat(desktopState.isProjectedMode()).isTrue()
    }

    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE, Flags.FLAG_ENABLE_PROJECTED_DISPLAY_DESKTOP_MODE, FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
    @Test
    fun isProjectedMode_oneDisplay_returnFalse_addAnotherDisplay_onDesktopModeEligibleChanged_returnsTrue() {
        val mockService: IWindowManager = mock()
        whenever(WindowManagerGlobal.getWindowManagerService()).thenReturn(mockService)
        val resources = mContext.getOrCreateTestableResources()
        resources.addOverride(R.bool.config_isDesktopModeSupported, true)
        resources.addOverride(R.bool.config_canInternalDisplayHostDesktops, false)
        whenever(displayManager.displays).thenReturn(arrayOf(defaultDisplay))
        val desktopState = DesktopStateImpl(context)

        assertThat(desktopState.isProjectedMode()).isFalse()

        val argument = argumentCaptor<IDisplayWindowListener>()
        verify(mockService)
            .registerDisplayWindowListener(
                argument.capture(),
            )
       val listener = argument.firstValue

        // Add an extended display
        whenever(displayManager.displays)
            .thenReturn(arrayOf(defaultDisplay, extendedDisplay))
        whenever(windowManager.isEligibleForDesktopMode(anyInt())).thenReturn(true)

        listener.onDesktopModeEligibleChanged(extendedDisplay.displayId)
        assertThat(desktopState.isProjectedMode()).isTrue()
    }