Loading core/java/android/app/ContextImpl.java +2 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading core/java/android/view/IWindowManager.aidl +39 −8 Original line number Diff line number Diff line Loading @@ -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. Loading core/java/android/window/WindowContext.java +8 −5 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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 Loading @@ -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(); } Loading core/java/android/window/WindowContextController.java +53 −16 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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(); Loading @@ -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(); } Loading core/tests/coretests/src/android/window/WindowContextControllerTest.java +19 −14 Original line number Diff line number Diff line Loading @@ -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
core/java/android/app/ContextImpl.java +2 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading
core/java/android/view/IWindowManager.aidl +39 −8 Original line number Diff line number Diff line Loading @@ -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. Loading
core/java/android/window/WindowContext.java +8 −5 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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 Loading @@ -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(); } Loading
core/java/android/window/WindowContextController.java +53 −16 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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(); Loading @@ -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(); } Loading
core/tests/coretests/src/android/window/WindowContextControllerTest.java +19 −14 Original line number Diff line number Diff line Loading @@ -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()); } }