Loading core/java/android/view/IWindowManager.aidl +21 −0 Original line number Diff line number Diff line Loading @@ -932,6 +932,27 @@ interface IWindowManager */ void detachWindowContext(IBinder clientToken); /** * Reparents the {@link android.window.WindowContext} to the * {@link com.android.server.wm.DisplayArea} on another display. * This method also reparent the WindowContext associated WindowToken to another display if * necessary. * <p> * {@code type} and {@code options} must be the same as the previous call of * {@link #attachWindowContextToDisplayArea} on the same Context otherwise this will fail * silently. * * @param appThread the process that the window context is on. * @param clientToken the window context's token * @param type The window type of the WindowContext * @param displayId The new display id this context windows should be parented to * @param options Bundle the context was created with * * @return True if the operation was successful, False otherwise. */ boolean reparentWindowContextToDisplayArea(in IApplicationThread appThread, IBinder clientToken, int displayId); /** * Registers a listener, which is to be called whenever cross-window blur is enabled/disabled. * Loading core/java/android/window/WindowContext.java +12 −11 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.view.Display; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.window.flags.Flags; import java.lang.ref.Reference; Loading Loading @@ -95,20 +96,20 @@ public class WindowContext extends ContextWrapper implements WindowProvider { } /** * Updates this context to a new displayId. * Moves this context to another display. * <p> * Note that this doesn't re-parent previously attached windows (they should be removed and * re-added manually after this is called). Resources associated with this context will have * the correct value and configuration for the new display after this is called. * Note that this re-parents all the previously attached windows. Resources associated with this * context will have the correct value and configuration for the new display after this is * called. */ @Override public void updateDisplay(int displayId) { public void reparentToDisplay(int displayId) { if (Flags.reparentWindowTokenApi()) { if (displayId == getDisplayId()) { return; } super.updateDisplay(displayId); mController.detachIfNeeded(); mController.attachToDisplayArea(mType, displayId, mOptions); mController.reparentToDisplayArea(mType, displayId, mOptions); } } @Override Loading core/java/android/window/WindowContextController.java +15 −0 Original line number Diff line number Diff line Loading @@ -158,6 +158,21 @@ public class WindowContextController { } } /** * Reparents the window context from the current attached display to another. {@code type} and * {@code options} must be the same as the previous attach call, otherwise this will fail * silently. */ public void reparentToDisplayArea( @WindowType int type, int displayId, @Nullable Bundle options) { if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) { attachToDisplayArea(type, displayId, options); return; } // No need to propagate type and options as this is already attached and they can't change. getWindowTokenClientController().reparentToDisplayArea(mToken, displayId); } /** Gets the {@link WindowTokenClientController}. */ @VisibleForTesting @NonNull Loading core/java/android/window/WindowTokenClientController.java +15 −0 Original line number Diff line number Diff line Loading @@ -197,6 +197,21 @@ public class WindowTokenClientController { } } /** * Reparents a {@link WindowTokenClient} and its associated WindowContainer if there's one. */ public void reparentToDisplayArea(@NonNull WindowTokenClient client, int displayId) { try { if (!getWindowManagerService().reparentWindowContextToDisplayArea(mAppThread, client, displayId)) { Log.e(TAG, "Didn't succeed reparenting of " + client + " to displayId=" + displayId); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, @NonNull WindowContextInfo info, boolean shouldReportConfigChange) { recordWindowContextToken(client); Loading core/tests/coretests/src/android/window/WindowContextTest.java +31 −6 Original line number Diff line number Diff line Loading @@ -33,7 +33,9 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; import android.app.EmptyActivity; Loading @@ -48,7 +50,9 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.view.Display; import android.view.IWindowManager; import android.view.View; Loading @@ -64,6 +68,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; Loading Loading @@ -91,6 +96,8 @@ public class WindowContextTest { public ActivityTestRule<EmptyActivity> mActivityRule = new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */, false /* launchActivity */); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); private final WindowContext mWindowContext = createWindowContext(); Loading Loading @@ -340,17 +347,35 @@ public class WindowContextTest { } @Test public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() { @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API) public void reparentToDisplayId_wasAttached_reparentToDisplayAreaPropagatedToTokenController() { final WindowTokenClientController mockWindowTokenClientController = mock(WindowTokenClientController.class); when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(), any())).thenReturn(true); WindowTokenClientController.overrideForTesting(mockWindowTokenClientController); mWindowContext.reparentToDisplay(DEFAULT_DISPLAY + 1); verify(mockWindowTokenClientController).reparentToDisplayArea(any(), /* displayId= */ eq(DEFAULT_DISPLAY + 1) ); } @Test @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API) public void reparentToDisplayId_sameDisplayId_noReparenting() { final WindowTokenClientController mockWindowTokenClientController = mock(WindowTokenClientController.class); when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(), any())).thenReturn(true); WindowTokenClientController.overrideForTesting(mockWindowTokenClientController); mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1); mWindowContext.reparentToDisplay(DEFAULT_DISPLAY); verify(mockWindowTokenClientController).detachIfNeeded(any()); verify(mockWindowTokenClientController).attachToDisplayArea(any(), anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1), any()); verify(mockWindowTokenClientController, never()).reparentToDisplayArea(any(), /* displayId= */ eq(DEFAULT_DISPLAY) ); } private WindowContext createWindowContext() { Loading Loading
core/java/android/view/IWindowManager.aidl +21 −0 Original line number Diff line number Diff line Loading @@ -932,6 +932,27 @@ interface IWindowManager */ void detachWindowContext(IBinder clientToken); /** * Reparents the {@link android.window.WindowContext} to the * {@link com.android.server.wm.DisplayArea} on another display. * This method also reparent the WindowContext associated WindowToken to another display if * necessary. * <p> * {@code type} and {@code options} must be the same as the previous call of * {@link #attachWindowContextToDisplayArea} on the same Context otherwise this will fail * silently. * * @param appThread the process that the window context is on. * @param clientToken the window context's token * @param type The window type of the WindowContext * @param displayId The new display id this context windows should be parented to * @param options Bundle the context was created with * * @return True if the operation was successful, False otherwise. */ boolean reparentWindowContextToDisplayArea(in IApplicationThread appThread, IBinder clientToken, int displayId); /** * Registers a listener, which is to be called whenever cross-window blur is enabled/disabled. * Loading
core/java/android/window/WindowContext.java +12 −11 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.view.Display; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.window.flags.Flags; import java.lang.ref.Reference; Loading Loading @@ -95,20 +96,20 @@ public class WindowContext extends ContextWrapper implements WindowProvider { } /** * Updates this context to a new displayId. * Moves this context to another display. * <p> * Note that this doesn't re-parent previously attached windows (they should be removed and * re-added manually after this is called). Resources associated with this context will have * the correct value and configuration for the new display after this is called. * Note that this re-parents all the previously attached windows. Resources associated with this * context will have the correct value and configuration for the new display after this is * called. */ @Override public void updateDisplay(int displayId) { public void reparentToDisplay(int displayId) { if (Flags.reparentWindowTokenApi()) { if (displayId == getDisplayId()) { return; } super.updateDisplay(displayId); mController.detachIfNeeded(); mController.attachToDisplayArea(mType, displayId, mOptions); mController.reparentToDisplayArea(mType, displayId, mOptions); } } @Override Loading
core/java/android/window/WindowContextController.java +15 −0 Original line number Diff line number Diff line Loading @@ -158,6 +158,21 @@ public class WindowContextController { } } /** * Reparents the window context from the current attached display to another. {@code type} and * {@code options} must be the same as the previous attach call, otherwise this will fail * silently. */ public void reparentToDisplayArea( @WindowType int type, int displayId, @Nullable Bundle options) { if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) { attachToDisplayArea(type, displayId, options); return; } // No need to propagate type and options as this is already attached and they can't change. getWindowTokenClientController().reparentToDisplayArea(mToken, displayId); } /** Gets the {@link WindowTokenClientController}. */ @VisibleForTesting @NonNull Loading
core/java/android/window/WindowTokenClientController.java +15 −0 Original line number Diff line number Diff line Loading @@ -197,6 +197,21 @@ public class WindowTokenClientController { } } /** * Reparents a {@link WindowTokenClient} and its associated WindowContainer if there's one. */ public void reparentToDisplayArea(@NonNull WindowTokenClient client, int displayId) { try { if (!getWindowManagerService().reparentWindowContextToDisplayArea(mAppThread, client, displayId)) { Log.e(TAG, "Didn't succeed reparenting of " + client + " to displayId=" + displayId); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, @NonNull WindowContextInfo info, boolean shouldReportConfigChange) { recordWindowContextToken(client); Loading
core/tests/coretests/src/android/window/WindowContextTest.java +31 −6 Original line number Diff line number Diff line Loading @@ -33,7 +33,9 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; import android.app.EmptyActivity; Loading @@ -48,7 +50,9 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.view.Display; import android.view.IWindowManager; import android.view.View; Loading @@ -64,6 +68,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; Loading Loading @@ -91,6 +96,8 @@ public class WindowContextTest { public ActivityTestRule<EmptyActivity> mActivityRule = new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */, false /* launchActivity */); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); private final WindowContext mWindowContext = createWindowContext(); Loading Loading @@ -340,17 +347,35 @@ public class WindowContextTest { } @Test public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() { @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API) public void reparentToDisplayId_wasAttached_reparentToDisplayAreaPropagatedToTokenController() { final WindowTokenClientController mockWindowTokenClientController = mock(WindowTokenClientController.class); when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(), any())).thenReturn(true); WindowTokenClientController.overrideForTesting(mockWindowTokenClientController); mWindowContext.reparentToDisplay(DEFAULT_DISPLAY + 1); verify(mockWindowTokenClientController).reparentToDisplayArea(any(), /* displayId= */ eq(DEFAULT_DISPLAY + 1) ); } @Test @EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API) public void reparentToDisplayId_sameDisplayId_noReparenting() { final WindowTokenClientController mockWindowTokenClientController = mock(WindowTokenClientController.class); when(mockWindowTokenClientController.attachToDisplayArea(any(), anyInt(), anyInt(), any())).thenReturn(true); WindowTokenClientController.overrideForTesting(mockWindowTokenClientController); mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1); mWindowContext.reparentToDisplay(DEFAULT_DISPLAY); verify(mockWindowTokenClientController).detachIfNeeded(any()); verify(mockWindowTokenClientController).attachToDisplayArea(any(), anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1), any()); verify(mockWindowTokenClientController, never()).reparentToDisplayArea(any(), /* displayId= */ eq(DEFAULT_DISPLAY) ); } private WindowContext createWindowContext() { Loading