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

Commit 030a7546 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "Enable WindowContext to associate with WindowToken" into sc-dev

parents 6e62271c a5b660e3
Loading
Loading
Loading
Loading
+2 −3
Original line number Original line Diff line number Diff line
@@ -2641,9 +2641,8 @@ class ContextImpl extends Context {
        //         WindowContext's resources.
        //         WindowContext's resources.
        windowTokenClient.attachContext(windowContext);
        windowTokenClient.attachContext(windowContext);


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


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


    /**
    /**
     * Registers a listener for a {@link android.window.WindowContext} to handle configuration
     * Attaches a {@link android.window.WindowContext} to the DisplayArea specified by {@code type},
     * changes from the server side.
     * {@code displayId} and {@code options}.
     * <p>
     * <p>
     * Note that this API should be invoked after calling
     * Note that this API should be invoked after calling
     * {@link android.window.WindowTokenClient#attachContext(Context)}
     * {@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 type Window type of the window context
     * @param displayId The display associated with 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
     * @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);
            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
     * @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.
     * Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
+8 −5
Original line number Original line Diff line number Diff line
@@ -57,6 +57,8 @@ public class WindowContext extends ContextWrapper {
     *
     *
     * @param base Base {@link Context} for this new instance.
     * @param base Base {@link Context} for this new instance.
     * @param type Window type to be used with this context.
     * @param type Window type to be used with this context.
     * @param options A bundle used to pass window-related options.
     *
     * @hide
     * @hide
     */
     */
    public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
    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}
     * Attaches this {@link WindowContext} to the {@link com.android.server.wm.DisplayArea}
     * to receive configuration changes of the associated {@link WindowManager} node.
     * specified by {@code mType}, {@link #getDisplayId() display ID} and {@code mOptions}
     * to receive configuration changes.
     */
     */
    public void registerWithServer() {
    public void attachToDisplayArea() {
        mController.registerListener(mType, getDisplayId(), mOptions);
        mController.attachToDisplayArea(mType, getDisplayId(), mOptions);
    }
    }


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


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


/**
/**
 * The controller to manage {@link WindowContext} listener, such as registering and unregistering
 * The controller to manage {@link WindowContext}, such as attaching to a window manager node or
 * the listener.
 * 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
 * @hide
 */
 */
public class WindowContextController {
public class WindowContextController {
    private final IWindowManager mWms;
    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
    @VisibleForTesting
    public boolean mListenerRegistered;
    public boolean mAttachedToDisplayArea;
    @NonNull
    @NonNull
    private final IBinder mToken;
    private final IBinder mToken;


    /**
    /**
     * Window Context Controller constructor
     * 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()}.
     *              {@link Context#getWindowContextToken()}.
     */
     */
    public WindowContextController(@NonNull IBinder token) {
    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 type The window type of the {@link WindowContext}
     * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
     * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
     * @param options The window context launched option
     * @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) {
    public void attachToDisplayArea(@WindowType int type, int displayId, @Nullable Bundle options) {
        if (mListenerRegistered) {
        if (mAttachedToDisplayArea) {
            throw new UnsupportedOperationException("A Window Context can only register a listener"
            throw new IllegalStateException("A Window Context can be only attached to "
                    + " once.");
                    + "a DisplayArea once.");
        }
        }
        try {
        try {
            mListenerRegistered = mWms.registerWindowContextListener(mToken, type, displayId,
            mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId,
                    options);
                    options);
        }  catch (RemoteException e) {
        }  catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
@@ -80,14 +89,42 @@ public class WindowContextController {
    }
    }


    /**
    /**
     * Unregisters the window context listener associated with the {@code mToken} if it has been
     * Switches to attach the window context to a window token.
     * registered.
     * <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() {
    public void attachToWindowToken(IBinder windowToken) {
        if (mListenerRegistered) {
        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 {
            try {
                mWms.unregisterWindowContextListener(mToken);
                mWms.detachWindowContextFromWindowContainer(mToken);
                mListenerRegistered = false;
                mAttachedToDisplayArea = false;
            } catch (RemoteException e) {
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
                throw e.rethrowFromSystemServer();
            }
            }
+19 −14
Original line number Original line Diff line number Diff line
@@ -60,34 +60,39 @@ public class WindowContextControllerTest {
        mMockWms = mock(IWindowManager.class);
        mMockWms = mock(IWindowManager.class);
        mController = new WindowContextController(new Binder(), mMockWms);
        mController = new WindowContextController(new Binder(), mMockWms);


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


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


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


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


    @Test
    @Test
    public void testRegisterAndUnRegisterListener() {
    public void testAttachAndDetachDisplayArea() {
        mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
        mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
                null /* options */);
                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