Loading core/java/android/view/SurfaceControlViewHost.java +2 −3 Original line number Diff line number Diff line Loading @@ -400,8 +400,7 @@ public class SurfaceControlViewHost { public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), mAccessibilityEmbeddedConnection, mWm.getFocusGrantToken(), mRemoteInterface); mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface); } else { return null; } Loading Loading @@ -507,7 +506,7 @@ public class SurfaceControlViewHost { * @hide */ public IBinder getFocusGrantToken() { return mWm.getFocusGrantToken(); return mWm.getFocusGrantToken(getWindowToken().asBinder()); } private void addWindowToken(WindowManager.LayoutParams attrs) { Loading core/java/android/view/WindowlessWindowManager.java +16 −2 Original line number Diff line number Diff line Loading @@ -106,9 +106,23 @@ public class WindowlessWindowManager implements IWindowSession { mConfiguration.setTo(configuration); } IBinder getFocusGrantToken() { IBinder getFocusGrantToken(IBinder window) { synchronized (this) { // This can only happen if someone requested the focusGrantToken before setView was // called for the SCVH. In that case, use the root focusGrantToken since this will be // the same token sent to WMS for the root window once setView is called. if (mStateForWindow.isEmpty()) { return mFocusGrantToken; } State state = mStateForWindow.get(window); if (state != null) { return state.mFocusGrantToken; } } Log.w(TAG, "Failed to get focusGrantToken. Returning null token"); return null; } /** * Utility API. Loading services/tests/wmtests/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,8 @@ android:showWhenLocked="true"/> <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/> <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" /> <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" android:foregroundServiceType="mediaProjection" android:enabled="true"> Loading services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java 0 → 100644 +194 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.server.wm; import static android.Manifest.permission.ACCESS_SURFACE_FLINGER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible; import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertTrue; import android.app.Activity; import android.app.Instrumentation; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Gravity; import android.view.IWindow; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowlessWindowManager; import android.widget.Button; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; @Presubmit @SmallTest @RunWith(WindowTestRunner.class) public class SurfaceControlViewHostTests { private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>( TestActivity.class); private Instrumentation mInstrumentation; private TestActivity mActivity; private View mView1; private View mView2; private SurfaceControlViewHost mScvh1; private SurfaceControlViewHost mScvh2; @Before public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); // ACCESS_SURFACE_FLINGER is necessary to call waitForWindow // INTERNAL_SYSTEM_WINDOW is necessary to add SCVH with no host parent mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(ACCESS_SURFACE_FLINGER, INTERNAL_SYSTEM_WINDOW); mActivity = mActivityRule.launchActivity(null); } @After public void tearDown() { mInstrumentation.getUiAutomation().dropShellPermissionIdentity(); } @Test public void requestFocusWithMultipleWindows() throws InterruptedException, RemoteException { SurfaceControl sc = new SurfaceControl.Builder() .setName("SurfaceControlViewHostTests") .setCallsite("requestFocusWithMultipleWindows") .build(); mView1 = new Button(mActivity); mView2 = new Button(mActivity); mActivity.runOnUiThread(() -> { TestWindowlessWindowManager wwm = new TestWindowlessWindowManager( mActivity.getResources().getConfiguration(), sc, null); try { mActivity.attachToSurfaceView(sc); } catch (InterruptedException e) { } mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), wwm, "requestFocusWithMultipleWindows"); mScvh2 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), wwm, "requestFocusWithMultipleWindows"); mView1.setBackgroundColor(Color.RED); mView2.setBackgroundColor(Color.BLUE); WindowManager.LayoutParams lp1 = new WindowManager.LayoutParams(200, 200, TYPE_APPLICATION, 0, PixelFormat.OPAQUE); WindowManager.LayoutParams lp2 = new WindowManager.LayoutParams(100, 100, TYPE_APPLICATION, 0, PixelFormat.OPAQUE); mScvh1.setView(mView1, lp1); mScvh2.setView(mView2, lp2); }); assertTrue("Failed to wait for view1", waitForWindowVisible(mView1)); assertTrue("Failed to wait for view2", waitForWindowVisible(mView2)); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, mScvh1.getFocusGrantToken(), true); assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true)); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, mScvh2.getFocusGrantToken(), true); assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true)); } private static class TestWindowlessWindowManager extends WindowlessWindowManager { private final SurfaceControl mRoot; TestWindowlessWindowManager(Configuration c, SurfaceControl rootSurface, IBinder hostInputToken) { super(c, rootSurface, hostInputToken); mRoot = rootSurface; } @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { return mRoot; } } public static class TestActivity extends Activity implements SurfaceHolder.Callback { private SurfaceView mSurfaceView; private final CountDownLatch mSvReadyLatch = new CountDownLatch(1); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final FrameLayout content = new FrameLayout(this); mSurfaceView = new SurfaceView(this); mSurfaceView.setBackgroundColor(Color.BLACK); mSurfaceView.setZOrderOnTop(true); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500, Gravity.LEFT | Gravity.TOP); content.addView(mSurfaceView, lp); setContentView(content); mSurfaceView.getHolder().addCallback(this); } @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { mSvReadyLatch.countDown(); } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { } public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException { mSvReadyLatch.await(); new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl()) .show(sc).apply(); } } } Loading
core/java/android/view/SurfaceControlViewHost.java +2 −3 Original line number Diff line number Diff line Loading @@ -400,8 +400,7 @@ public class SurfaceControlViewHost { public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), mAccessibilityEmbeddedConnection, mWm.getFocusGrantToken(), mRemoteInterface); mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface); } else { return null; } Loading Loading @@ -507,7 +506,7 @@ public class SurfaceControlViewHost { * @hide */ public IBinder getFocusGrantToken() { return mWm.getFocusGrantToken(); return mWm.getFocusGrantToken(getWindowToken().asBinder()); } private void addWindowToken(WindowManager.LayoutParams attrs) { Loading
core/java/android/view/WindowlessWindowManager.java +16 −2 Original line number Diff line number Diff line Loading @@ -106,9 +106,23 @@ public class WindowlessWindowManager implements IWindowSession { mConfiguration.setTo(configuration); } IBinder getFocusGrantToken() { IBinder getFocusGrantToken(IBinder window) { synchronized (this) { // This can only happen if someone requested the focusGrantToken before setView was // called for the SCVH. In that case, use the root focusGrantToken since this will be // the same token sent to WMS for the root window once setView is called. if (mStateForWindow.isEmpty()) { return mFocusGrantToken; } State state = mStateForWindow.get(window); if (state != null) { return state.mFocusGrantToken; } } Log.w(TAG, "Failed to get focusGrantToken. Returning null token"); return null; } /** * Utility API. Loading
services/tests/wmtests/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,8 @@ android:showWhenLocked="true"/> <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/> <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" /> <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" android:foregroundServiceType="mediaProjection" android:enabled="true"> Loading
services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java 0 → 100644 +194 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.server.wm; import static android.Manifest.permission.ACCESS_SURFACE_FLINGER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible; import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertTrue; import android.app.Activity; import android.app.Instrumentation; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Gravity; import android.view.IWindow; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowlessWindowManager; import android.widget.Button; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; @Presubmit @SmallTest @RunWith(WindowTestRunner.class) public class SurfaceControlViewHostTests { private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>( TestActivity.class); private Instrumentation mInstrumentation; private TestActivity mActivity; private View mView1; private View mView2; private SurfaceControlViewHost mScvh1; private SurfaceControlViewHost mScvh2; @Before public void setUp() throws Exception { mInstrumentation = InstrumentationRegistry.getInstrumentation(); // ACCESS_SURFACE_FLINGER is necessary to call waitForWindow // INTERNAL_SYSTEM_WINDOW is necessary to add SCVH with no host parent mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(ACCESS_SURFACE_FLINGER, INTERNAL_SYSTEM_WINDOW); mActivity = mActivityRule.launchActivity(null); } @After public void tearDown() { mInstrumentation.getUiAutomation().dropShellPermissionIdentity(); } @Test public void requestFocusWithMultipleWindows() throws InterruptedException, RemoteException { SurfaceControl sc = new SurfaceControl.Builder() .setName("SurfaceControlViewHostTests") .setCallsite("requestFocusWithMultipleWindows") .build(); mView1 = new Button(mActivity); mView2 = new Button(mActivity); mActivity.runOnUiThread(() -> { TestWindowlessWindowManager wwm = new TestWindowlessWindowManager( mActivity.getResources().getConfiguration(), sc, null); try { mActivity.attachToSurfaceView(sc); } catch (InterruptedException e) { } mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), wwm, "requestFocusWithMultipleWindows"); mScvh2 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), wwm, "requestFocusWithMultipleWindows"); mView1.setBackgroundColor(Color.RED); mView2.setBackgroundColor(Color.BLUE); WindowManager.LayoutParams lp1 = new WindowManager.LayoutParams(200, 200, TYPE_APPLICATION, 0, PixelFormat.OPAQUE); WindowManager.LayoutParams lp2 = new WindowManager.LayoutParams(100, 100, TYPE_APPLICATION, 0, PixelFormat.OPAQUE); mScvh1.setView(mView1, lp1); mScvh2.setView(mView2, lp2); }); assertTrue("Failed to wait for view1", waitForWindowVisible(mView1)); assertTrue("Failed to wait for view2", waitForWindowVisible(mView2)); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, mScvh1.getFocusGrantToken(), true); assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true)); WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, mScvh2.getFocusGrantToken(), true); assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true)); } private static class TestWindowlessWindowManager extends WindowlessWindowManager { private final SurfaceControl mRoot; TestWindowlessWindowManager(Configuration c, SurfaceControl rootSurface, IBinder hostInputToken) { super(c, rootSurface, hostInputToken); mRoot = rootSurface; } @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { return mRoot; } } public static class TestActivity extends Activity implements SurfaceHolder.Callback { private SurfaceView mSurfaceView; private final CountDownLatch mSvReadyLatch = new CountDownLatch(1); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); final FrameLayout content = new FrameLayout(this); mSurfaceView = new SurfaceView(this); mSurfaceView.setBackgroundColor(Color.BLACK); mSurfaceView.setZOrderOnTop(true); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500, Gravity.LEFT | Gravity.TOP); content.addView(mSurfaceView, lp); setContentView(content); mSurfaceView.getHolder().addCallback(this); } @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { mSvReadyLatch.countDown(); } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { } public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException { mSvReadyLatch.await(); new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl()) .show(sc).apply(); } } }