diff options
| author | 2023-07-27 17:38:34 +0000 | |
|---|---|---|
| committer | 2023-09-28 04:39:14 +0000 | |
| commit | 2679009499636470926dd62b306bc96bf0ef017c (patch) | |
| tree | 2360f0455fa676230db375bad1e86c979fc3426f | |
| parent | e09e234181b33c63dc237036debb427ad80c49a8 (diff) | |
Expose SurfaceControl of TaskFragment to system organzier
Bug: 284050041
Test: atest TaskFragmentOrganizerControllerTest TaskFragmentTest WindowFlagsTest
Change-Id: Ibcc8461d22a8c2bfbe88df388e2a5d432bf33150
9 files changed, 178 insertions, 12 deletions
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl index d25c8a834c7b..7b7e34172fed 100644 --- a/core/java/android/window/ITaskFragmentOrganizerController.aidl +++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl @@ -25,9 +25,12 @@ import android.window.WindowContainerTransaction; interface ITaskFragmentOrganizerController { /** - * Registers a TaskFragmentOrganizer to manage TaskFragments. + * Registers a TaskFragmentOrganizer to manage TaskFragments. Registering a system + * organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer will have additional + * system capabilities. */ - void registerOrganizer(in ITaskFragmentOrganizer organizer); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true)") + void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer); /** * Unregisters a previously registered TaskFragmentOrganizer. diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java index f785a3d1514e..a6c9cecb508f 100644 --- a/core/java/android/window/TaskFragmentOrganizer.java +++ b/core/java/android/window/TaskFragmentOrganizer.java @@ -22,9 +22,11 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import android.annotation.CallSuper; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.os.Bundle; import android.os.IBinder; @@ -32,6 +34,8 @@ import android.os.RemoteException; import android.view.RemoteAnimationDefinition; import android.view.WindowManager; +import com.android.window.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; @@ -140,12 +144,34 @@ public class TaskFragmentOrganizer extends WindowOrganizer { } /** - * Registers a TaskFragmentOrganizer to manage TaskFragments. + * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. */ @CallSuper public void registerOrganizer() { + // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed. + try { + getController().registerOrganizer(mInterface, false /* isSystemOrganizer */); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. + * + * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer + * will have additional system capabilities, including: (1) it will receive SurfaceControl for + * the organized TaskFragment, and (2) it needs to update the + * {@link android.view.SurfaceControl} following the window change accordingly. + * + * @hide + */ + @CallSuper + @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true) + @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG) + public void registerOrganizer(boolean isSystemOrganizer) { try { - getController().registerOrganizer(mInterface); + getController().registerOrganizer(mInterface, isSystemOrganizer); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java index 3c5d60dc8ae2..4dada108c4c6 100644 --- a/core/java/android/window/TaskFragmentTransaction.java +++ b/core/java/android/window/TaskFragmentTransaction.java @@ -30,6 +30,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.view.SurfaceControl; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -192,6 +193,9 @@ public final class TaskFragmentTransaction implements Parcelable { @Nullable private TaskFragmentParentInfo mTaskFragmentParentInfo; + @Nullable + private SurfaceControl mSurfaceControl; + public Change(@ChangeType int type) { mType = type; } @@ -206,6 +210,7 @@ public final class TaskFragmentTransaction implements Parcelable { mActivityIntent = in.readTypedObject(Intent.CREATOR); mActivityToken = in.readStrongBinder(); mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR); + mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR); } @Override @@ -219,6 +224,7 @@ public final class TaskFragmentTransaction implements Parcelable { dest.writeTypedObject(mActivityIntent, flags); dest.writeStrongBinder(mActivityToken); dest.writeTypedObject(mTaskFragmentParentInfo, flags); + dest.writeTypedObject(mSurfaceControl, flags); } /** The change is related to the TaskFragment created with this unique token. */ @@ -306,6 +312,13 @@ public final class TaskFragmentTransaction implements Parcelable { return this; } + /** @hide */ + @NonNull + public Change setTaskFragmentSurfaceControl(@Nullable SurfaceControl sc) { + mSurfaceControl = sc; + return this; + } + @ChangeType public int getType() { return mType; @@ -359,6 +372,21 @@ public final class TaskFragmentTransaction implements Parcelable { return mTaskFragmentParentInfo; } + /** + * Gets the {@link SurfaceControl} of the TaskFragment. This field is {@code null} for + * a regular {@link TaskFragmentOrganizer} and is only available for a system + * {@link TaskFragmentOrganizer} in the + * {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_APPEARED} event. See + * {@link ITaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer, + * boolean)} + * + * @hide + */ + @Nullable + public SurfaceControl getTaskFragmentSurfaceControl() { + return mSurfaceControl; + } + @Override public String toString() { return "Change{ type=" + mType + " }"; diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java index a8b40325a713..a5bbeb58bc08 100644 --- a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java +++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java @@ -17,6 +17,7 @@ package android.window.flags; import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag; +import static com.android.window.flags.Flags.taskFragmentSystemOrganizerFlag; import android.platform.test.annotations.Presubmit; @@ -42,4 +43,10 @@ public class WindowFlagsTest { // No crash when accessing the flag. syncWindowConfigUpdateFlag(); } + + @Test + public void testTaskFragmentSystemOrganizerFlag() { + // No crash when accessing the flag. + taskFragmentSystemOrganizerFlag(); + } } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 5d95bc77edcb..50bc825d8bea 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -255,6 +255,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { boolean mClearedForReorderActivityToFront; /** + * Whether the TaskFragment surface is managed by a system {@link TaskFragmentOrganizer}. + */ + boolean mIsSurfaceManagedBySystemOrganizer = false; + + /** * When we are in the process of pausing an activity, before starting the * next one, this variable holds the activity that is currently being paused. * @@ -449,13 +454,21 @@ class TaskFragment extends WindowContainer<WindowContainer> { void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid, @NonNull String processName) { + setTaskFragmentOrganizer(organizer, uid, processName, + false /* isSurfaceManagedBySystemOrganizer */); + } + + void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid, + @NonNull String processName, boolean isSurfaceManagedBySystemOrganizer) { mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder()); mTaskFragmentOrganizerUid = uid; mTaskFragmentOrganizerProcessName = processName; + mIsSurfaceManagedBySystemOrganizer = isSurfaceManagedBySystemOrganizer; } void onTaskFragmentOrganizerRemoved() { mTaskFragmentOrganizer = null; + mIsSurfaceManagedBySystemOrganizer = false; } /** Whether this TaskFragment is organized by the given {@code organizer}. */ @@ -2396,6 +2409,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) { return; } + if (mIsSurfaceManagedBySystemOrganizer) { + return; + } if (mTransitionController.isShellTransitionsEnabled() && !mTransitionController.isCollecting(this)) { // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index ea722b61be6f..04164c20a372 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -26,6 +26,7 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_I import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; +import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission; import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED; import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; @@ -50,12 +51,15 @@ import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizerController; import android.window.TaskFragmentInfo; import android.window.TaskFragmentOperation; +import android.window.TaskFragmentOrganizerToken; import android.window.TaskFragmentParentInfo; import android.window.TaskFragmentTransaction; import android.window.WindowContainerTransaction; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; +import com.android.window.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -133,6 +137,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr new WeakHashMap<>(); /** + * Whether this {@link android.window.TaskFragmentOrganizer} is a system organizer. If true, + * the {@link android.view.SurfaceControl} of the {@link TaskFragment} is provided to the + * client in the {@link TYPE_TASK_FRAGMENT_APPEARED} event. + */ + private final boolean mIsSystemOrganizer; + + /** * {@link RemoteAnimationDefinition} for embedded activities transition animation that is * organized by this organizer. */ @@ -147,10 +158,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr */ private final ArrayMap<IBinder, Integer> mDeferredTransitions = new ArrayMap<>(); - TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) { + TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid, + boolean isSystemOrganizer) { mOrganizer = organizer; mOrganizerPid = pid; mOrganizerUid = uid; + mIsSystemOrganizer = isSystemOrganizer; try { mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/); } catch (RemoteException e) { @@ -235,11 +248,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr tf.mTaskFragmentAppearedSent = true; mLastSentTaskFragmentInfos.put(tf, info); mTaskFragmentTaskIds.put(tf, taskId); - return new TaskFragmentTransaction.Change( + final TaskFragmentTransaction.Change change = new TaskFragmentTransaction.Change( TYPE_TASK_FRAGMENT_APPEARED) .setTaskFragmentToken(tf.getFragmentToken()) .setTaskFragmentInfo(info) .setTaskId(taskId); + if (mIsSystemOrganizer) { + change.setTaskFragmentSurfaceControl(tf.getSurfaceControl()); + } + return change; } @NonNull @@ -435,8 +452,25 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr : null; } + @VisibleForTesting + void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) { + registerOrganizerInternal(organizer, false /* isSystemOrganizer */); + } + @Override - public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) { + public void registerOrganizer( + @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) { + registerOrganizerInternal( + organizer, + Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer); + } + + @VisibleForTesting + void registerOrganizerInternal( + @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) { + if (isSystemOrganizer) { + enforceTaskPermission("registerSystemOrganizer()"); + } final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); synchronized (mGlobalLock) { @@ -448,7 +482,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr "Replacing existing organizer currently unsupported"); } mTaskFragmentOrganizerState.put(organizer.asBinder(), - new TaskFragmentOrganizerState(organizer, pid, uid)); + new TaskFragmentOrganizerState(organizer, pid, uid, isSystemOrganizer)); mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>()); } } @@ -711,6 +745,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr } } + boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) { + final TaskFragmentOrganizerState state = + mTaskFragmentOrganizerState.get(token.asBinder()); + return state != null && state.mIsSystemOrganizer; + } + @Nullable private PendingTaskFragmentEvent getLastPendingParentInfoChangedEvent( @NonNull ITaskFragmentOrganizer organizer, @NonNull Task task) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 6d7e2970e2b1..376cad734866 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -99,6 +99,7 @@ import android.window.IWindowOrganizerController; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; import android.window.TaskFragmentOperation; +import android.window.TaskFragmentOrganizerToken; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -1993,8 +1994,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub creationParams.getFragmentToken(), true /* createdByOrganizer */); // Set task fragment organizer immediately, since it might have to be notified about further // actions. - taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(), - ownerActivity.getUid(), ownerActivity.info.processName); + TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer(); + taskFragment.setTaskFragmentOrganizer(organizerToken, + ownerActivity.getUid(), ownerActivity.info.processName, + mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken)); final int position; if (creationParams.getPairedPrimaryFragmentToken() != null) { // When there is a paired primary TaskFragment, we want to place the new TaskFragment diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index bfa279d5c3d5..2bf13857e537 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -212,7 +212,30 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mController.dispatchPendingEvents(); assertTaskFragmentParentInfoChangedTransaction(mTask); - assertTaskFragmentAppearedTransaction(); + assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */); + } + + @Test + public void testOnTaskFragmentAppeared_systemOrganizer() { + mController.unregisterOrganizer(mIOrganizer); + mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */); + + // No-op when the TaskFragment is not attached. + mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); + mController.dispatchPendingEvents(); + + verify(mOrganizer, never()).onTransactionReady(any()); + + // Send callback when the TaskFragment is attached. + setupMockParent(mTaskFragment, mTask); + + mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); + mController.dispatchPendingEvents(); + + assertTaskFragmentParentInfoChangedTransaction(mTask); + + // System organizer should receive the SurfaceControl + assertTaskFragmentAppearedTransaction(true /* hasSurfaceControl */); } @Test @@ -1664,7 +1687,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } /** Asserts that there will be a transaction for TaskFragment appeared. */ - private void assertTaskFragmentAppearedTransaction() { + private void assertTaskFragmentAppearedTransaction(boolean hasSurfaceControl) { verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); @@ -1675,6 +1698,11 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertEquals(TYPE_TASK_FRAGMENT_APPEARED, change.getType()); assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); assertEquals(mFragmentToken, change.getTaskFragmentToken()); + if (hasSurfaceControl) { + assertNotNull(change.getTaskFragmentSurfaceControl()); + } else { + assertNull(change.getTaskFragmentSurfaceControl()); + } } /** Asserts that there will be a transaction for TaskFragment info changed. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 6a9bb6c85c70..5205bb0038c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -27,8 +27,12 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -124,6 +128,17 @@ public class TaskFragmentTest extends WindowTestsBase { } @Test + public void testUpdateOrganizedTaskFragmentSurface_noSurfaceUpdateWhenOrganizedBySystem() { + clearInvocations(mTransaction); + mTaskFragment.mIsSurfaceManagedBySystemOrganizer = true; + + mTaskFragment.updateOrganizedTaskFragmentSurface(); + + verify(mTransaction, never()).setPosition(eq(mLeash), anyFloat(), anyFloat()); + verify(mTransaction, never()).setWindowCrop(eq(mLeash), anyInt(), anyInt()); + } + + @Test public void testShouldStartChangeTransition_relativePositionChange() { final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); |