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

Commit b41528c5 authored by Robert Carr's avatar Robert Carr Committed by Rob Carr
Browse files

WM/SurfacePackage: Forward insets to WindowContainer overlays

We add support for forwarding InsetsState over SurfacePackage
to reach embedded ViewRootImpl. As a next step we hook up
forwarding of insets from the main visible window of a task
with overlays, to said overlays. We provide a frame so that
the remote ViewRootImpl can interpret the insets relative
to this frame. In the TaskOverlay case everything in the
remote SurfaceControlViewHost is in a world relative to the
task bounds, and so we send the task bounds. Transient insets
are also forwarded via this channel. This is due to the
specific requirements of game service overlay, but seems
to make sense in general for overlays, who will need to be able
to intelligently handle immersive mode.

Bug: 213603716
Bug: 214239892
Test: SurfaceControlWindowInsetsTest
Change-Id: I22e22050a5dec71d8120ef4fea4a1c9bc452e02f
parent 501ee91b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package android.view;

import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.InsetsState;

/**
 * API from content embedder back to embedded content in SurfaceControlViewHost
@@ -25,4 +27,5 @@ import android.content.res.Configuration;
oneway interface ISurfaceControlViewHost {
    void onConfigurationChanged(in Configuration newConfig);
    void onDispatchDetachedFromWindow();
    void onInsetsChanged(in InsetsState state, in Rect insetFrame);
}
+12 −0
Original line number Diff line number Diff line
@@ -22,10 +22,12 @@ import android.annotation.TestApi;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.view.InsetsState;

import java.util.Objects;

@@ -71,6 +73,16 @@ public class SurfaceControlViewHost {
                release();
            });
        }

        @Override
        public void onInsetsChanged(InsetsState state, Rect frame) {
            if (mViewRoot != null) {
                mViewRoot.mHandler.post(() -> {
                    mViewRoot.setOverrideInsetsFrame(frame);
                });
            }
            mWm.setInsetsState(state);
        }
    }

    private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
+17 −1
Original line number Diff line number Diff line
@@ -643,6 +643,7 @@ public final class ViewRootImpl implements ViewParent,

    // These are accessed by multiple threads.
    final Rect mWinFrame; // frame given by window manager.
    Rect mOverrideInsetsFrame;

    final Rect mPendingBackDropFrame = new Rect();

@@ -8069,7 +8070,22 @@ public final class ViewRootImpl implements ViewParent,

    private void setFrame(Rect frame) {
        mWinFrame.set(frame);
        mInsetsController.onFrameChanged(frame);
        mInsetsController.onFrameChanged(mOverrideInsetsFrame != null ?
            mOverrideInsetsFrame : frame);
    }

    /**
     * In the normal course of operations we compute insets relative to
     * the frame returned from relayout window. In the case of
     * SurfaceControlViewHost, this frame is in local coordinates
     * instead of global coordinates. We support this override
     * frame so we can allow SurfaceControlViewHost to set a frame
     * to be used to calculate insets, without disturbing the main
     * mFrame.
     */
    void setOverrideInsetsFrame(Rect frame) {
        mOverrideInsetsFrame = new Rect(frame);
        mInsetsController.onFrameChanged(mOverrideInsetsFrame);
    }

    /**
+22 −2
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.MergedConfiguration;
import android.view.InsetsState;
import android.view.IWindow;
import android.window.ClientWindowFrames;
import android.window.IOnBackInvokedCallback;

@@ -49,12 +51,14 @@ public class WindowlessWindowManager implements IWindowSession {
        int mDisplayId;
        IBinder mInputChannelToken;
        Region mInputRegion;
        IWindow mClient;
        State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId,
                IBinder inputChannelToken) {
              IBinder inputChannelToken, IWindow client) {
            mSurfaceControl = sc;
            mParams.copyFrom(p);
            mDisplayId = displayId;
            mInputChannelToken = inputChannelToken;
            mClient = client;
        }
    };

@@ -77,6 +81,7 @@ public class WindowlessWindowManager implements IWindowSession {
    private final IWindowSession mRealWm;
    private final IBinder mHostInputToken;
    private final IBinder mFocusGrantToken = new Binder();
    private InsetsState mInsetsState;

    private int mForceHeight = -1;
    private int mForceWidth = -1;
@@ -170,7 +175,7 @@ public class WindowlessWindowManager implements IWindowSession {
        }

        final State state = new State(sc, attrs, displayId,
                outInputChannel != null ? outInputChannel.getToken() : null);
            outInputChannel != null ? outInputChannel.getToken() : null, window);
        synchronized (this) {
            mStateForWindow.put(window.asBinder(), state);
        }
@@ -318,6 +323,10 @@ public class WindowlessWindowManager implements IWindowSession {
            }
        }

        if (mInsetsState != null) {
            outInsetsState.set(mInsetsState);
        }

        // Include whether the window is in touch mode.
        return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
    }
@@ -507,4 +516,15 @@ public class WindowlessWindowManager implements IWindowSession {
    public boolean dropForAccessibility(IWindow window, int x, int y) {
        return false;
    }

    public void setInsetsState(InsetsState state) {
        mInsetsState = state;
        for (State s : mStateForWindow.values()) {
            try {
                s.mClient.insetsChanged(state, false, false);
            } catch (RemoteException e) {
                // Too bad
            }
        }
    }
}
+122 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 android.view;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import static android.view.InsetsState.ITYPE_STATUS_BAR;

import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

@Presubmit
@RunWith(AndroidJUnit4.class)
public class SurfaceControlViewHostInsetsTest {
    SurfaceControlViewHost mSurfaceControlViewHost;
    private boolean mStatusBarIsVisible = false;
    private Insets mStatusBarInsets;
    private Instrumentation mInstrumentation;

    private void createViewHierarchy() {
        Context context = mInstrumentation.getTargetContext();

        View v = new View(context);
        v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
                public WindowInsets onApplyWindowInsets(View v, WindowInsets w) {
                    mStatusBarIsVisible = w.isVisible(WindowInsets.Type.statusBars());
                    mStatusBarInsets = w.getInsets(WindowInsets.Type.statusBars());
                    return w;
                }
        });
        mSurfaceControlViewHost = new SurfaceControlViewHost(context,
            context.getDisplayNoVerify(), new Binder());
        mSurfaceControlViewHost.setView(v, 100, 100);
    }

    @Before
    public void setup() {
        mInstrumentation = InstrumentationRegistry.getInstrumentation();
        mInstrumentation.runOnMainSync(() -> { createViewHierarchy(); });
        mInstrumentation.waitForIdleSync();
    }

    private InsetsState statusBarState(boolean visible) {
        final InsetsState insetsState = new InsetsState();
        insetsState.setDisplayFrame(new Rect(0, 0, 1000, 1000));
        insetsState.getSource(ITYPE_STATUS_BAR).setVisible(visible);
        insetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 10));
        return insetsState;
    }

    private InsetsState statusBarVisibleState() {
        return statusBarState(true);
    }

    private void sendInsetsSync(InsetsState s, Rect f) {
        try  {
            mSurfaceControlViewHost.getSurfacePackage().getRemoteInterface()
                .onInsetsChanged(s, f);
        } catch (Exception e) {
        }
        mInstrumentation.waitForIdleSync();
    }

    @Test
    public void sendInsetsToSurfaceControlViewHost() {
        final InsetsState insetsState = statusBarVisibleState();
        sendInsetsSync(insetsState, new Rect(0, 0, 100, 100));
        assertTrue(mStatusBarIsVisible);

        final InsetsState insetsState2 = statusBarState(false);
        sendInsetsSync(insetsState2, new Rect(0, 0, 100, 100));
        assertFalse(mStatusBarIsVisible);
   }

    @Test
    public void insetsAreRelativeToFrame() {
        final InsetsState insetsState = statusBarVisibleState();
        sendInsetsSync(insetsState, new Rect(0, 0, 100, 100));

        assertTrue(mStatusBarIsVisible);
        assertEquals(10, mStatusBarInsets.top);

        sendInsetsSync(insetsState, new Rect(0, 5, 100, 100));
        assertEquals(5, mStatusBarInsets.top);
    }
}
Loading