Merge "Start an activity cross profile in same task." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
784e20708d
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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