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

Commit ddb4c83f authored by Charles Chen's avatar Charles Chen Committed by Automerger Merge Worker
Browse files

Merge "Enable WindowContext to associate with WindowToken" into sc-dev am: 030a7546

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14018005

Change-Id: I292abff399ff48f43a5d99b3033ad900c15b9d5c
parents 72984973 030a7546
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -2641,9 +2641,8 @@ class ContextImpl extends Context {
        //         WindowContext's resources.
        windowTokenClient.attachContext(windowContext);

        // Step 5. Register the window context's token to the server side to associate with a
        //         window manager node.
        windowContext.registerWithServer();
        // Step 5. Associate the WindowContext's token to a DisplayArea.
        windowContext.attachToDisplayArea();

        return windowContext;
    }
+39 −8
Original line number Diff line number Diff line
@@ -786,29 +786,60 @@ interface IWindowManager
     void setDisplayHashThrottlingEnabled(boolean enable);

    /**
     * Registers a listener for a {@link android.window.WindowContext} to handle configuration
     * changes from the server side.
     * Attaches a {@link android.window.WindowContext} to the DisplayArea specified by {@code type},
     * {@code displayId} and {@code options}.
     * <p>
     * Note that this API should be invoked after calling
     * {@link android.window.WindowTokenClient#attachContext(Context)}
     * </p>
     * </p><p>
     * Generally, this API is used for initializing a {@link android.window.WindowContext}
     * before obtaining a valid {@link com.android.server.wm.WindowToken}. A WindowToken is usually
     * generated when calling {@link android.view.WindowManager#addView(View, LayoutParams)}, or
     * obtained from {@link android.view.WindowManager.LayoutParams#token}.
     * </p><p>
     * In some cases, the WindowToken is passed from the server side because it is managed by the
     * system server. {@link #attachWindowContextToWindowToken(IBinder, IBinder)} could be used in
     * this case to attach the WindowContext to the WindowToken.</p>
     *
     * @param clientToken the window context's token
     * @param clientToken {@link android.window.WindowContext#getWindowContextToken()
     * the WindowContext's token}
     * @param type Window type of the window context
     * @param displayId The display associated with the window context
     * @param options A bundle used to pass window-related options and choose the right DisplayArea
     *
     * @return {@code true} if the listener was registered successfully.
     * @return {@code true} if the WindowContext is attached to the DisplayArea successfully.
     */
    boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
    boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
            in Bundle options);

    /**
     * Unregisters a listener which registered with {@link #registerWindowContextListener()}.
     * Attaches a {@link android.window.WindowContext} to a {@code WindowToken}.
     * <p>
     * This API is used when we hold a valid WindowToken and want to associate with the token and
     * receive its configuration updates.
     * </p><p>
     * Note that this API should be invoked after calling
     * {@link android.window.WindowTokenClient#attachContext(Context)}
     * </p>
     *
     * @param clientToken {@link android.window.WindowContext#getWindowContextToken()
     * the WindowContext's token}
     * @param token the WindowToken to attach
     *
     * @throws IllegalArgumentException if the {@code clientToken} have not been attached to
     * the server or the WindowContext's type doesn't match WindowToken {@code token}'s type.
     *
     * @see #attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
     */
    void attachWindowContextToWindowToken(IBinder clientToken, IBinder token);

    /**
     * Detaches {@link android.window.WindowContext} from the window manager node it's currently
     * attached to. It is no-op if the WindowContext is not attached to a window manager node.
     *
     * @param clientToken the window context's token
     */
    void unregisterWindowContextListener(IBinder clientToken);
    void detachWindowContextFromWindowContainer(IBinder clientToken);

    /**
     * Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
+8 −5
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ public class WindowContext extends ContextWrapper {
     *
     * @param base Base {@link Context} for this new instance.
     * @param type Window type to be used with this context.
     * @param options A bundle used to pass window-related options.
     *
     * @hide
     */
    public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
