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

Commit 5b35502e authored by archisha's avatar archisha
Browse files

Adding a wrapper on Window Manager to add ViewCapture tracing to new

windows.

Bug: 336521992
Flag: NONE Made library changes.
Test: Tested locally

Change-Id: I2ca3720a2482796b0ac754f40bb22ccdaee44006
parent 4a696ae8
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ android_test {
        "androidx.test.ext.junit",
        "androidx.test.rules",
        "testables",
        "mockito-kotlin2",
        "mockito-target-extended-minus-junit4",
    ],
    srcs: [
@@ -71,5 +72,8 @@ android_test {
        "android.test.base",
        "android.test.mock",
    ],
    jni_libs: [
        "libdexmakerjvmtiagent",
    ],
    test_suites: ["device-tests"],
}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.viewcapture

import android.media.permission.SafeCloseable
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager

/** Tag for debug logging. */
private const val TAG = "ViewCaptureWindowManager"

/**
 * Wrapper class for [WindowManager]. Adds [ViewCapture] to associated window when it is added to
 * view hierarchy.
 */
class ViewCaptureAwareWindowManager(
    private val windowManager: WindowManager,
    private val lazyViewCapture: Lazy<ViewCapture>,
) : WindowManager by windowManager {

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

    override fun addView(view: View, params: ViewGroup.LayoutParams?) {
        windowManager.addView(view, params)
        val viewCaptureCloseable: SafeCloseable =
            lazyViewCapture.value.startCapture(view, getViewName(view))
        viewCaptureCloseableMap[view] = viewCaptureCloseable
    }

    override fun removeView(view: View?) {
        removeViewFromCloseableMap(view)
        windowManager.removeView(view)
    }

    override fun removeViewImmediate(view: View?) {
        removeViewFromCloseableMap(view)
        windowManager.removeViewImmediate(view)
    }

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

    private fun removeViewFromCloseableMap(view: View?) {
        if (viewCaptureCloseableMap.containsKey(view)) {
            viewCaptureCloseableMap[view]?.close()
            viewCaptureCloseableMap.remove(view)
        } else {
            Log.wtf(TAG, "removeView called with view not present in closeable map!")
        }
    }
}
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.viewcapture

import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import android.view.WindowManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.invocation.InvocationOnMock
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

@RunWith(AndroidTestingRunner::class)
@SmallTest
class ViewCaptureAwareWindowManagerTest {
    private val context: Context = ApplicationProvider.getApplicationContext()
    private val mockRootView = mock<View>()
    private val windowManager = mock<WindowManager>()
    private val viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context))
    private val lazyViewCapture = mock<Lazy<ViewCapture>> { on { value } doReturn viewCaptureSpy }
    private var mViewCaptureAwareWindowManager: ViewCaptureAwareWindowManager? = null

    @Before
    fun setUp() {
        doAnswer { invocation: InvocationOnMock ->
                val view = invocation.getArgument<View>(0)
                val lp = invocation.getArgument<WindowManager.LayoutParams>(1)
                view.layoutParams = lp
                null
            }
            .`when`(windowManager)
            .addView(any(View::class.java), any(WindowManager.LayoutParams::class.java))
        `when`(mockRootView.context).thenReturn(context)

        mViewCaptureAwareWindowManager =
            ViewCaptureAwareWindowManager(windowManager, lazyViewCapture)
    }

    @Test
    fun testAddView_verifyStartCaptureCall() {
        mViewCaptureAwareWindowManager?.addView(mockRootView, mockRootView.layoutParams)
        verify(viewCaptureSpy).startCapture(any(), anyString())
    }
}