Merge "Use correct focus token for SCVH#getFocusGrantToken" into udc-dev
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index e9984da..16c1335 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -400,8 +400,7 @@
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;
}
@@ -507,7 +506,7 @@
* @hide
*/
public IBinder getFocusGrantToken() {
- return mWm.getFocusGrantToken();
+ return mWm.getFocusGrantToken(getWindowToken().asBinder());
}
private void addWindowToken(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 9868144..96bfb2d 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -106,8 +106,22 @@
mConfiguration.setTo(configuration);
}
- IBinder getFocusGrantToken() {
- return mFocusGrantToken;
+ 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;
}
/**
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 2696d2b..f12b53a 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -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">
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
new file mode 100644
index 0000000..41bfc80
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -0,0 +1,194 @@
+/*
+ * 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();
+ }
+ }
+}
+