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

Commit deeec97a authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 13219462 from b5f6b215 to 25Q2-release

Change-Id: I9f0d1d83ff272e5bfd68b3502d3f70a79c4c2463
parents abeb99d6 b5f6b215
Loading
Loading
Loading
Loading
+37 −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.app.displaylib

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

/**
 * Reports the display ids that should have a per-display instance, if any.
 *
 * This can be overridden to support different policies (e.g. display being connected, display
 * having decorations, etc..). A [PerDisplayRepository] instance is expected to be cleaned up when a
 * displayId is removed from this set.
 */
interface DisplayInstanceLifecycleManager {
    /** Set of display ids that are allowed to have an instance. */
    val displayIds: StateFlow<Set<Int>>
}

/** Meant to be used in tests. */
class FakeDisplayInstanceLifecycleManager : DisplayInstanceLifecycleManager {
    override val displayIds = MutableStateFlow<Set<Int>>(emptySet())
}
+46 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.app.displaylib

import android.util.Log
import android.view.Display
import com.android.app.tracing.coroutines.flow.stateInTraced
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.traceSection
import dagger.assisted.Assisted
@@ -26,7 +27,10 @@ import dagger.assisted.AssistedInject
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Qualifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine

/**
 * Used to create instances of type `T` for a specific display.
@@ -109,10 +113,16 @@ interface PerDisplayRepository<T> {
 *
 * This class manages a cache of per-display instances of type `T`, creating them using a provided
 * [PerDisplayInstanceProvider] and optionally tearing them down using a
 * [PerDisplayInstanceProviderWithTeardown] when displays are disconnected.
 * [PerDisplayInstanceProviderWithTeardown] when based on [lifecycleManager].
 *
 * It listens to the [DisplayRepository] to detect when displays are added or removed, and
 * automatically manages the lifecycle of the per-display instances.
 * An instance will be destroyed when either
 * - The display is not connected anymore
 * - or based on [lifecycleManager]. If no lifecycle manager is provided, instances are destroyed
 *   when the display is disconnected.
 *
 * [DisplayInstanceLifecycleManager] can decide to delete instances for a display even before it is
 * disconnected. An example of usecase for it, is to delete instances when screen decorations are
 * removed.
 *
 * Note that this is a [PerDisplayStoreImpl] 2.0 that doesn't require [CoreStartable] bindings,
 * providing all args in the constructor.
@@ -122,6 +132,7 @@ class PerDisplayInstanceRepositoryImpl<T>
constructor(
    @Assisted override val debugName: String,
    @Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
    @Assisted lifecycleManager: DisplayInstanceLifecycleManager? = null,
    @DisplayLibBackground bgApplicationScope: CoroutineScope,
    private val displayRepository: DisplayRepository,
    private val initCallback: PerDisplayRepository.InitCallback,
@@ -129,13 +140,34 @@ constructor(

    private val perDisplayInstances = ConcurrentHashMap<Int, T?>()

    private val allowedDisplays: StateFlow<Set<Int>> =
        if (lifecycleManager == null) {
                displayRepository.displayIds
            } else {
                // If there is a lifecycle manager, we still consider the smallest subset between
                // the ones connected and the ones from the lifecycle. This is to safeguard against
                // leaks, in case of lifecycle manager misbehaving (as it's provided by clients, and
                // we can't guarantee it's correct).
                combine(lifecycleManager.displayIds, displayRepository.displayIds) {
                    lifecycleAllowedDisplayIds,
                    connectedDisplays ->
                    lifecycleAllowedDisplayIds.intersect(connectedDisplays)
                }
            }
            .stateInTraced(
                "allowed displays for $debugName",
                bgApplicationScope,
                SharingStarted.WhileSubscribed(),
                setOf(Display.DEFAULT_DISPLAY),
            )

    init {
        bgApplicationScope.launch("$debugName#start") { start() }
    }

    private suspend fun start() {
        initCallback.onInit(debugName, this)
        displayRepository.displayIds.collectLatest { displayIds ->
        allowedDisplays.collectLatest { displayIds ->
            val toRemove = perDisplayInstances.keys - displayIds
            toRemove.forEach { displayId ->
                Log.d(TAG, "<$debugName> destroying instance for displayId=$displayId.")
@@ -154,6 +186,15 @@ constructor(
            return null
        }

        if (displayId !in allowedDisplays.value) {
            Log.e(
                TAG,
                "<$debugName: Display with id $displayId exists but it's not " +
                    "allowed by lifecycle manager.",
            )
            return null
        }

        // If it doesn't exist, create it and put it in the map.
        return perDisplayInstances.computeIfAbsent(displayId) { key ->
            Log.d(TAG, "<$debugName> creating instance for displayId=$key, as it wasn't available.")
@@ -176,6 +217,7 @@ constructor(
        fun create(
            debugName: String,
            instanceProvider: PerDisplayInstanceProvider<T>,
            overrideLifecycleManager: DisplayInstanceLifecycleManager? = null,
        ): PerDisplayInstanceRepositoryImpl<T>
    }

+10 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ abstract class LiveWallpaper : WallpaperService() {
        const val COMMAND_PREVIEW_INFO = "android.wallpaper.previewinfo"
        const val COMMAND_LOCKSCREEN_LAYOUT_CHANGED = "android.wallpaper.lockscreen_layout_changed"
        const val COMMAND_LOCKSCREEN_TAP = "android.wallpaper.lockscreen_tap"
        const val COMMAND_KEYGUARD_APPEARING = "android.wallpaper.keyguardappearing"
        const val WALLPAPER_FLAG_NOT_FOUND = -1
    }

@@ -423,6 +424,9 @@ abstract class LiveWallpaper : WallpaperService() {
                        onLockscreenFocalAreaTap(x, y)
                    }
                }
                COMMAND_KEYGUARD_APPEARING -> {
                    onKeyguardAppearing()
                }
            }

            if (resultRequested) return extras
@@ -475,6 +479,12 @@ abstract class LiveWallpaper : WallpaperService() {
            }
        }

        fun onKeyguardAppearing() {
            if (wallpaperEngine is LiveWallpaperKeyguardEventListener) {
                (wallpaperEngine as LiveWallpaperKeyguardEventListener).onKeyguardAppearing()
            }
        }

        fun onPreviewInfoReceived(extras: Bundle?) {
            if (wallpaperEngine is LiveWallpaperEventListener) {
                (wallpaperEngine as LiveWallpaperEventListener).onPreviewInfoReceived(extras)
+2 −0
Original line number Diff line number Diff line
@@ -21,4 +21,6 @@ interface LiveWallpaperKeyguardEventListener {

    /** Called when the keyguard is going away. */
    fun onKeyguardGoingAway()

    fun onKeyguardAppearing()
}
+2 −0
Original line number Diff line number Diff line
@@ -152,6 +152,8 @@ class WeatherEngine(
        userPresenceController.onKeyguardGoingAway()
    }

    override fun onKeyguardAppearing() {}

    override fun onOffsetChanged(xOffset: Float, xOffsetStep: Float) {
        // No-op.
    }