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