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

Commit de26e7fc authored by Charles Chen's avatar Charles Chen
Browse files

Implement ViewCaptureAwareWm with wrapper

Test: atest ViewCaptureAwareWindowManagerTest
Bug: 406318166
Flag: com.android.systemui.enable_view_capture_tracing
Change-Id: I1172b9add5b01f3f9eded430e5bf713a343ff40c
parent 783e666b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ android_test {
        "view_capture",
        "androidx.test.ext.junit",
        "androidx.test.rules",
        "flag-junit",
        "testables",
        "mockito-kotlin2",
        "mockito-target-extended-minus-junit4",
+7 −5
Original line number Diff line number Diff line
@@ -18,12 +18,11 @@ package com.android.app.viewcapture

import android.content.Context
import android.media.permission.SafeCloseable
import android.os.IBinder
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import android.view.WindowManagerImpl
import android.view.WindowManagerWrapper

/**
 * [WindowManager] implementation to enable view tracing. Adds [ViewCapture] to associated window
@@ -32,9 +31,8 @@ import android.view.WindowManagerImpl
 */
internal class ViewCaptureAwareWindowManager(
    private val context: Context,
    private val parent: Window? = null,
    private val windowContextToken: IBinder? = null,
) : WindowManagerImpl(context, parent, windowContextToken) {
    private val base: WindowManager,
) : WindowManagerWrapper(base) {

    private var viewCaptureCloseableMap: MutableMap<View, SafeCloseable> = mutableMapOf()

@@ -55,6 +53,10 @@ internal class ViewCaptureAwareWindowManager(
        super.removeViewImmediate(view)
    }

    override fun createLocalWindowManager(parentWindow: Window): WindowManager {
        return ViewCaptureAwareWindowManager(context, base.createLocalWindowManager(parentWindow))
    }

    private fun getViewName(view: View) = "." + view.javaClass.name

    private fun removeViewFromCloseableMap(view: View?) {
+11 −11
Original line number Diff line number Diff line
@@ -17,16 +17,13 @@
package com.android.app.viewcapture

import android.content.Context
import android.os.IBinder
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.view.Window
import android.view.WindowManager
import java.lang.ref.WeakReference
import java.util.Collections
import java.util.WeakHashMap


/** Factory to create [Context] specific instances of [ViewCaptureAwareWindowManager]. */
object ViewCaptureAwareWindowManagerFactory {

@@ -43,19 +40,22 @@ object ViewCaptureAwareWindowManagerFactory {
     * no instance is cached; it creates, caches and returns a new instance.
     */
    @JvmStatic
    fun getInstance(
        context: Context,
        parent: Window? = null,
        windowContextToken: IBinder? = null,
    ): WindowManager {
        Trace.traceCounter(TRACE_TAG_APP,
            "ViewCaptureAwareWindowManagerFactory#instanceMap.size", instanceMap.size)
    fun getInstance(context: Context): WindowManager {
        Trace.traceCounter(
            TRACE_TAG_APP,
            "ViewCaptureAwareWindowManagerFactory#instanceMap.size",
            instanceMap.size,
        )

        val cachedWindowManager = instanceMap[context]?.get()
        if (cachedWindowManager != null) {
            return cachedWindowManager
        } else {
            val windowManager = ViewCaptureAwareWindowManager(context, parent, windowContextToken)
            val windowManager =
                ViewCaptureAwareWindowManager(
                    context,
                    context.getSystemService(WindowManager::class.java),
                )
            instanceMap[context] = WeakReference(windowManager)
            return windowManager
        }
+86 −1
Original line number Diff line number Diff line
@@ -18,11 +18,23 @@ package com.android.app.viewcapture

import android.content.Context
import android.content.Intent
import android.hardware.display.DisplayManager
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.View
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG
import android.window.WindowContext
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.window.flags.Flags
import com.google.common.truth.Truth.assertWithMessage
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
@@ -38,10 +50,16 @@ class ViewCaptureAwareWindowManagerTest {

    @get:Rule val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent)

    @get:Rule val mSetFlagsRule: SetFlagsRule = SetFlagsRule()

    @Test
    fun testAddView_verifyStartCaptureCall() {
        activityScenarioRule.scenario.onActivity { activity ->
            mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(mContext)
            mViewCaptureAwareWindowManager =
                ViewCaptureAwareWindowManager(
                    mContext,
                    mContext.getSystemService(WindowManager::class.java),
                )

            val activityDecorView = activity.window.decorView
            // removing view since it is already added to view hierarchy on declaration
@@ -55,4 +73,71 @@ class ViewCaptureAwareWindowManagerTest {
            assertTrue(viewCapture.mIsStarted)
        }
    }

    @EnableFlags(Flags.FLAG_ENABLE_WINDOW_CONTEXT_OVERRIDE_TYPE)
    @Test
    fun useWithWindowContext_attachWindow_attachToViewCaptureAwareWm() {
        val windowContext =
            mContext.createWindowContext(
                mContext.getSystemService(DisplayManager::class.java).getDisplay(DEFAULT_DISPLAY),
                TYPE_APPLICATION,
                null, /* options */
            ) as WindowContext

        // Obtain ViewCaptureAwareWindowManager with WindowContext.
        mViewCaptureAwareWindowManager =
            ViewCaptureAwareWindowManagerFactory.getInstance(windowContext)
                as ViewCaptureAwareWindowManager

        // Attach to an Activity so that we can add an application parent window.
        val params = WindowManager.LayoutParams()
        activityScenarioRule.scenario.onActivity { activity ->
            params.token = activity.activityToken
        }

        // Create and attach an application window, and listen to OnAttachStateChangeListener.
        // We need to know when the parent window is attached and then we can add the attached
        // dialog.
        val listener = AttachStateListener()
        val parentWindow = View(windowContext)
        parentWindow.addOnAttachStateChangeListener(listener)
        windowContext.attachWindow(parentWindow)

        // Attach the parent window to ViewCaptureAwareWm
        activityScenarioRule.scenario.onActivity {
            mViewCaptureAwareWindowManager.addView(parentWindow, params)
        }

        // Wait for parent window to be attached.
        listener.mLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)
        assertWithMessage("The WindowContext token must be attached.")
            .that(params.mWindowContextToken)
            .isEqualTo(windowContext.windowContextToken)

        val subWindow = View(windowContext)
        val subParams = WindowManager.LayoutParams(TYPE_APPLICATION_ATTACHED_DIALOG)

        // Attach the sub-window
        activityScenarioRule.scenario.onActivity {
            mViewCaptureAwareWindowManager.addView(subWindow, subParams)
        }

        assertWithMessage("The sub-window must be attached to the parent window")
            .that(subParams.token)
            .isEqualTo(parentWindow.windowToken)
    }

    private class AttachStateListener : View.OnAttachStateChangeListener {
        val mLatch: CountDownLatch = CountDownLatch(1)

        override fun onViewAttachedToWindow(v: View) {
            mLatch.countDown()
        }

        override fun onViewDetachedFromWindow(v: View) {}
    }

    companion object {
        private const val TIMEOUT_IN_SECONDS = 4L
    }
}