@@ -72,11 +74,12 @@ public class WindowContext extends ContextWrapper {
    }

    /**
     * Registers this {@link WindowContext} with {@link com.android.server.wm.WindowManagerService}
     * to receive configuration changes of the associated {@link WindowManager} node.
     * Attaches this {@link WindowContext} to the {@link com.android.server.wm.DisplayArea}
     * specified by {@code mType}, {@link #getDisplayId() display ID} and {@code mOptions}
     * to receive configuration changes.
     */
    public void registerWithServer() {
        mController.registerListener(mType, getDisplayId(), mOptions);
    public void attachToDisplayArea() {
        mController.attachToDisplayArea(mType, getDisplayId(), mOptions);
    }

    @Override
@@ -96,7 +99,7 @@ public class WindowContext extends ContextWrapper {
    /** Used for test to invoke because we can't invoke finalize directly. */
    @VisibleForTesting
    public void release() {
        mController.unregisterListenerIfNeeded();
        mController.detachIfNeeded();
        destroy();
    }

+53 −16
Original line number Diff line number Diff line
@@ -29,22 +29,29 @@ import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;

/**
 * The controller to manage {@link WindowContext} listener, such as registering and unregistering
 * the listener.
 * The controller to manage {@link WindowContext}, such as attaching to a window manager node or
 * detaching from the current attached node. The user must call
 * {@link #attachToDisplayArea(int, int, Bundle)}, call {@link #attachToWindowToken(IBinder)}
 * after that if necessary, and then call {@link #detachIfNeeded()} for release.
 *
 * @hide
 */
public class WindowContextController {
    private final IWindowManager mWms;
    /**
     * {@code true} to indicate that the {@code mToken} is associated with a
     * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a
     * WindowToken after this flag sets to {@code true}.
     */
    @VisibleForTesting
    public boolean mListenerRegistered;
    public boolean mAttachedToDisplayArea;
    @NonNull
    private final IBinder mToken;

    /**
     * Window Context Controller constructor
     *
     * @param token The token to register to the window context listener. It is usually from
     * @param token The token used to attach to a window manager node. It is usually from
     *              {@link Context#getWindowContextToken()}.
     */
    public WindowContextController(@NonNull IBinder token) {
@@ -60,19 +67,21 @@ public class WindowContextController {
    }

    /**
     * Registers the {@code mToken} to the window context listener.
     * Attaches the {@code mToken} to a {@link com.android.server.wm.DisplayArea}.
     *
     * @param type The window type of the {@link WindowContext}
     * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
     * @param options The window context launched option
     * @throws IllegalStateException if the {@code mToken} has already been attached to a
     * DisplayArea.
     */
    public void registerListener(@WindowType int type, int displayId,  @Nullable Bundle options) {
        if (mListenerRegistered) {
            throw new UnsupportedOperationException("A Window Context can only register a listener"
                    + " once.");
    public void attachToDisplayArea(@WindowType int type, int displayId, @Nullable Bundle options) {
        if (mAttachedToDisplayArea) {
            throw new IllegalStateException("A Window Context can be only attached to "
                    + "a DisplayArea once.");
        }
        try {
            mListenerRegistered = mWms.registerWindowContextListener(mToken, type, displayId,
            mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId,
                    options);
        }  catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
@@ -80,14 +89,42 @@ public class WindowContextController {
    }

    /**
     * Unregisters the window context listener associated with the {@code mToken} if it has been
     * registered.
     * Switches to attach the window context to a window token.
     * <p>
     * Note that the context should have been attached to a
     * {@link com.android.server.wm.DisplayArea} by {@link #attachToDisplayArea(int, int, Bundle)}
     * before attaching to a window token, and the window token's type must match the window
     * context's type.
     * </p><p>
     * A {@link WindowContext} can only attach to a specific window manager node, which is either a
     * {@link com.android.server.wm.DisplayArea} by calling
     * {@link #attachToDisplayArea(int, int, Bundle)} or the latest attached {@code windowToken}
     * although this API is allowed to be called multiple times.
     * </p>
     * @throws IllegalStateException if the {@code mClientToken} has not yet attached to
     * a {@link com.android.server.wm.DisplayArea} by
     * {@link #attachToDisplayArea(int, int, Bundle)}.
     *
     * @see IWindowManager#attachWindowContextToWindowToken(IBinder, IBinder)
     */
    public void unregisterListenerIfNeeded() {
        if (mListenerRegistered) {
    public void attachToWindowToken(IBinder windowToken) {
        if (!mAttachedToDisplayArea) {
            throw new IllegalStateException("The Window Context should have been attached"
                    + " to a DisplayArea.");
        }
        try {
            mWms.attachWindowContextToWindowToken(mToken, windowToken);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /** Detaches the window context from the node it's currently associated with. */
    public void detachIfNeeded() {
        if (mAttachedToDisplayArea) {
            try {
                mWms.unregisterWindowContextListener(mToken);
                mListenerRegistered = false;
                mWms.detachWindowContextFromWindowContainer(mToken);
                mAttachedToDisplayArea = false;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
+19 −14
Original line number Diff line number Diff line
@@ -60,34 +60,39 @@ public class WindowContextControllerTest {
        mMockWms = mock(IWindowManager.class);
        mController = new WindowContextController(new Binder(), mMockWms);

        doReturn(true).when(mMockWms).registerWindowContextListener(
                any(), anyInt(), anyInt(), any());
        doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(),
                anyInt(), any());
    }

    @Test(expected = UnsupportedOperationException.class)
    public void testRegisterListenerTwiceThrowException() {
        mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
    @Test(expected = IllegalStateException.class)
    public void testAttachToDisplayAreaTwiceThrowException() {
        mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
                null /* options */);
        mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
        mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
                null /* options */);
    }

    @Test
    public void testUnregisterListenerIfNeeded_NotRegisteredYet_DoNothing() throws Exception {
        mController.unregisterListenerIfNeeded();
    public void testDetachIfNeeded_NotAttachedYet_DoNothing() throws Exception {
        mController.detachIfNeeded();

        verify(mMockWms, never()).registerWindowContextListener(any(), anyInt(), anyInt(), any());
        verify(mMockWms, never()).detachWindowContextFromWindowContainer(any());
    }

    @Test
    public void testRegisterAndUnRegisterListener() {
        mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
    public void testAttachAndDetachDisplayArea() {
        mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
                null /* options */);

        assertThat(mController.mListenerRegistered).isTrue();
        assertThat(mController.mAttachedToDisplayArea).isTrue();

        mController.unregisterListenerIfNeeded();
        mController.detachIfNeeded();

        assertThat(mController.mListenerRegistered).isFalse();
        assertThat(mController.mAttachedToDisplayArea).isFalse();
    }

    @Test(expected = IllegalStateException.class)
    public void testAttachToWindowTokenBeforeAttachingToDAThrowException() {
        mController.attachToWindowToken(new Binder());
    }
}
Loading