Loading services/core/java/com/android/server/wm/ActivityStartController.java +9 −1 Original line number Diff line number Diff line Loading @@ -384,6 +384,14 @@ public class ActivityStartController { callingUid, realCallingUid, UserHandle.USER_NULL); final SparseArray<String> startingUidPkgs = new SparseArray<>(); final long origId = Binder.clearCallingIdentity(); SafeActivityOptions bottomOptions = null; if (options != null) { // To ensure the first N-1 activities (N == total # of activities) are also launched // into the correct display, use a copy of the passed-in options (keeping only // display-related info) for these activities. bottomOptions = options.selectiveCloneDisplayOptions(); } try { intents = ArrayUtils.filterNotNull(intents, Intent[]::new); final ActivityStarter[] starters = new ActivityStarter[intents.length]; Loading Loading @@ -432,7 +440,7 @@ public class ActivityStartController { final boolean top = i == intents.length - 1; final SafeActivityOptions checkedOptions = top ? options : null; : bottomOptions; starters[i] = obtainStarter(intent, reason) .setIntentGrants(intentGrants) .setCaller(caller) Loading services/core/java/com/android/server/wm/SafeActivityOptions.java +28 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,34 @@ public class SafeActivityOptions { mOriginalOptions = options; } /** * To ensure that two activities, one using this object, and the other using the * SafeActivityOptions returned from this function, are launched into the same display through * ActivityStartController#startActivities, all display-related information, i.e. * displayAreaToken, launchDisplayId and callerDisplayId, are cloned. */ @Nullable SafeActivityOptions selectiveCloneDisplayOptions() { final ActivityOptions options = cloneLaunchingDisplayOptions(mOriginalOptions); final ActivityOptions callerOptions = cloneLaunchingDisplayOptions(mCallerOptions); if (options == null && callerOptions == null) { return null; } final SafeActivityOptions safeOptions = new SafeActivityOptions(options, mOriginalCallingPid, mOriginalCallingUid); safeOptions.mCallerOptions = callerOptions; safeOptions.mRealCallingPid = mRealCallingPid; safeOptions.mRealCallingUid = mRealCallingUid; return safeOptions; } private ActivityOptions cloneLaunchingDisplayOptions(ActivityOptions options) { return options == null ? null : ActivityOptions.makeBasic() .setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea()) .setLaunchDisplayId(options.getLaunchDisplayId()) .setCallerDisplayId((options.getCallerDisplayId())); } /** * Overrides options with options from a caller and records {@link Binder#getCallingPid}/ * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this Loading services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,12 @@ package com.android.server.wm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; import android.app.ActivityOptions; import android.platform.test.annotations.Presubmit; import android.window.WindowContainerToken; import androidx.test.filters.MediumTest; Loading @@ -43,4 +46,21 @@ public class SafeActivityOptionsTest { final ActivityOptions result = options.mergeActivityOptions(opts1, opts2); assertEquals(6, result.getLaunchDisplayId()); } @Test public void test_selectiveCloneDisplayOptions() { final WindowContainerToken token = mock(WindowContainerToken.class); final int launchDisplayId = 5; final int callerDisplayId = 6; final SafeActivityOptions clone = new SafeActivityOptions(ActivityOptions.makeBasic() .setLaunchTaskDisplayArea(token) .setLaunchDisplayId(launchDisplayId) .setCallerDisplayId(callerDisplayId)) .selectiveCloneDisplayOptions(); assertSame(clone.getOriginalOptions().getLaunchTaskDisplayArea(), token); assertEquals(clone.getOriginalOptions().getLaunchDisplayId(), launchDisplayId); assertEquals(clone.getOriginalOptions().getCallerDisplayId(), callerDisplayId); } } Loading
services/core/java/com/android/server/wm/ActivityStartController.java +9 −1 Original line number Diff line number Diff line Loading @@ -384,6 +384,14 @@ public class ActivityStartController { callingUid, realCallingUid, UserHandle.USER_NULL); final SparseArray<String> startingUidPkgs = new SparseArray<>(); final long origId = Binder.clearCallingIdentity(); SafeActivityOptions bottomOptions = null; if (options != null) { // To ensure the first N-1 activities (N == total # of activities) are also launched // into the correct display, use a copy of the passed-in options (keeping only // display-related info) for these activities. bottomOptions = options.selectiveCloneDisplayOptions(); } try { intents = ArrayUtils.filterNotNull(intents, Intent[]::new); final ActivityStarter[] starters = new ActivityStarter[intents.length]; Loading Loading @@ -432,7 +440,7 @@ public class ActivityStartController { final boolean top = i == intents.length - 1; final SafeActivityOptions checkedOptions = top ? options : null; : bottomOptions; starters[i] = obtainStarter(intent, reason) .setIntentGrants(intentGrants) .setCaller(caller) Loading
services/core/java/com/android/server/wm/SafeActivityOptions.java +28 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,34 @@ public class SafeActivityOptions { mOriginalOptions = options; } /** * To ensure that two activities, one using this object, and the other using the * SafeActivityOptions returned from this function, are launched into the same display through * ActivityStartController#startActivities, all display-related information, i.e. * displayAreaToken, launchDisplayId and callerDisplayId, are cloned. */ @Nullable SafeActivityOptions selectiveCloneDisplayOptions() { final ActivityOptions options = cloneLaunchingDisplayOptions(mOriginalOptions); final ActivityOptions callerOptions = cloneLaunchingDisplayOptions(mCallerOptions); if (options == null && callerOptions == null) { return null; } final SafeActivityOptions safeOptions = new SafeActivityOptions(options, mOriginalCallingPid, mOriginalCallingUid); safeOptions.mCallerOptions = callerOptions; safeOptions.mRealCallingPid = mRealCallingPid; safeOptions.mRealCallingUid = mRealCallingUid; return safeOptions; } private ActivityOptions cloneLaunchingDisplayOptions(ActivityOptions options) { return options == null ? null : ActivityOptions.makeBasic() .setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea()) .setLaunchDisplayId(options.getLaunchDisplayId()) .setCallerDisplayId((options.getCallerDisplayId())); } /** * Overrides options with options from a caller and records {@link Binder#getCallingPid}/ * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this Loading
services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,12 @@ package com.android.server.wm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; import android.app.ActivityOptions; import android.platform.test.annotations.Presubmit; import android.window.WindowContainerToken; import androidx.test.filters.MediumTest; Loading @@ -43,4 +46,21 @@ public class SafeActivityOptionsTest { final ActivityOptions result = options.mergeActivityOptions(opts1, opts2); assertEquals(6, result.getLaunchDisplayId()); } @Test public void test_selectiveCloneDisplayOptions() { final WindowContainerToken token = mock(WindowContainerToken.class); final int launchDisplayId = 5; final int callerDisplayId = 6; final SafeActivityOptions clone = new SafeActivityOptions(ActivityOptions.makeBasic() .setLaunchTaskDisplayArea(token) .setLaunchDisplayId(launchDisplayId) .setCallerDisplayId(callerDisplayId)) .selectiveCloneDisplayOptions(); assertSame(clone.getOriginalOptions().getLaunchTaskDisplayArea(), token); assertEquals(clone.getOriginalOptions().getLaunchDisplayId(), launchDisplayId); assertEquals(clone.getOriginalOptions().getCallerDisplayId(), callerDisplayId); } }