Start an activity cross profile in same task.

Adding new overloaded methods of startMainActivity and startActivity that allow the caller to
specify that the new activity should be added to the current task rather
than creating a new one.
Test: atest android.devicepolicy.cts.CrossProfileAppsTest FrameworksServicesTests:com.android.server.pm.CrossProfileAppsServiceImplTest
Bug: 215425075

Change-Id: I21c258c5962daefb32f65779035f4ba8f0474da7
(cherry picked from commit 96ca7241ff)
Merged-In:I21c258c5962daefb32f65779035f4ba8f0474da7
This commit is contained in:
Gareth Vaughan
2022-01-19 19:02:24 -05:00
committed by Cherrypicker Worker
parent 9870b8a5f4
commit 10b6509d00
7 changed files with 190 additions and 24 deletions

View File

@@ -11293,6 +11293,7 @@ package android.content.pm {
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity);
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
}

View File

@@ -3173,6 +3173,7 @@ package android.content.pm {
}
public class CrossProfileApps {
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES}) public void startActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES}) public void startActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
}

View File

@@ -104,7 +104,44 @@ public class CrossProfileApps {
mContext.getAttributionTag(),
component,
targetUser.getIdentifier(),
true);
true,
null,
null);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Starts the specified main activity of the caller package in the specified profile, launching
* in the specified activity.
*
* @param component The ComponentName of the activity to launch, it must be exported and has
* action {@link android.content.Intent#ACTION_MAIN}, category
* {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
* be thrown.
* @param targetUser The UserHandle of the profile, must be one of the users returned by
* {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
* be thrown.
* @param callingActivity The activity to start the new activity from for the purposes of
* deciding which task the new activity should belong to. If {@code null}, the activity
* will always be started in a new task.
* @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
*/
public void startMainActivity(@NonNull ComponentName component,
@NonNull UserHandle targetUser,
@Nullable Activity callingActivity,
@Nullable Bundle options) {
try {
mService.startActivityAsUser(
mContext.getIApplicationThread(),
mContext.getPackageName(),
mContext.getAttributionTag(),
component,
targetUser.getIdentifier(),
true,
callingActivity != null ? callingActivity.getActivityToken() : null,
options);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -179,6 +216,48 @@ public class CrossProfileApps {
}
}
/**
* Starts the specified activity of the caller package in the specified profile. Unlike
* {@link #startMainActivity}, this can start any activity of the caller package, not just
* the main activity.
* The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
* or {@link android.Manifest.permission#START_CROSS_PROFILE_ACTIVITIES}
* permission and both the caller and target user profiles must be in the same profile group.
*
* @param component The ComponentName of the activity to launch. It must be exported.
* @param targetUser The UserHandle of the profile, must be one of the users returned by
* {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
* be thrown.
* @param callingActivity The activity to start the new activity from for the purposes of
* deciding which task the new activity should belong to. If {@code null}, the activity
* will always be started in a new task.
* @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.INTERACT_ACROSS_PROFILES,
android.Manifest.permission.START_CROSS_PROFILE_ACTIVITIES})
public void startActivity(
@NonNull ComponentName component,
@NonNull UserHandle targetUser,
@Nullable Activity callingActivity,
@Nullable Bundle options) {
try {
mService.startActivityAsUser(
mContext.getIApplicationThread(),
mContext.getPackageName(),
mContext.getAttributionTag(),
component,
targetUser.getIdentifier(),
false,
callingActivity != null ? callingActivity.getActivityToken() : null,
options);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/**
* Starts the specified activity of the caller package in the specified profile. Unlike
* {@link #startMainActivity}, this can start any activity of the caller package, not just
@@ -201,7 +280,7 @@ public class CrossProfileApps {
try {
mService.startActivityAsUser(mContext.getIApplicationThread(),
mContext.getPackageName(), mContext.getAttributionTag(), component,
targetUser.getIdentifier(), false);
targetUser.getIdentifier(), false, null, null);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}

View File

@@ -29,7 +29,7 @@ import android.os.UserHandle;
interface ICrossProfileApps {
void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
in String callingFeatureId, in ComponentName component, int userId,
boolean launchMainActivity);
boolean launchMainActivity, in IBinder task, in Bundle options);
void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
in String callingFeatureId, in Intent intent, int userId, in IBinder callingActivity,
in Bundle options);

View File

@@ -112,7 +112,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
String callingFeatureId,
ComponentName component,
@UserIdInt int userId,
boolean launchMainActivity) throws RemoteException {
boolean launchMainActivity,
IBinder targetTask,
Bundle options) throws RemoteException {
Objects.requireNonNull(callingPackage);
Objects.requireNonNull(component);
@@ -145,8 +147,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
if (launchMainActivity) {
launchIntent.setAction(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
if (targetTask == null) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
} else {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
// Only package name is set here, as opposed to component name, because intent action
// and category are ignored if component name is present while we are resolving intent.
launchIntent.setPackage(component.getPackageName());
@@ -170,15 +176,20 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
}
verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, userId);
// Always show the cross profile animation
if (options == null) {
options = ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
} else {
options.putAll(ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle());
}
launchIntent.setPackage(null);
launchIntent.setComponent(component);
mInjector.getActivityTaskManagerInternal().startActivityAsUser(
caller, callingPackage, callingFeatureId, launchIntent,
/* resultTo= */ null,
Intent.FLAG_ACTIVITY_NEW_TASK,
launchMainActivity
? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
: null,
targetTask,
/* startFlags= */ 0,
options,
userId);
}
@@ -225,6 +236,13 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
verifyActivityCanHandleIntent(launchIntent, callingUid, userId);
// Always show the cross profile animation
if (options == null) {
options = ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
} else {
options.putAll(ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle());
}
mInjector.getActivityTaskManagerInternal()
.startActivityAsUser(
caller,

View File

@@ -64,6 +64,7 @@ android_test {
"testng",
"junit",
"platform-compat-test-rules",
"ActivityContext",
],
aidl: {

View File

@@ -20,6 +20,7 @@ import static org.testng.Assert.assertThrows;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.app.admin.DevicePolicyManagerInternal;
@@ -46,6 +47,7 @@ import android.permission.PermissionManager;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
import com.android.activitycontext.ActivityContext;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
import com.android.server.LocalServices;
@@ -240,7 +242,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PRIMARY_USER).getIdentifier(),
true));
true,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -265,7 +269,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PRIMARY_USER).getIdentifier(),
false));
false,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -292,7 +298,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
true));
true,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -319,7 +327,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
false));
false,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -344,7 +354,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
true));
true,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -369,7 +381,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
false));
false,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -396,7 +410,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
true));
true,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -440,7 +456,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
false));
false,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -465,7 +483,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
new ComponentName(PACKAGE_TWO, "test"),
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
true));
true,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -490,7 +510,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
new ComponentName(PACKAGE_TWO, "test"),
UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
false));
false,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -515,7 +537,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(SECONDARY_USER).getIdentifier(),
true));
true,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -540,7 +564,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(SECONDARY_USER).getIdentifier(),
false));
false,
/* targetTask */ null,
/* options */ null));
verify(mActivityTaskManagerInternal, never())
.startActivityAsUser(
@@ -564,7 +590,9 @@ public class CrossProfileAppsServiceImplTest {
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PRIMARY_USER).getIdentifier(),
true);
true,
/* targetTask */ null,
/* options */ null);
verify(mActivityTaskManagerInternal)
.startActivityAsUser(
@@ -578,6 +606,44 @@ public class CrossProfileAppsServiceImplTest {
eq(PRIMARY_USER));
}
@Test
public void startActivityAsUser_sameTask_fromProfile_success() throws Exception {
mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
Bundle options = ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
IBinder result = ActivityContext.getWithContext(activity -> {
try {
IBinder targetTask = activity.getActivityToken();
mCrossProfileAppsServiceImpl.startActivityAsUser(
mIApplicationThread,
PACKAGE_ONE,
FEATURE_ID,
ACTIVITY_COMPONENT,
UserHandle.of(PRIMARY_USER).getIdentifier(),
true,
targetTask,
options);
return targetTask;
} catch (Exception re) {
return null;
}
});
if (result == null) {
throw new Exception();
}
verify(mActivityTaskManagerInternal)
.startActivityAsUser(
nullable(IApplicationThread.class),
eq(PACKAGE_ONE),
eq(FEATURE_ID),
any(Intent.class),
eq(result),
anyInt(),
eq(options),
eq(PRIMARY_USER));
}
private void mockAppsInstalled(String packageName, int user, boolean installed) {
when(mPackageManagerInternal.getPackageInfo(
eq(packageName),