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
(cherry picked from commit 96ca7241ff)
Merged-In:I21c258c5962daefb32f65779035f4ba8f0474da7
Change-Id: I81a02cb606b95fe428a553bbce986e7bac126ac8
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -64,6 +64,7 @@ android_test {
|
||||
"testng",
|
||||
"junit",
|
||||
"platform-compat-test-rules",
|
||||
"ActivityContext",
|
||||
],
|
||||
|
||||
aidl: {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user