Loading services/core/java/com/android/server/wm/ActivityClientController.java +8 −6 Original line number Diff line number Diff line Loading @@ -841,22 +841,22 @@ class ActivityClientController extends IActivityClientController.Stub { /** * Requests that an activity should enter picture-in-picture mode if possible. This method may * be used by the implementation of non-phone form factors. * * @return false if the activity cannot enter PIP mode. */ void requestPictureInPictureMode(@NonNull ActivityRecord r) { boolean requestPictureInPictureMode(@NonNull ActivityRecord r) { if (r.inPinnedWindowingMode()) { throw new IllegalStateException("Activity is already in PIP mode"); return false; } final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( "requestPictureInPictureMode", /* beforeStopping */ false); if (!canEnterPictureInPicture) { throw new IllegalStateException( "Requested PIP on an activity that doesn't support it"); return false; } if (r.pictureInPictureArgs.isAutoEnterEnabled()) { mService.enterPictureInPictureMode(r, r.pictureInPictureArgs); return; return mService.enterPictureInPictureMode(r, r.pictureInPictureArgs); } try { Loading @@ -864,9 +864,11 @@ class ActivityClientController extends IActivityClientController.Stub { r.app.getThread(), r.token); transaction.addCallback(EnterPipRequestedItem.obtain()); mService.getLifecycleManager().scheduleTransaction(transaction); return true; } catch (Exception e) { Slog.w(TAG, "Failed to send enter pip requested item: " + r.intent.getComponent(), e); return false; } } Loading services/core/java/com/android/server/wm/WindowOrganizerController.java +29 −0 Original line number Diff line number Diff line Loading @@ -546,6 +546,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + " windowing mode during locked task mode."); } if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) { // Do not directly put the container into PINNED mode as it may not support it or // the app may not want to enter it. Instead, send a signal to request PIP // mode to the app if they wish to support it below in #applyTaskChanges. return effects; } final int prevMode = container.getWindowingMode(); container.setWindowingMode(windowingMode); if (prevMode != container.getWindowingMode()) { Loading Loading @@ -582,6 +589,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds); } if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED && !tr.inPinnedWindowingMode()) { final ActivityRecord activity = tr.getTopNonFinishingActivity(); if (activity != null) { final boolean lastSupportsEnterPipOnTaskSwitch = activity.supportsEnterPipOnTaskSwitch; // Temporarily force enable enter PIP on task switch so that PIP is requested // regardless of whether the activity is resumed or paused. activity.supportsEnterPipOnTaskSwitch = true; boolean canEnterPip = activity.checkEnterPictureInPictureState( "applyTaskChanges", true /* beforeStopping */); if (canEnterPip) { canEnterPip = mService.mActivityClientController .requestPictureInPictureMode(activity); } if (!canEnterPip) { // Restore the flag to its previous state when the activity cannot enter PIP. activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch; } } } return effects; } Loading services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +17 −9 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; Loading @@ -42,8 +41,8 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.annotation.Nullable; Loading @@ -60,7 +59,6 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.LocaleList; import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Display; Loading Loading @@ -136,7 +134,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertNull(transaction.getLifecycleStateRequest()); } @Test(expected = IllegalStateException.class) @Test public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); Loading @@ -146,11 +144,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.mActivityClientController.requestPictureInPictureMode(activity); // Check enter no transactions with enter pip requests are made. verify(lifecycleManager, times(0)).scheduleTransaction(any()); verify(lifecycleManager, atLeast(0)) .scheduleTransaction(mClientTransactionCaptor.capture()); final ClientTransaction transaction = mClientTransactionCaptor.getValue(); // Check that none are enter pip request items. transaction.getCallbacks().forEach(clientTransactionItem -> { assertFalse(clientTransactionItem instanceof EnterPipRequestedItem); }); } @Test(expected = IllegalStateException.class) @Test public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); Loading @@ -159,8 +162,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.mActivityClientController.requestPictureInPictureMode(activity); // Check that no transactions with enter pip requests are made. verify(lifecycleManager, times(0)).scheduleTransaction(any()); verify(lifecycleManager, atLeast(0)) .scheduleTransaction(mClientTransactionCaptor.capture()); final ClientTransaction transaction = mClientTransactionCaptor.getValue(); // Check that none are enter pip request items. transaction.getCallbacks().forEach(clientTransactionItem -> { assertFalse(clientTransactionItem instanceof EnterPipRequestedItem); }); } @Test Loading services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +11 −2 Original line number Diff line number Diff line Loading @@ -522,7 +522,10 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode()); // Get the root task from the PIP activity record again, since the PIP root task may have // changed when the activity entered PIP mode. final Task pipRootTask = record.getRootTask(); assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode()); } @Test Loading Loading @@ -1015,6 +1018,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; record.setPictureInPictureParams(new PictureInPictureParams.Builder() .setAutoEnterEnabled(true).build()); spyOn(record); doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); Loading Loading @@ -1499,7 +1504,11 @@ public class WindowOrganizerTests extends WindowTestsBase { verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); clearInvocations(mWm.mAtmService.mRootWindowContainer); t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN); // The token for the PIP root task may have changed when the task entered PIP mode, so do // not reuse the one from above. final WindowContainerToken newToken = record.getRootTask().mRemoteToken.toWindowContainerToken(); t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); } Loading Loading
services/core/java/com/android/server/wm/ActivityClientController.java +8 −6 Original line number Diff line number Diff line Loading @@ -841,22 +841,22 @@ class ActivityClientController extends IActivityClientController.Stub { /** * Requests that an activity should enter picture-in-picture mode if possible. This method may * be used by the implementation of non-phone form factors. * * @return false if the activity cannot enter PIP mode. */ void requestPictureInPictureMode(@NonNull ActivityRecord r) { boolean requestPictureInPictureMode(@NonNull ActivityRecord r) { if (r.inPinnedWindowingMode()) { throw new IllegalStateException("Activity is already in PIP mode"); return false; } final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( "requestPictureInPictureMode", /* beforeStopping */ false); if (!canEnterPictureInPicture) { throw new IllegalStateException( "Requested PIP on an activity that doesn't support it"); return false; } if (r.pictureInPictureArgs.isAutoEnterEnabled()) { mService.enterPictureInPictureMode(r, r.pictureInPictureArgs); return; return mService.enterPictureInPictureMode(r, r.pictureInPictureArgs); } try { Loading @@ -864,9 +864,11 @@ class ActivityClientController extends IActivityClientController.Stub { r.app.getThread(), r.token); transaction.addCallback(EnterPipRequestedItem.obtain()); mService.getLifecycleManager().scheduleTransaction(transaction); return true; } catch (Exception e) { Slog.w(TAG, "Failed to send enter pip requested item: " + r.intent.getComponent(), e); return false; } } Loading
services/core/java/com/android/server/wm/WindowOrganizerController.java +29 −0 Original line number Diff line number Diff line Loading @@ -546,6 +546,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + " windowing mode during locked task mode."); } if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) { // Do not directly put the container into PINNED mode as it may not support it or // the app may not want to enter it. Instead, send a signal to request PIP // mode to the app if they wish to support it below in #applyTaskChanges. return effects; } final int prevMode = container.getWindowingMode(); container.setWindowingMode(windowingMode); if (prevMode != container.getWindowingMode()) { Loading Loading @@ -582,6 +589,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds); } if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED && !tr.inPinnedWindowingMode()) { final ActivityRecord activity = tr.getTopNonFinishingActivity(); if (activity != null) { final boolean lastSupportsEnterPipOnTaskSwitch = activity.supportsEnterPipOnTaskSwitch; // Temporarily force enable enter PIP on task switch so that PIP is requested // regardless of whether the activity is resumed or paused. activity.supportsEnterPipOnTaskSwitch = true; boolean canEnterPip = activity.checkEnterPictureInPictureState( "applyTaskChanges", true /* beforeStopping */); if (canEnterPip) { canEnterPip = mService.mActivityClientController .requestPictureInPictureMode(activity); } if (!canEnterPip) { // Restore the flag to its previous state when the activity cannot enter PIP. activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch; } } } return effects; } Loading
services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +17 −9 Original line number Diff line number Diff line Loading @@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; Loading @@ -42,8 +41,8 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.annotation.Nullable; Loading @@ -60,7 +59,6 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.LocaleList; import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Display; Loading Loading @@ -136,7 +134,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertNull(transaction.getLifecycleStateRequest()); } @Test(expected = IllegalStateException.class) @Test public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); Loading @@ -146,11 +144,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.mActivityClientController.requestPictureInPictureMode(activity); // Check enter no transactions with enter pip requests are made. verify(lifecycleManager, times(0)).scheduleTransaction(any()); verify(lifecycleManager, atLeast(0)) .scheduleTransaction(mClientTransactionCaptor.capture()); final ClientTransaction transaction = mClientTransactionCaptor.getValue(); // Check that none are enter pip request items. transaction.getCallbacks().forEach(clientTransactionItem -> { assertFalse(clientTransactionItem instanceof EnterPipRequestedItem); }); } @Test(expected = IllegalStateException.class) @Test public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); Loading @@ -159,8 +162,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.mActivityClientController.requestPictureInPictureMode(activity); // Check that no transactions with enter pip requests are made. verify(lifecycleManager, times(0)).scheduleTransaction(any()); verify(lifecycleManager, atLeast(0)) .scheduleTransaction(mClientTransactionCaptor.capture()); final ClientTransaction transaction = mClientTransactionCaptor.getValue(); // Check that none are enter pip request items. transaction.getCallbacks().forEach(clientTransactionItem -> { assertFalse(clientTransactionItem instanceof EnterPipRequestedItem); }); } @Test Loading
services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +11 −2 Original line number Diff line number Diff line Loading @@ -522,7 +522,10 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode()); // Get the root task from the PIP activity record again, since the PIP root task may have // changed when the activity entered PIP mode. final Task pipRootTask = record.getRootTask(); assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode()); } @Test Loading Loading @@ -1015,6 +1018,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; record.setPictureInPictureParams(new PictureInPictureParams.Builder() .setAutoEnterEnabled(true).build()); spyOn(record); doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); Loading Loading @@ -1499,7 +1504,11 @@ public class WindowOrganizerTests extends WindowTestsBase { verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); clearInvocations(mWm.mAtmService.mRootWindowContainer); t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN); // The token for the PIP root task may have changed when the task entered PIP mode, so do // not reuse the one from above. final WindowContainerToken newToken = record.getRootTask().mRemoteToken.toWindowContainerToken(); t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); } Loading