Merge "Start an activity cross profile in same task." into tm-dev

This commit is contained in:
Kholoud Mohamed
2022-03-15 15:20:54 +00:00
committed by Android (Google) Code Review
7 changed files with 190 additions and 24 deletions

View File

@@ -11300,6 +11300,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

@@ -3192,6 +3192,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),